Constraints from code behave differently from interface builder - swift

I'm moving part of UI to code from Interface Builder. After finishing, I'm comparing different branches in Debug View Hierarchy mode - the set of constraints look the same, except different constraints are grayed:
Before:
After:
What does this gray color mean? They all active. The issue is that the grayed constraint on the second picture doesn't work

The gray color highlights the active constraints that Auto Layout is not using due to conflicts or because there are other higher priority constraints it is using.
The problem is that your constraints aren't quite the same.
Before:
superview.bottom = self.bottom + 200 # 750
After:
self.bottom = superview.bottom + 200 # 750
Those aren't the same. To fix the After case, you either need to switch the self.bottom and superview.bottom items or change the constant to -200.

Related

Swift: Auto Layout - Columns of Text - removing trailing and leading warnings

I have two columns of text. The left is anchored top, left and bottom and the righthand side conversely. This still generates leading and trailing warnings. How do I connect the two columns' rows to tell Auto Layout to just expand the space in between?
While YOU know exactly what text you're going to put into those labels, Storyboard / Interface Builder (IB) has no idea.
So this looks great to you:
But... what happens if the "Date" text changes to "When do you want to get started?":
Because we haven't given a constraint between the two labels, they overlap.
So, let's do the same thing on both "rows" but, add a Trailing-to-Leading constraint of 8 between the labels:
We've prevented the overlap but now we see a new problem (that IB will warn you about)... Which label should get compressed? IB (and auto-layout at run-time) will make its own decision, which may not be what you want, and which may be inconsistent between similar layouts.
To fix that, we give a higher Content Compression Resistance Priority to the label we do not want compressed:
And here is the result - top "row" has the left-hand label at the default of 750, and the right-hand label at 751, and the bottom "row" has the left-hand label at 751, and the right-hand label at the default of 750:
It looks the same as "C" but we no longer have errors/warnings from IB.
So, even if you know the text in your two columns will never be enough to overlap, IB is going to encourage you to provide enough constraints (and priority settings) to make sure you get exactly what you want.

Xcode: horizontal constraints are not respected?

The result today is:
As you can see there is an error with the view which contains both Labels doesn't respect the constraint between icon and its.
Global constraints:
Did you see some configuration errors guys on constraints?
Aren't you getting constraint issue in your "Xib" or "Storyboard" because when I added the same constraints that you have used, I am getting constraint conflict issue.
After correcting it, It is like this now:
First the Texts layout.centerX = centreX is not required if you are adding leading and trailing.
Second Icon View.leading = leading + 16 if you want the icon view to stick to the left side.

Can I use auto layout in a storyboard to position my views, then disable the constraints?

I'd like to perform an interactive gesture-based animation with my views that requires moving a view which is centered in the screen to the top-left corner of the screen. I can't seem to interpolate between the position of two different x/y constraints (only changing the constant), so as an alternative, I thought perhaps I could lay out the views in my storyboard, then animate them by changing the frame directly. Is there a good way to do this, or is it a bad idea?
You don't need to remove them! Just deactivate them is enough.
If you have seen the docs, you should know that the constraints property of a UIView returns the constraints that it has as a [NSLayoutConstraint]. You just need to loop through this array and deactivate all the constraints!
for constraint in someView.constraints {
constraint.active = false
}
Or using forEach:
someView.constraints.forEach { $0.active = false }
The advantage of deactivating constraints instead of removing them is that you can activate them again easily when you need it.
If I were you I would choose to animate constraint instead. Even if you cannot change the constraint constant for some reason, you can still remove the previous contraint and add a new one.
To do so you'll need to :
Keep a reference to the constraint you want to change
remove it from the view
set the new constraint and add it to the view
keep the reference of that new constraint
Animate with :
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}

Autolayout Constraint Error with a single constraint

