I am working in an iOS 11.X app with Swift 4.1, fully programatic. Using Xcode 9.4
I am trying to have a view centered in the middle (horizontally), with a distance of 10 pts to the top of the view, with rotation support.
The app architecture has the regular stuff, as follows (no storyboard):
Given that in landscape mode, the statusBar
is not visible and to simplify the autolayout configuration, I decided to add a top anchor to the navigationBar
as follows (code was simplified for clarity):
let navBar = self.navigationController!.navigationBar
let label = UILabel()
label.backgroundColor = .yellow
label.text = "TEXT"
label.textAlignment = NSTextAlignment.center
self.view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.widthAnchor.constraint(equalToConstant: 50).isActive = true
label.heightAnchor.constraint(equalToConstant: 20).isActive = true
label.centerXAnchor.constraint(equalTo: label.superview!.centerXAnchor).isActive = true
label.topAnchor.constraint(equalTo: navBar.bottomAnchor, constant: 10).isActive = true
The result is as expected. It rotates nicely, and also works fine if the App is launched in landscape or portrait (both in the device and in the simulator).
Now. FastForward 1 commit in GitHub, with no changes in the overall App View Architecture, and the same code, it crashes with the following message
*** NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.
Any idea of what may be going on here?. I downloaded the .zip from the previous GitHub commit to have it isolated and to re-confirm that it works!!. But the current commit fails without no changes to the code related to this UILabel
.
The navBar is part of UIWindow
->UILayoutContainerView
->UINavigationBar
I was able to identify what causes the crash.
The only difference in the last commit is a small delay (before adding the UI elements and the constraints). 0.01 seconds are enough and it works fine as follows:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "APP TITLE"
self.view.backgroundColor = .white
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) { (nil) in
let navBar = self.navigationController!.navigationBar
let label = UILabel()
label.backgroundColor = .yellow
label.text = "TEXT"
label.textAlignment = NSTextAlignment.center
self.view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.widthAnchor.constraint(equalToConstant: 50).isActive = true
label.heightAnchor.constraint(equalToConstant: 20).isActive = true
label.centerXAnchor.constraint(equalTo: label.superview!.centerXAnchor).isActive = true
label.topAnchor.constraint(equalTo: navBar.bottomAnchor, constant: 10).isActive = true
}
}
}
You will find a clean Xcode project with the working code here https://www.dropbox.com/s/vhd9emd8ixf5lbs/AutoLO.zip?dl=0
Comment the Timer.scheduledTimer(withTimeInterval: repeats:) lines and it will crash with the message:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors NSLayoutYAxisAnchor:0x60c000267e80 "UILabel:0x7fe0ede0b960'TEXT'.top" and NSLayoutYAxisAnchor:0x60c000267f00 "UINavigationBar:0x7fe0edd06ce0.bottom" because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.
The explanation of why Xcode complains about a centerY / YAxis anchor when there is none, is a mystery to me.