解雇View Controller之后的约束崩溃

时间:2018-11-10 02:05:27

标签: ios swift crash autolayout programmatically-created

我对Xcode有点陌生,一直在尝试以编程方式进行操作。我有View Controller A,B,C和D。在C和D上有一个后退按钮。当使用self.dismiss从D转到C时,它工作正常,但是当我从C转到BI时,看起来像是约束问题的崩溃,我也不知道为什么。

同样,当从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

问题更新:

这是View Controller 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 \u{2304}", 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 + " \u{2304}", 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)
    }

}

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

嗨,我认为问题出在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)
}

答案 2 :(得分:0)

以此替换您的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)
    }
}