I have a scence with a bunch of labels and buttons. When you tap on a button a view slides up from the bottom with controls in it, a kind of keyboard so to say. It "looks" like this:
-----------------------------
| |
| [Button 1] [ Slider 1 ] |
| |
| [Button 2] [ Slider 2 ] |
| |
-----------------------------
This "keyboard" is created at the very beginning when the view loads and the animation is done switching its heigth from 0 to its instrinsic content size. This escene only supports landscape mode and it took me quite a while to keep the "keyboard" on the view when the device rotates 180 degrees.
The problem I see pops up with either of the two following situations:
The device rotates 180 degrees.
The "keyboard" is called.
This problem as follows:
Unable to simultaneously satisfy constraints...
.
.
.
.
(
"<NSLayoutConstraint:0x718c6c0 UIButton:0x717e0d0.centerY == UISlider:0x717d9d0.centerY>",
"<NSLayoutConstraint:0x7190a00 UIButton:0x717e0d0.centerY == UISlider:0x717d9d0.centerY>"
)
The error log gives me this error twice, once for each set of button-slider.
What I think is weird is that the conflicting constraints are exactly the same. I thought I did some copy-paste mistake and added the same constraint twice, but it's not the case.
I'm guessing it has something to do with updateViewConstraints being called upon rotation and also whe I perform the animation, but I cannot see why only these constraints are affected since there are some more in this "keyboard" view.
All and all, this Autolayout is beign quite more difficult than Apples whants to claim. In my opinion, of course.
Any ideas?
EDIT: the constrains are set all in code using mainly the visual language format. The constrains of the controls inside the "keyboard" are added into the "keyboard" view which is the normal thing to do, I believe.
Just to try it out, I changed the problematic constraints and, instead to adding them to the "keyboard" subview, I added them to the self.view ("keyboard" superview). All the sudden, no more errors are shown.
Despite of that, I'd really like some discussion on the matter because I still don't know what's wrong and I just had a lucky shot. I'd really like to undestand it.
The fact that the conflicting constraints are exactly the same is in fact the error. With Auto Layout, you can't have a constraint twice. That will generate the error you see above.
Definitely, you have added the constraint twice. You can see this from the memory addresses. You have two different NSLayoutConstraint instances, 0x718c6c0 and 0x7190a00. However, the instances each refers to are both the same. That being that the vertical center centerY of your UIButton instance 0x717e0d0 should be in the middle of the UISlider 0x717d9d0.
Possibly your updateConstraints method has been called and you haven't checked to see if a constraint already exists before adding it again.
I don't know if this may help you but I just used this tutorial for setting up constraints to buttons and labels in my app

Hide a UITableView by setting row height to 0?

I just inherited code which hides/shows rows a UITableView by using the delegate heightForRowAtIndexPath method and returning height 0 for "hidden rows".
The code works, but it has me concerned there might be fraught with unforeseen complications. Can someone either ease my concerns or give me good reasons why this could cause problems (I couldn't find any issues with initial testing).
The table is fairly small <10 rows total and would require custom row heights even without this hidden row solution.
I do the same thing in the code I just worked on. I am not happy with different behaviour for different table view settings.
The alternative in my case is more complex (a model that adapts to what is visible or not).
For now, I put a //HACK comment on it and document a few peculiarities.
This is what I have found (iOS 5.0 tested):
Set tableView.rowHeight = 1; Zero will give a cell with zero height (as returned by tableView:tableView heightForRowAtIndexPath:) some default height.
You must have a cell separator. If none is selected, then a default height is assigned to zero height rows. The height of 1 is included with the separator.
If your code works in a different way, it would be interesting to know how it is set up.
It would be cleaner to add and remove the rows between two beginUpdates and endUpdates calls, but I don't see why this 0-height method should not work.
If there are no UI-artifacts, that is (e.g. the Delete button showing up overflowing to the next cell).
I use this method of setting hidden cell heights to 0. It works well and also means I can animate the inclusion of new cells by expanding the cell height (such as adding a DatePicker Cell like the calendar app does).
A few things I have had to watch out for in iOS 7.1 are that very squashed text does still appear even when a cell height is = 0 so I've needed to remove cell text in that case. Also, I have change the size of the cell's separatorInset as that was appearing as well.