关闭视图控制器后约束崩溃
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)
【讨论】:
以上是关于关闭视图控制器后约束崩溃的主要内容,如果未能解决你的问题,请参考以下文章