Auto layout and animation - ios

in my program I have 14 different buttons with a letter each. And every single button is connected to each other with auto layout. So there is crazy lots of constraints. See first image for explanation:
But I want to be able to move every single button back and forth through UIAnimation without messing up the whole auto layout setup. See second image for explanation, what I want to do:
Now the code I currently use to get these animation:
self.letterA.translatesAutoresizingMaskIntoConstraints = YES;
[UIView animateWithDuration:0.2 animations:^{
[letterA setFrame:CGRectMake(x, y, width, height)];
}];
Now the program works perfectly! Absolutely no problems at all! But the only "problem" is this code generates this message in the console:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
And this warning/error/problem goes on and on and on several times, every single time a UIButton is moved. Now what should I do?
Can I ignore the Unable to simultaneously satisfy constraints? Or will that cause trouble down the road?
If not, how to fix it? And since there are crazy amounts of constraints involved, how can it be fixed without editing every single constrain?
I would very much like to keep animating using setFrame:

To your points:
"Can I ignore the Unable to simultaneously satisfy constraints warning"
No. It's a really bad idea to ignore this, even if the UI appears more or less as you want it to even with the warning. It means that the system is deciding how to render your layout, because the instructions you have supplied are contradictory. It has analyzed the constraints you have provided and found a way to create a consistent layout by breaking one or several of them. There is no guarantee that it will decide to break the same constraints on different screen sizes or OS versions. Ignoring this warning massively increases the chance of UI bugs.
I'd rethink the whole way this UI is designed. IB/Storyboard Autolayout works well for UIs up to a certain level of complexity. This UI looks to be slightly beyond that level of complexity - if the tiles didn't need to move, it would be fine. As they do, programmatic autolayout may make things simpler.
The approach I'd take would be as follows.
a. Create a tile object, which exposes NSLayoutConstraint properties for top, left, width and height constraints. (These constraints would be added on the superview, but also stored on the tile itself).
b. Set up the views using a factory object method taking the starting position offset, width and height of the new tile as parameters. Use these values to set up the constraints on each tile independently. Don't constrain the tiles to each other at all - all constraints should be either internal (width,height) or relative to the superview (x,y). This means that when you animate changes only one tile is affected. You actually have more constraints, but they are in code and therefore easier to manage.
c). Use UIView animations to move the tiles around and resize them using the constraints on the tiles. You can store the initial frames for each tile position and use those values to determine the target constraint values. You should easily be able to resize the position and size of the tiles in this way.
d). Apple's API for NSLayoutConstraint is a bit verbose and ugly. Consider using Masonry, a nicer Autolayout DSL, in order to keep your code clean.
You can continue to use -setFrame:, as long as you don't use Autolayout - the two approaches just don't play well together. If you want your app to run on more than one screen size, you need to use Autolayout, or else recalculate every frame and offset dynamically in code. If you don't (maybe it's an iPad app and you don't care about the Pro, then just use -setFrame:). But, on balance, I'd advise biting the bullet and just learning Autolayout

If your using auto layout you should set the translateAutoResizingMask to false otherwise you will keep getting this error. Instead you should be setting the new location using constraints only before the animation block then call layoutIfNeeded in the animation block. As you are setting the constraints in interface builder that has the effect of setting the translateAutoResizingMaskToConstraints to false automatically.

Related

What is the proper way of animate views when constraints applied?

