iosswiftanimationconstraintschained

Change UIView constraints in chained animations


I'm trying to create a chained animation of a UIButton with a delay between the two animations:

    let firstAnimation: TimeInterval = 0.3
    let secondAnimation: TimeInterval = 0.35
    let delay: TimeInterval = 2.0
    let animationDuration: TimeInterval = firstAnimation + secondAnimation + delay

    UIView.animateKeyframes(withDuration: animationDuration, delay: 0, options: [], animations: {
        UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: firstAnimation / animationDuration, animations: {
            self.enterEqualWidthConstraint.isActive = false
            self.enterWidthConstraint.constant = self.view.frame.width
            self.enterWidthConstraint.isActive = true
            self.enterButton.setTitle("HAVE FUN!", for: .normal)
            print("now1")
            self.view.layoutIfNeeded()
        })

        UIView.addKeyframe(withRelativeStartTime: delay / animationDuration, relativeDuration: secondAnimation / animationDuration, animations: {
            print("now2")
            self.enterWidthConstraint.constant = 0
            self.enterButton.setTitle("ENTER", for: .normal)
            self.view.layoutIfNeeded()
        })
    }, completion: nil)
}

The output gets printed at the same time:

now1
now2

And even self.enterWidthConstraint.constat changes before the delay.


How can I have a delay between the two chained animations?
If the constraint changes before the delay the text that's inside the UIButton disappears.
I need to do some other animations after these ones but I'm stuck at the first ones.

UPDATE
I tried the approach without keyframes but the delay doesn't work:

    UIView.animate(withDuration: 0.30, animations: {
        self.enterEqualWidthConstraint.isActive = false
        self.enterWidthConstraint.constant = self.view.frame.width
        self.enterWidthConstraint.isActive = true
        self.enterButton.setTitle("HAVE FUN!", for: .normal)
        print("now1")
        self.view.layoutIfNeeded()
    }, completion: { _ in
        UIView.animate(withDuration: 0.35, delay: 2.0, options: [], animations: {
            print("now2")
            self.enterButton.setTitle("ENTER", for: .normal)
            self.enterWidthConstraint.constant = 0
            self.view.layoutIfNeeded()
        }, completion: nil)
    })

It does the same thing.


Solution

  • UIView.animate(withDuration: 0.30, animations: {
            self.enterEqualWidthConstraint.isActive = false
            self.enterWidthConstraint.constant = self.view.frame.width
            self.enterWidthConstraint.isActive = true
            self.enterButton.setTitle("HAVE FUN!", for: .normal)
            print("now1")
            self.view.layoutIfNeeded()
        }, completion: { _ in
    
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                  self.enterButton.setTitle("ENTER", for: .normal)
    
                  UIView.animate(withDuration: 0.35, animations: {
    
                         print("now2")
                         self.enterWidthConstraint.constant = 0
                         self.view.layoutIfNeeded()
                 }, completion: nil)
            })
    });
    

    another way is to start two independent animations instead of chaining it

     UIView.animate(withDuration: 0.30, animations: {
                self.enterEqualWidthConstraint.isActive = false
                self.enterWidthConstraint.constant = self.view.frame.width
                self.enterWidthConstraint.isActive = true
                self.enterButton.setTitle("HAVE FUN!", for: .normal)
                print("now1")
                self.view.layoutIfNeeded()
            }, completion: { _ in             
                      self.enterButton.setTitle("ENTER", for: .normal)
       });
    
        UIView.animate(withDuration: 0.35, delay: 2.0, options: [], animations: {
            print("now2")
            self.enterWidthConstraint.constant = 0
            self.view.layoutIfNeeded()
        }, completion: nil)