关闭视图控制器后约束崩溃

Posted

技术标签:

【中文标题】关闭视图控制器后约束崩溃【英文标题】:Constraint Crash after Dismissing View Controller 【发布时间】:2018-11-10 02:05:27 【问题描述】:

我对 Xcode 有点陌生,一直在尝试以编程方式做事。我有视图控制器 A、B、C 和 D。我在 C 和 D 上有一个后退按钮。当使用 self.dismiss 从 D 转到 C 时,它工作正常,但是当我从 C 转到 B 时,我遇到了崩溃看起来这是一个约束问题,我不知道为什么。

再次,当从 C 到 B 时发生崩溃。错误表明 DropDownButton 没有共同的祖先,但 ViewController B 上没有 DropDownButton,它存在于我试图关闭的 C 上。

我想了解更多关于视图控制器关闭和自动布局如何工作的信息,有人能指出我正确的方向吗?

"oneonone.DropDownButton:0x7fcfe9d30660'????????+1 ⌄'.bottom"> because they have no common ancestor.  Does the constraint or its anchors reference items in different view hierarchies?  That's illegal. userInfo: (null)
2018-11-09 19:56:22.828322-0600 oneonone[62728:4835265] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutYAxisAnchor

问题更新:

这里是视图控制器 C,包括 var,将其添加到子视图,以及我如何关闭此视图控制器

    lazy var countryCodes: DropDownButton = 
        let button = DropDownButton(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
        let us = flag(country: "US")
        let br = flag(country: "BR")
        let lightGray = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
        button.backgroundColor = lightGray
        button.setTitle(us + "+1 \u2304", for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 20)
        button.setTitleColor(UIColor.darkGray, for: .normal)
        button.uiView.dropDownOptions = [us + "+1", br + "+55", "+33", "+17", "+19"]
        return button
    ()

override func viewDidLoad() 
        super.viewDidLoad()
        self.view.backgroundColor = .white

        [countryCodes].forEach view.addSubview($0) 

        setupLayout()

    

func setupLayout()
        countryCodes.translatesAutoresizingMaskIntoConstraints = false
        countryCodes.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 30).isActive = true
        countryCodes.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -77.5).isActive = true
        countryCodes.widthAnchor.constraint(equalToConstant: 85).isActive = true // guarantees this width for stack
        countryCodes.heightAnchor.constraint(equalToConstant: 40).isActive = true


@objc func buttonPressed()
    self.dismiss(animated: true, completion: nil)

这是视图控制器 B 中的代码(创建或呈现?)视图控制器 C

@objc func phoneAuthButtonPressed()
        let vc = phoneAuthViewController()
        self.present(vc, animated: true, completion: nil)
    

更新 2:添加自定义类

这是我在教程之后用作自定义类的按钮代码,我相信问题出在此处

protocol dropDownProtocol 
    func dropDownPressed(string: String)


class DropDownButton: UIButton, dropDownProtocol 

    var uiView = DropDownView()

    var height = NSLayoutConstraint()

    var isOpen = false

    func dropDownPressed(string: String) 
        self.setTitle(string + " \u2304", for: .normal)
        self.titleLabel?.font = UIFont.systemFont(ofSize: 18)
        self.dismissDropDown()
    

    override init(frame: CGRect) 
        super.init(frame: frame)
        self.backgroundColor = UIColor.gray
        uiView = DropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
        uiView.delegate = self
        uiView.layer.zPosition = 1 // show in front of other labels
        uiView.translatesAutoresizingMaskIntoConstraints = false
    

    override func didMoveToSuperview() 
        self.superview?.addSubview(uiView)
        self.superview?.bringSubviewToFront(uiView)
        uiView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        uiView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        uiView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
        height = uiView.heightAnchor.constraint(equalToConstant: 0)
    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)  // animates drop down list
        NSLayoutConstraint.deactivate([self.height])

        if self.uiView.tableView.contentSize.height > 150 
            self.height.constant = 150
         else 
            self.height.constant = self.uiView.tableView.contentSize.height
        

        if isOpen == false 
            isOpen = true
            NSLayoutConstraint.activate([self.height])
            UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut, animations: 
                self.uiView.layoutIfNeeded()
                self.uiView.center.y += self.uiView.frame.height / 2
            , completion: nil)
         else 
            dismissDropDown()
        
    

    func dismissDropDown()
        isOpen = false
        self.height.constant = 0
        NSLayoutConstraint.activate([self.height])
        UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut, animations: 
            self.uiView.center.y -= self.uiView.frame.height / 2
            self.uiView.layoutIfNeeded()
        , completion: nil)
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    



class DropDownView: UIView, UITableViewDelegate, UITableViewDataSource 

    var dropDownOptions = [String]()

    var tableView = UITableView()

    var delegate : dropDownProtocol!

    let lightGray = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)

    override init(frame: CGRect) 
        super.init(frame: frame)
        tableView.backgroundColor = lightGray
        tableView.delegate = self
        tableView.dataSource = self
        tableView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(tableView) // can not come after constraints
        tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    func numberOfSections(in tableView: UITableView) -> Int 
        return 1
    

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return dropDownOptions.count
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = UITableViewCell()
        cell.textLabel?.text = dropDownOptions[indexPath.row]
        cell.textLabel?.font = UIFont.systemFont(ofSize: 14)
        cell.textLabel?.textColor = UIColor.darkGray
        cell.backgroundColor = lightGray
        return cell
    

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
        self.tableView.deselectRow(at: indexPath, animated: true)
    


【问题讨论】:

你能发布你如何为 DropDownButton 创建约束 通常在添加子视图之前激活或更改约束时会发生此错误。请提供问题的Minimal, Complete, and Verifiable example。你是如何在视图控制器之间转换的?见Passing data between ViewControllers 和Push and Present UIViewController Programmatically。 @Sh_Khan 我已经更新了问题并感谢您花时间查看此问题 【参考方案1】:

用这个替换你的 didMoveToSuperview 函数,它就可以工作了。当视图从父视图中移除并且父视图将为 nil 并导致崩溃时,也会调用此函数。

    override func didMoveToSuperview() 
    if let superview = self.superview 
        self.superview?.addSubview(dropView)
        self.superview?.bringSubviewToFront(dropView)
        dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
        dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
        height = dropView.heightAnchor.constraint(equalToConstant: 0)
    

【讨论】:

【参考方案2】:

如我所见,错误表明countryCodes 按钮之一位于与instructionLabel 不同的视图中。如果您希望它们相互约束,它们应该具有相同的父级。

【讨论】:

嘿,我做了一些测试,我相信这与我在观看教程时构建的自定义类有关,我在 UPDATE 2 下使用自定义类代码更新了我的帖子【参考方案3】:

您好,我认为问题出现在 didMoveToSuperView() 函数中,因为当视图从其父视图中删除时也会调用它。因此,当您尝试将锚点设置为不再存在的东西时,它会崩溃。

试试这样的:

  if let superview = self.superview 
    superview.addSubview(uiView)
    superview.bringSubviewToFront(uiView)
    uiView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
    uiView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
    uiView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
    height = uiView.heightAnchor.constraint(equalToConstant: 0)

【讨论】:

以上是关于关闭视图控制器后约束崩溃的主要内容,如果未能解决你的问题,请参考以下文章

关闭模式视图控制器后,框架不反映自动布局约束

关闭呈现的视图控制器后调用主控制器的函数

导航控制器在关闭视图控制器时崩溃

模态视图控制器关闭时应用程序崩溃

关闭纵向视图控制器时应用程序崩溃

-[NSView setFrame:] 由于无效约束而崩溃