Сбой ограничения после закрытия контроллера представления

Я немного новичок в Xcode и пытаюсь делать что-то программно. У меня есть View Controller A, B, C и D. У меня есть кнопка «Назад» на C и D. При переходе от D к C с помощью self.dismiss он работает нормально, однако, когда я перехожу от C к B, я получаю сбой, который выглядит как будто это проблема ограничений, и я понятия не имею, почему.

Опять же, сбой происходит при переходе от C к B. Ошибка говорит об отсутствии общего предка для DropDownButton, но на ViewController B нет DropDownButton, он существует на C, который я пытаюсь отклонить.

Я хотел бы узнать больше о том, как работают контроллеры представления и Auto Layout, может ли кто-нибудь указать мне правильное направление, пожалуйста?

"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 \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)
    }

}

person Davidhall    schedule 10.11.2018    source источник
comment
вы можете опубликовать, как вы создаете ограничения для DropDownButton   -  person Sh_Khan    schedule 10.11.2018
comment
Обычно эта ошибка возникает при активации или изменении ограничений до добавления подпредставления. Приведите минимальный, полный и поддающийся проверке пример проблемы. Как вы переходите между контроллерами просмотра? См. Передача данных между ViewControllers и Отправить и представить UIViewController программно.   -  person RLoniello    schedule 10.11.2018
comment
@Sh_Khan Я обновил вопрос и ценю время, которое вы потратили на его изучение.   -  person Davidhall    schedule 10.11.2018


Ответы (3)


Замените свою функцию didMoveToSuperview этой, и она заработает. Эта функция также вызывается, когда представление удаляется из супервизора, и супервизор становится нулевым, что вызывает сбой.

    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)
    }
}
person Alin Lipan    schedule 24.08.2019

Как я вижу, ошибка говорит о том, что одна из кнопок countryCodes расположена в другом представлении, чем instructionLabel. У них должен быть один и тот же родитель, если вы хотите, чтобы они были ограничены друг другом.

person kelin    schedule 11.11.2018
comment
Эй, я провел некоторое тестирование и считаю, что это связано с пользовательским классом, который я создал во время просмотра учебника, я обновил свой пост кодом пользовательского класса в разделе ОБНОВЛЕНИЕ 2. - person Davidhall; 11.11.2018

Привет, я думаю, что проблема возникает в функции 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)
}
person Soufiane.ess    schedule 12.02.2019