Auto layout constraint breaks when resizing view dynamically (dragging motion) - ios

I have a view that is sandwiched between two other views, and the height of the middle view can be changed through pan gesture.
I'm using a gesture recognizer to get the location of touch and feeding the value to the middle view's height constraint to make it dynamically resizable.
One issue is when the touch location goes beyond the middle view's bounds and overlaps with either the top view or the bottom view, I get the warning:
Unable to simultaneously satisfy constraints ...
Will attempt to recover by breaking constraint
This hasn't really caused any real issues but just in case it might cause a big problem, I'd like to know if
a) it's correct to use the height constraint to dynamically change the height
b) there is any way to avoid getting that warning logged
Thanks!

To answer your question:
a) Yes, this is the correct way to update the height of the view
b) Yes, there is a way to get rid of the warnings
Since your view gets negative values for the height constraints, you could add a check for the height so that you never set a negative value to it. Then, you won't have the error log anymore.
Not sure if you are supporting iOS 7 also, but if you are, the app will most probably crash because of the layout errors.
So, my suggestion would be to add something like this, to where you are updating the constraints:
[self.heightConstraint setConstant:MAX(0, value)];
Good luck with your project!

Related

Why might Autolayout constraints not appear at runtime?

I am trying to create a custom subclass of UITableViewCell that displays an image and a few labels to the right of the image. I have my UIImageView and UILabels arranged in stack views, the outermost of which I pinned to all four sides of the content view.
But when I run the app in the simulator, I do not see the image views. Debugging the view hierarchy, I see they are getting clipped.
And digging in a little further by focusing on the outermost stack view with "Show Constraints" on, I don't see my auto layout constraints at all (instead I get an ambiguous position runtime warning for each of the outermost stack views).
The text label that expands to the right is set up with 0 lines and the table view gets a constant estimatedRowHeight value and its rowHeight set to UITableViewAutomaticDimension, so I think if these constraints were working, the rows in the table would size to fit the content. Perhaps notably, any width or height auto layout constraint I add does appear when I debug the view hierarchy, so I know some constraints are present at runtime, I just cannot for the life of me figure out why the constraints relative to the content view are not.
After wrestling with this for a long time and having a much more experienced developer look over my project, it seems as if there was nothing wrong with the way it was set up. Recreating the table view cell from scratch with exactly the same layout and constraints and using the exact same code fixed this issue. I had messed around with the stack views and constraints quite a bit before this started happening, so it might be possible to get Xcode into a place where it does not properly set up constraints when you run the app in the simulator.
Try to solve all warnings which are printed into your console log when you run your application and viewing this screen. Because you can not ignore warnings always, many warnings you must have to remove by correcting or solving issues in your constraint. And that is the only solution which helps you most of the time.
I'm 5 years late to the party, but I managed to find a solution to this problem in my project. If any of the views that are contained in the table view cell have a bad subclass set on them, the intializer defaults them back to a regular UIView and without any of the layout constraints. Hopefully that saves someone the hours it took me!

Why does Xcode only complain about missing constraints after another one has been set

Help me understand:
When you place any view in the interface builder, Xcode won't complain about missing constraints or ambiguous rules. Only once you set some constraints (but not all to remove every ambiguity), Xcode will notify you of missing constraints and suggest some.
For example: I set a constraint to a view, that centers it horizontally in the superview. Now Xcode complains about a missing rule for the Y position.
Why doesn't it just infer that from the current X-Postition as it does, when you don't have any constraints in place?
That's because before adding constraints to the view, Xcode will see it as if you want the view to be at the exact X,Y position. That means no matter what size the container is, the view will always be at the exact coordinate and have the same size.
However, after adding a few constraints, it means that you want the view to change its size or position according to the container size (which is what autolayout is for), so the constraints you added must provide sufficient information for Xcode to determine its frame.
For example, if you add only Horizontally in Container constraint without specifying its size or Y coordinate, Xcode can't tell where you want the view to be placed. That's why you're getting the warnings.
As for the example you mentioned, if you set the view to be centered horizontally in container, as the width of the container gets larger, Xcode can't tell which one you would prefer:
stay at the same X position and increase the width of the view.
preserve the size and increase X.
Increasing the height of the container will also face a similar problem.
Hope the explanation helps :)
Until you start using constraints, Xcode doesn't know that constraints will be used on that view so it won't show any error. But once a constraint has been added it knows to start applying autoLayout. Xcode now needs to know the width, height, x and y position of that particular view from the constraints. It can either infer them based on the constraints applied on the other views or you can explicitly define them.
Additionally you can use an option that allows Xcode to apply the constraints that it think should be present. But they aren't always what you want. Look in the image below. (The add missing constraints button)
In your example, you applied a constraint specifying where it should be in terms of X-axis but not the Y-Axis. To infer constraints in this case you need to have other views that will have constraints applied on them and this view should have other constraints with respect to these other views that allow it infer its position. In your case, this happens as there is no relation between the X-Axis and Y-Axis. Simply specifying the X coordinate cannot let autoLayout figure out the Y-Coordinate.
Also, for a UIView even if you center it horizontally and vertically, even then it will show an error telling you to define a width and height provided their is no content inside the view

