Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoLayout, Constraints and Animation

Let's assume that I have this UIView:

enter image description here

with these relative constraints:

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *leftMarginConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topMarginConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *widthConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstraint;

Okay now let's assume that when the user click on that UIButton the button should move to the bottom-right corner of the view. We can easily use two constraints defining bottom space between the button and the bottom layout guide and right space (trailing space) from the button and the right edge of the view.

The problem it's that the UIButton already had two constraints (left/top) AND two contraints defining its width and its height so we can't add two new constraints because they would conflict with the other ones.

Simple and common situation for an animation scenario, but it causing me some problems. Ideas?

EDIT

When the user taps on the UIButton, I need that the button:

  1. change its title to "Second"
  2. wait 1 second and move to the bottom-right corner (remove top and left margin constraints and add bottom and right margin constraints)
  3. change its title to "Third"
  4. wait 1 second and move to the top-right corner (remove bottom margin constraint and add top margin constraint)

Am I seriously bound to use this messy code?

@implementation ViewController
{
    NSLayoutConstraint *_topMarginConstraint;
    NSLayoutConstraint *_leftMarginConstraint;
    NSLayoutConstraint *_bottomMarginConstraint;
    NSLayoutConstraint *_rightMarginConstraint;
}

- (IBAction)buttonPressed:(id)sender
{
    UIButton *button = sender;

    // 1.
    [sender setTitle:@"Second" forState:UIControlStateNormal];

    // 2.
    double delayInSeconds = 1.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
        [button removeConstraints:@[self.leftMarginConstraint, self.topMarginConstraint]];
        _bottomMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view
                                                               attribute:NSLayoutAttributeBottom
                                                               relatedBy:0 toItem:button
                                                               attribute:NSLayoutAttributeBottom
                                                              multiplier:1
                                                                constant:20];
        [self.view addConstraint:_bottomMarginConstraint];

        _rightMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view
                                                              attribute:NSLayoutAttributeRight
                                                              relatedBy:0 toItem:button
                                                              attribute:NSLayoutAttributeRight
                                                             multiplier:1
                                                               constant:20];
        [self.view addConstraint:_rightMarginConstraint];

        [UIView animateWithDuration:1 animations:^{
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
            // 3.
            [sender setTitle:@"Third" forState:UIControlStateNormal];

            // 4.
            double delayInSeconds = 1.0;
            dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
            dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
                [button removeConstraint:_bottomMarginConstraint];
                _topMarginConstraint = [NSLayoutConstraint constraintWithItem:self.view
                                                                       attribute:NSLayoutAttributeTop
                                                                       relatedBy:0 toItem:button
                                                                       attribute:NSLayoutAttributeTop
                                                                      multiplier:1
                                                                        constant:20];
                [UIView animateWithDuration:1 animations:^{
                    [self.view layoutIfNeeded];
                }];
            });
        }];
    });
}

Serious? :D

like image 607
Fred Collins Avatar asked Nov 11 '13 05:11

Fred Collins


2 Answers

Delete the left and top constraints, add the bottom and right constraints, then call layoutIfNeeded in an animation block to animate to the new position. The code in the second method, move2 could be embedded in the completion block of move1, but I find it easier to read if you keep the two moves in separate methods (it does necessitate the addition of another property, bottomCon):

- (IBAction)move1:(UIButton *)sender {
    [sender setTitle:@"Second" forState:UIControlStateNormal];
    [sender layoutIfNeeded];
    [self.view removeConstraints:@[self.leftCon,self.topCon]];
    self.bottomCon = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.button attribute:NSLayoutAttributeBottom multiplier:1 constant:20];
    [self.view addConstraint:self.bottomCon];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:0 toItem:self.button attribute:NSLayoutAttributeRight multiplier:1 constant:20]];
    [UIView animateWithDuration:1 delay:1.0 options:0 animations:^{
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        [sender setTitle:@"Third" forState:UIControlStateNormal];
        [self move2];
    }];
}

-(void)move2 {
    [self.view removeConstraint:self.bottomCon];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.button attribute:NSLayoutAttributeTop multiplier:1 constant:-20]];
    [UIView animateWithDuration:1 delay:1.0 options:0 animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];
}
like image 151
rdelmar Avatar answered Oct 14 '22 13:10

rdelmar


For animation you need to change constant of the left and top constraint instead of adding new constraint. As follows:

 self.mBtnTopConstraint.constant = yourval1;
    self.mBtmLeftConstraint.constant = yourval2;
    [yourbtn setNeedsUpdateConstraints];

    [UIView animateWithDuration:0.5 animations:^{

            [yourbtn layoutIfNeeded];

        } completion:^(BOOL isFinished){
        }];

Hope this helps.

like image 31
Nuzhat Zari Avatar answered Oct 14 '22 14:10

Nuzhat Zari