I have to animate a couple of UIViews which I have added in a UIViewController inside the UIStoryboard. I have attached them with proper constraints so that they will always visible in a way I am looking. This is fine.
I am using https://github.com/satoshin21/Anima library to animate those views as per my need.
But the problem is they don't work as expected means, they are not animating in a direction or position it should be. I believe this is because of the constraints I have applied.
What is the best way to achieve this even if the constraints applied?
Setting, myView.translatesAutoresizingMaskIntoConstraints = true is coming up with lots of warning messages in console.
P.S. I am aware of taking references to the constraints in form of NSLayoutConstraints but this is not I am looking at as the above library is simply providing good chaining functions though we can do it without having references to the constraints.
The problem here, is that NSLayoutConstraint toggling works like properties in the sense that they are nothing but values which can be switched on/off, and alternated by playing with this toggling and other references to other possible values they can have. There's no real way of going around this that I know of unfortunately, and in fact i myself have built a small library similar to Anima, and it works rather well if you respect the NSLayoutConstraints' nature.
The proof of this is that under the hood of this Anima library, it's simply storing the animation points declared inside of the chain (inside Enum values in fact), and applying them as the animation moves along. Regardless, you should never re-set translatesAutoResizingMaskIntoConstraints to true when working with NSLayoutConstraints.
The second reason for this is that Constraints are the basis for all iOS frame operations, including .frame, and animations (which is why Anima works so well from the looks of it).
I wrote a post on this recently, but as I explain by referencing Apple:
Your problem is that when translatesAutoresizingMaskIntoConstraints is
called, methods like .frame or .frame.size are ignored/overriden
(depending on when you use them, before or after
translatesAutoresizingMaskIntoConstraints). As described by Apple:
Note that the autoresizing mask constraints fully specify the view’s
size and position; therefore, you cannot add additional constraints to
modify this size or position without introducing conflicts. If you
want to use Auto Layout to dynamically calculate the size and position
of your view, you must set this property to false, and then provide a
non ambiguous, nonconflicting set of constraints for the view.
UPDATED
Otherwise, try not to set translatesAutoResizingMaskIntoConstraints to true with these views, by doing that you basically tell your controller to ignore your constraints, and to try to apply constraints based on the .frame or .frame.size or position values set on the UIView. Thus, making your custom constraints obsolete. If by stopping this, you still get the issue, it's probably a constraint value issue, of which i can't give you much more advice without any code unfortunately.
First, you shouldn't set translatesAutoresizingMaskIntoConstraints to true if you have set suitable constraints on a view already. Setting this property to true will add more constraints to the view which leads to conflicts.
The general code for animation with constraints is
aConstraint.constant = 1234
anotherConstraint.isActive = false
thirdConstraint.isActive = true // thirdConstraint replaces anotherConstraint
UIView.animatewithDuration: 0.25) {
self.view.layoutIfNeeded()
}
Hope this helps. ;)
My solution is to remove constraints for that UIViewController and set the frame programmatically as per my needs. This works fine and no need to do this patchy thing with the usage of AutoLayout.

What if I use "Add missing constraints" option from storyboard, for assigning constraints in storyboard?