iOS: Getting height of views with programatically added constraints as only indicator

Hello there fellow iOS programmers. While creating an app I've ran into a problem I can't seem to find an answer to. Let's lay it out:
I'm creating a UIViewController with a UIScrollView as it's only child. Inside this view I have a UIView, and inside of this there is a list of UIViews with UILabels inside them. As you all know you need to specify a contentSize for a UIScrollView.
The problem is that the list needs to be dynamic with it's content, and I therefore have no way to know the views heights beforehand. I'm adding all views with constraints where the height is set to ">=0".
When I later try to set the height of the UIScrollView I need to either get the height of the UIView that the list is inside, or get the origin.y and height of the last view in the list. This of course needs to be ready by the time the view is displayed to the user.
I've currently tried view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize), which returned 0; view.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize), which returned 10000; and view.origin.y + view.frame.height, which also returns 0. It seems to me like the constraints haven't taken effect yet.
I've tried setting both constraints and UIScollView.contentSize from viewDidLoad(). I've also tried to set constraints in viewDidLoad and setting contentSize in viewWillAppear; this yielded the same results. Setting constraints in viewWillAppear and setting contentSize in viewDidLoad only caused a crash.
Bottom-line: When should I set up the UIScrollView.contentSize if I want to get view.height or similar methods to return a correct result, while at the same time be ready by the time the user sees the view?
Btw, I'm making this app in Swift, so answers in this language is preferred, but I'm able to translate from Objective-C to Swift; post in whatever suits you best.
Thank you! :)
You say:
As you all know you need to specify a contentSize for a UIScrollView.
No, as TN2154 says, the constraints between the scroll view and its subviews are "interpreted as the content size of the scroll view" (emphasis added). This is a boon, because you no longer have to mess around with contentSize if doing auto-layout. Just set the constraints for the scroll view's subviews and the content size takes care of itself. This leverages the labels' intrinsic size (and make sure that the label's numberOfLines to zero).
The only trick is that it sometimes cannot figure out the correct width of the labels (because the trailing constraint is to the scroll view's content size, it will sometimes make the scroll view a horizontally scrolling one). You can remedy this by either manually setting preferredMaxLayoutWidth or by setting a width constraint between the label and the scroll view's superview.
Personally, while I understand the inclination to add the UIView containers between the scroll view and the labels, I'd suggest losing them unless you need them for some other reason. IMHO, it simply complicates the constraints unnecessarily. It's hairy enough as it is. Obviously, if these containers bear other utility for you, then go ahead and keep them (and they'll work fine), but if you're doing this simply for the sake of the constraints, you might consider eliminating them.

UIDynamicAnimation issue with autolayout

I am new to autolayout.
I have got a view hierarchy working fine with autolayout.
I got a container view displaying a menu. I am animating this view using Dynamics to make a pop effect: the view grows from a tiny size to its target size.
The effect is great. The problem is, since the view needs to be shrink to a tiny tiny size (like 5x5 at the beginning), all the first part of the animation cannot resolve my subviews constraints (like leading AND trailing space cannot be both 10 because the view itself is 10).
I don't care if at this minimum scale the layout is messy or exceeds the view frame but I don't know how to define my constraints to make it work.
Like, is there a way to tell autolayout:
This trailing space should be 10 but if you can't then ignore it
Or something like that. Since I know which constraint should be ignored it would be great if there is a way to tell it to autolayout instead of having the warning and letting the OS "guess" which constraint to drop.
I am sure I could do everything programmatically by skipping all these constraints before animation, animating, and putting them back but since it is a big table view with many rows there are so many table view cell inner contraints to consider that it would really be hard to achieve.
Any insight / help / pointer on this would be great.
This trailing space should be 10 but if you can't then ignore it
The above statement is ambiguous, we need to define when the drawing system needs to add constraint for trailing space to 10 and when to ignore it, specifically.
This is obviously possible through coding it programatically. But,
You can set constraints using inequalities like "greater than or equal to" or "less that or equal to", and that would hopefully solve you problem.

Suppress Autolayout Warnings

I have 3 image views that I have placed in the nib. They need to start at a different y value based on portrait or landscape. Additionally, they will move to the bottom of the screen based on a button click. At that point, I remove the top constraint and add a bottom constraint.
Due to the complexity of this, I decided to handle those particular constraints in the code. However, I still get a warning that the y position for those image views is ambiguous.
Is there any way for me to tell Xcode not to worry, that I have set the constraints in code? The only solution I can think of is to create a constraint in the nib and delete it on view did load but that seems hacky.
You can add constraints that are removed at build time. This tells interface builder that you're going to handle it in code. Working on capturing a screen shot.

Resources