I had this problem in various different settings, both with IB and when defining constraints manually. Here is the current setting:
I have a label and a text field in a container view using the following visual format constraints:
H:|-(4)-[label(labelWidth)]-(4)-[editor]-(4)-|
option = NSLayoutFormatAlignAllFirstBaseline
V:|-(4)-[editor]-(4)-|
For these constraints, the label is positioned outside the container view in the top-left corner of the screen (actually under the navigation bar).
When I add these constraints:
V:|-(>=4)-[label]-(>=4)-|
the label is positioned 4pt from the top (matching the new constraint).
In both cases, once I tap into the text field opening the keyboard and then closing it again (no change is needed for this to happen), the label correctly aligns with the text fields baseline.
[EDIT: Added some screenshots] Initial state:
Keyboard opened:
Correct display after keyboard closed:
In the Xcode debugger, the baseline constraint shows as inactive before and as active after the keyboard was opened and closed.
It seems as if the baseline of the textfield is not (correctly) computed until after the first edit. Initializing the textfield with nil, @"" or some text does not affect the behavior.
The question is, what's causing this behavior and more importantly, what can I do about it?
If the text field is created programmatically, or if the baseline alignment constraint is not added by IB, then the text fields baseline view is not correctly positioned.
A call to [textField setNeedsUpdateConstraints] at the right time (see below) fixes this.
In my case I have a custom container view, which adds/replaces constraints to layout the label an text field. I had to call [textField setNeedsUpdateConstraints] in the layoutSubviews method of the container view. Calling it in updateConstraints where the constraints are modified did not help.
In a similar scenario where the text field already had a baseline alignment (with any view, the label or another one) which would be replaced by a new baseline alignment, the problem does not occur, apparently because IB would somehow take care to layout the participating text field in time.
So my workaround/solution is:
- (void)updateConstraints {
// setup constraints for affected views in self.viewsDictionary
[super updateConstraints];
}
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView* view in self.viewDictionary.objectEnumerator) {
[view setNeedsUpdateConstraints];
}
}