Search code examples
objective-cxcodeanimationnslayoutconstraintiboutletcollection

Apply and animate many constraints to outlet collection of uiviews


I have an outlet collection of labels. The labels are in stack views parented by a stack view. When the view loads I'd like to have each label fade in and move slightly to the right one after the other. I can apply the constraint in a loop to offset it. But only one will animate back to the final position.

-(void)viewDidLoad {
[super viewDidLoad];
for (UILabel *lbl in _constructionlabels) {
    lbl.alpha = 0.0;
    leadingCnst=[NSLayoutConstraint
    constraintWithItem:lbl
    attribute:NSLayoutAttributeLeading
    relatedBy:NSLayoutRelationEqual
    toItem:[lbl superview]
    attribute:NSLayoutAttributeLeading
    multiplier:1.0
    constant:-25];
    [self.view addConstraint:leadingCnst];
}

}

-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];

leadingCnst.constant = 0;
[UIView animateWithDuration:0.33 delay:2 options:UIViewAnimationOptionCurveEaseOut animations:^{
    for (UILabel *lbl in self->_constructionlabels) {
        lbl.alpha = 1.0;
    }
    [self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];

}

How can I apply constraints to every needed label, and then animate all of them one after the other?


Solution

  • Keep references to each label constraint and start all animations at once, each with a delay.

    // Declare array to hold references to constraints
    NSMutableArray* _labelConstraints = [NSMutableArray array];
    
    -(void) viewDidLoad {
        [super viewDidLoad];
        for (UILabel * lbl in _constructionlabels) {
            lbl.alpha = 0.0;
    
            NSLayoutConstraint* leadingCnst = [NSLayoutConstraint
                constraintWithItem: lbl
                attribute: NSLayoutAttributeLeading
                relatedBy: NSLayoutRelationEqual
                toItem: [lbl superview]
                attribute: NSLayoutAttributeLeading
                multiplier: 1.0
                constant: -25
            ];
            [self.view addConstraint: leadingCnst];
    
            // Add constraint reference
            [_labelConstraints addObject: @(leadingCnst)];
        }
    }
    
    -(void) viewDidAppear: (BOOL) animated {
        [super viewDidAppear: animated];
    
        for (i = 0; i < [_constructionlabels count]; i++) {
    
            // Get label
            Label* lbl = [_constructionlabels objectAtIndex:i];        
    
            // Get constraint
            NSLayoutConstraint* labelConstraint = [_labelConstraints objectAtIndex:i];      
    
            // Animate
            [UIView animateWithDuration: 0.33 delay: i options: UIViewAnimationOptionCurveEaseOut animations: ^ {
                    lbl.alpha = 1.0;
                    labelConstraint.constant = 0;
                    [self.view layoutIfNeeded];
                }
                completion: ^ (BOOL finished) {}
            ];  
        }
    }
    

    Note: This is just a proof of concept - you may want to refactor the code.

    (It's been a while since I wrote ObjC, if you let me know any mistakes I'll correct them.)