My question is that, can I trust on
Resolve autolayout issues’ - “Add missing constraints”
option(as in the attached Screen shot), which automatically adds constraints to the objects present in the storyboard?
I used this and tried running the app in all screen formats and it works fine, so can I continue using this or is it wrong to consider “Add missing constraints” for the constraint design. I’m new to auto layout and any kind of response, explaining this concept will be appreciated. Thanks
Utilizing the automatic constraint system is a bad idea. Most of the time, it won't work dynamically for all screen sizes. It generally adds constraints so objects appear correct in the current resolution you're designing in.
For example, it may pin a label you have placed in the center of an iPhone screen based on the distance from the left edge of the screen instead of the X value. That distance from the edge is going to stay the same when you run it on an iPad and it's going to be significantly off-center to satisfy that constraint.
However, depending on the situation, it could pin them correctly (IE leading edges to the super view instead of a constant). You can use the automatic constraint system for suggestions to reference what you need to add still, but I would not rely on it for dynamic UI.
Spend your time learning autolayout instead of throwing darts in the dark, it's really not as intimidating as it seems!
No you should not trust. Add missing constraints will add constraints that are missing. It will not add constraints that's your design actually want.
So I suggest you to understand what constraint you'll require to complete UI.
`For every control, Compiler need to know its : x,y,width and height.
For example : You drag & drop UILabel on your xib. Now you add top space constraint. So compiler will give warning. Need constraint for : X position.
On above example width and height will take according to text of label. Now you had given top space so its y position is known.
But for X you didn't give any constraint. In this case if you use Add missing constraints. compiler will add constraint for x position according to your placement. It may be your require constraint or may be not.
No iT just add the required constant so may be they are fixed so remove all constraints and try again.
Just like what the others say, "Add Missing Contraints" will always give the result of the layout that you desired. It's best that you learn to add constraints manually. But, there are times that you can be lazy for a couple of seconds by using that method but only for very simple layout.
I'll just share my thoughts about when should we use this method.
I use "Add Missing Constraints" when:
My layout is very much simple, it's like I know that when I do it automatically will yield the same result as doing it manually. This help saves a lot of time.
I am setting up constraints manually, but sometimes I don't know what constraints I am missing because the object that I am setting the constraints still shows red lines(missing constraints). This is just my purpose of learning.

If a view is set up in IB with Auto Layout, what happens if you try to change its frame programmatically?

A project I've started helping on did not use Auto Layout before and I'm updating it to use Auto Layout and size classes. There's a decent amount of frame manipulation code throughout the app (e.g. setting frame directly rather than changing constraint constants), and I'm wondering how this affects a view that's been set up with Auto Layout constraints.
I'm working on doing away with the frame-changing portions of code and changing it to update constraint constants where needed, but since I'm not yet 100% familiar with how every piece of the code works, it'd be helpful to have a better understanding of how auto layout and coded frame changes can affect each other so that if a view doesn't appear properly at runtime I can better determine if it's something I set up or perhaps a piece of older code somewhere that needs to be found and updated.
It's very simple. You just have to understand what auto layout is.
Here's how it works. The constraints are just a list of instructions; they do not, of themselves, actually do anything at all. There's a system message layoutSubviews, which is sent at moments you do not control — so you should imagine it could be sent any time. When layoutSubviews is actually sent, the constraints are consulted and obeyed (by doing exactly what you would do — that is, the runtime sets the frame, or the bounds and center, of each view).
Thus, you are free to change the frame of a view, but be aware that if layoutSubviews is sent and the constraints disagree with the frame that you set, the view will jump back to where the constraints say to put it.

Animating views constrained with auto layout

Just for fun I started to play with animations after successfully applying them to some basic background color changes. I have some different images on my "welcome/splash" view that I would like to animate. One image should appear from the bottom, another from the top and so on.
I immediately ran into trouble since I am using auto layout it was not that easy as animating background colors. I found this post How do I animate constraint changes? and after doing what was described at least my image was animating. However, is it correct that the console window should be filled with warnings/info/errors about constraint violations? Also, animating the vertical position of one image causes all the other image to animate too, probably because of some constraint relations.
How are you supposed to deal with that? when animating the "Bottom layout attribute" with a new constant value I expect only the view it belongs to animate, not the whole screen.
And how are you supposed to refer to the constraints? By outlets?
I am creating my views and constraints in storyboard. I deleted my code for animating the image views since it was just a mess. But whats done with the button and textfields in this tutorial is pretty much what I am trying to do with my images. Without animating one image causing the whole view to animate with it. http://www.raywenderlich.com/113674/ios-animation-tutorial-getting-started
However, is it correct that the console window should be filled with warnings/info/errors about constraint violations?
Learning Auto layout can be a challenge which will fill your days with such warnings/info/errors about constraints. Many iOS developer's struggle with Auto Layout at first. Auto layout is an addition to the layout process. The issue here is how are you going to deal with them. I suggest reading the Apple Auto Layout Guide, it contains a section on debugging. Also look at the Debugging Tricks and Tips section.
Here's a great article explaining more of the concepts behind Auto layout.
Build a simply app that has only one view and a subview, so that you can reduce the noise around layout constraint errors in more complicated layouts.
Here is a code snippet of how to animate a constraint.
if myViewTrailingConstraint.constant == -2 {
myViewTrailingConstraint.constant = 200
} else {
myViewTrailingConstraint.constant = -2
}
UIView.animateWithDuration(0.3,
animations: {
self.view.layoutIfNeeded()
},
completion: nil)
How are you supposed to deal with that?
Auto layout is an inter connected system of relationships between your views. A constraint represents a relationship. So you really need to think through your view's layout and plan your constraints first. Why? because if you plan to animate certain views you need to need to make sure that the constraint constant you are going to change will effect only that view in question.
And how are you supposed to refer to the constraints? By outlets?
You can create constraints solely in code or with the interface builder (outlets). I would suggest that you start with interface builder as even when you are comfortable working in code, it is useful and time saving to be able to do your initial layout in interface builder - so learn to use both.
Warnings are not normal - you have to solve them to avoid strange effects.
If animating a constraint value of one image moves other images, then indeed you must have some constraints in place that also change - e.g "equal width" constraints or such. Normally it just works - if it doesn't, you have to show the warnings you get and the constraints you set in order for someone to see what is going wrong.

Autolayout Dumbness (Mine)?

I'm using auto layout in one of my projects and am having trouble with what I thought should be a trivial task.
As an example, I have two text boxes below each other. I have created a vertical spacing constraint between the two, of 5 points.
I would expect that if i move the top text box (using setFrame), the constraint would mean that the bottom one would automatically move with it to enforce the constraint?
Is my thinking correct? or am I using auto layout in a way that it was not meant to. ie. I have misunderstood its purpose. Or should I be moving the top text box using a different manner other than setFrame?
Any input would be great. Thanks in advance :-)
PS. I have played around with using: setTranslatesAutoresizingMaskIntoConstraints which seems to work. However, this doesn't seem like the proper way to do this. My initial thoughts are that auto layout is supposed to handle size/position relationships with little code, in this case, i would expect it would be handled with zero code.
You can't use setFrame to do that. Auto-layout decides the frame. If you change it after the fact the constraints aren't automatically updated.
You could get a reference to the constraint that decides the Y position (either when you create it programmatically or using an IBOutlet) of the top box and change its value. That would trigger an update to recompute the position of everything and run layout again.
You would do that by changing the value of the constant property on the NSLayoutConstraint object.
Reference Docs Here

Resources