Peter Peter - 5 months ago 32
Objective-C Question

Auto layout and animation

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:

enter image description here

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:

enter image description here

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?


  1. Can I ignore the
    Unable to simultaneously satisfy constraints
    ? Or will that cause trouble down the road?

  2. 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?

  3. I would very much like to keep animating using
    setFrame:


Answer

To your points:

  1. "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.

  2. 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.

  3. 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