I'm attempting to constrain two ui elements to the same place. At the beginning of my program, I'm constraining a label to the center of a UiView. This is working fine. However later in my program, I'm attempting to remove this label from the UiView and constrain a button to the center of the same UiView. However, when I begin constraining my button, the system errors out.
How do you constrain two ui elements to the same place in Swift?
Here's the relevant code.
override func viewDidLoad() {
super.viewDidLoad()
// Middle UI View
view.addSubview(middleUIView)
NSLayoutConstraint.activate([
middleUIView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
middleUIView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
middleUIView.topAnchor.constraint(equalTo: sosButton.bottomAnchor),
middleUIView.bottomAnchor.constraint(equalTo: textView.topAnchor)
])
// Middle Label
middleUIView.addSubview(middleUILabel)
NSLayoutConstraint.activate([
middleUILabel.centerXAnchor.constraint(equalTo: middleUIView.centerXAnchor),
middleUILabel.centerYAnchor.constraint(equalTo: middleUIView.centerYAnchor)
])
}
func createButton() {
middleUILabel.removeFromSuperview()
middleUIView.removeConstraints(middleUIView.constraints)
// It errors out here
NSLayoutConstraint.activate([
continueButton.widthAnchor.constraint(equalToConstant: 150),
continueButton.heightAnchor.constraint(equalToConstant: 50),
continueButton.centerXAnchor.constraint(equalTo: middleUIView.centerXAnchor),
continueButton.centerYAnchor.constraint(equalTo: middleUIView.centerYAnchor)
])
}
This is the error:
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutXAxisAnchor:0x2807ffd80 "UIButton:0x135425fc0'Continue'.centerX"> and <NSLayoutXAxisAnchor:0x2807d4b00 "UIView:0x135425700.centerX"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
CodePudding user response:
You need to add your button to the view hierarchy before adding contraints to it.
Also, you should keep track of the constraints you created for the label so that you can call NSContraint.deactivate()
on them before you remove the label.
var labelConstraints: [NSLayoutConstraint] = []
override func viewDidLoad() {
super.viewDidLoad()
// Middle UI View
view.addSubview(middleUIView)
NSLayoutConstraint.activate([
middleUIView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
middleUIView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
middleUIView.topAnchor.constraint(equalTo: sosButton.bottomAnchor),
middleUIView.bottomAnchor.constraint(equalTo: textView.topAnchor)
])
// Middle Label
middleUIView.addSubview(middleUILabel)
labelConstraints = [
middleUILabel.centerXAnchor.constraint(equalTo: middleUIView.centerXAnchor),
middleUILabel.centerYAnchor.constraint(equalTo: middleUIView.centerYAnchor)
]
NSLayoutConstraint.activate(labelConstraints)
}
func createButton() {
NSLayoutConstraint.deactivate(labelConstraints)
labelConstraints = []
middleUILabel.removeFromSuperview()
// Add continue Button to the view hierarchy
middleUIView.addSubview(continueButton)
NSLayoutConstraint.activate([
continueButton.widthAnchor.constraint(equalToConstant: 150),
continueButton.heightAnchor.constraint(equalToConstant: 50),
continueButton.centerXAnchor.constraint(equalTo: middleUIView.centerXAnchor),
continueButton.centerYAnchor.constraint(equalTo: middleUIView.centerYAnchor)
])
}
Note: Manually removing constraints from views (like middleUIView.removeConstraints(middleUIView.constraints)
) is not recommended because you might remove constraints you didn't know were there. In general, you should just activate()
and deactivate()
constraints and let the system add them to and remove them from the proper views for you.