Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programatic NSLayoutConstraint animation issues

So i am trying to animate some layout constraints and am having miserable luck, i just dont see anything and then i get an error message saying how it can't simultaneously satisfy all constrains etc.

I am using a class called PullableView whose animation is using old style [UIView commitAnimation] so i subclassed it and added in my code for what i believe would be for animating constraints...No such luck and attempting to animate it or even get it to do much of anything is proving to be difficult i just get "Unable to simultaneously satisfy constraints".. Problem is i am fairly new to this constraint business so i would not know where to start.

here is the error the other one is pretty much the same but for centerY.

"<NSLayoutConstraint:0x7c8a3a0 StyledPullableView:0x905a9b0.centerX == UIView:0x9054680.centerX>", "<NSLayoutConstraint:0x7c6b480 StyledPullableView:0x905a9b0.centerX == UIView:0x9054680.centerX + 128>"

I of course do [pullRightView setTranslatesAutoresizingMaskIntoConstraints:NO]; before calling this

any help appreciated.

- (void)setOpenedForConstraints:(BOOL)op animated:(BOOL)anim
{
    opened = op;

    if (anim)
    {        
        NSLayoutConstraint *constraintX = [NSLayoutConstraint constraintWithItem:self
                                                                               attribute:NSLayoutAttributeCenterX
                                                                               relatedBy:NSLayoutRelationEqual
                                                                                  toItem:_parentView
                                                                               attribute:NSLayoutAttributeCenterX
                                                                              multiplier:1
                                                                                constant:0];

        NSLayoutConstraint *constraintY = [NSLayoutConstraint constraintWithItem:self
                                                                               attribute:NSLayoutAttributeCenterY
                                                                               relatedBy:NSLayoutRelationEqual
                                                                                  toItem:_parentView
                                                                               attribute:NSLayoutAttributeCenterY
                                                                              multiplier:1
                                                                                constant:0];

        [_parentView addConstraint:constraintX];
        [_parentView addConstraint:constraintY];

        constraintX.constant = opened ? self.openedCenter.x : self.closedCenter.x;
        constraintY.constant = opened ? self.openedCenter.y : self.closedCenter.y;
    }

    if (anim)
    {

        // For the duration of the animation, no further interaction with the view is permitted
        dragRecognizer.enabled = NO;
        tapRecognizer.enabled = NO;

        //[UIView commitAnimations];

        [UIView animateWithDuration:animationDuration
                         animations:^
         {
             [self layoutIfNeeded];
         }];

    }
    else
    {

        if ([delegate respondsToSelector:@selector(pullableView:didChangeState:)])
        {
            [delegate pullableView:self didChangeState:opened];
        }
    }
}
like image 506
Genhain Avatar asked May 21 '13 04:05

Genhain


1 Answers

A couple of thoughts:

  1. You're creating new constraints here. Generally you will adjust the constant of an existing constraint. Or, if necessary, you will remove the old constraint, and add a new one.

  2. When changing an existing constraint, there are two approaches. One is to iterate through the view's constraints, finding the one in question, and then adjusting the constant for that. The other approach (and far easier when the view is one that you added in Interface Builder) is to create an IBOutlet for the constraint right in Interface Builder. Then you can animate that.

    • This answer, while focusing on a another problem, illustrates the process of creating an IBOutlet for a constraint and then animating the changing of its value.

    • If you have to iterate through the constraints looking for constraints (e.g. picking out one constraint out of a whole collection that you may have created with visual format language, for example), then the replaceLeadingAndTopWithCenterConstraints illustrated in this answer might be a helpful example of how you find a constraint on an existing view. This particular example is removing a constraint and adding a new one, but you could, just as easily, once you find the existing constraint, adjust its constant.


I'm not entirely sure of the UX you're going for, but let's say you have a panel you want to slide off to the left of the screen:

  1. The first issue is what constraints you have defined: In this example, I'd have four constraints defined, three to the superview (namely top, bottom, and left, but not the right) and a width constraint. And if you're doing this in IB, it can be finicky (e.g. you cannot remove the right constraint until you add the width constraint). Just make sure that the constraints are fully defined, but minimally so.

  2. Then I can animate the sliding of the panel off screen just by changing the constant of the left constraint:

    - (IBAction)touchUpInsideButton:(id)sender
    {
        self.offscreen = !self.offscreen;
    
        [UIView animateWithDuration:0.5
                         animations:^{
                             self.panelLeftConstraint.constant = ([self isOffscreen] ? -kHowMuchToSlideOffToLeft : 0.0);
                             [self.view layoutIfNeeded];
                         }];
    }
    
  3. You can see now why it was important to make sure that there was no right constraint defined. Because if I adjusted the left constraint while I had a width constraint and a right constraint defined, then it would become unsatisfiable. By making sure the constraints were minimally defined (though unambiguously defined) at the start, I can change the left constraint and the view now animates like I wanted it to.

like image 197
Rob Avatar answered Nov 10 '22 05:11

Rob