user3608914
user3608914

Reputation: 136

Animation not working properly (Swift 3)

The following block is my code. I don't know why, but the animation delay is not working at all. No matter how many seconds I increase it to be..

func changeLabelisTapped(_ textLabel: UILabel, tapGesture: UITapGestureRecognizer) {
    for i in 0...2{
        UIView.animate(withDuration: 2, delay: Double(i)+2, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: ({
            self.subArray.remove(at: 0)
            self.collectionView.deleteItems(at: [IndexPath(row: 0, section: 0)])
        }), completion: nil)
    }
    let tempArray = Array(self.tabledData[3...self.tabledData.count-1])
    print(tempArray.count)
    for j in 0...2{
        UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: ({
            let newElement = tempArray[j]
            self.subArray.append(newElement)
            self.collectionView.insertItems(at:[IndexPath(row: j, section: 0)])
        }), completion: nil)
    }
}

Upvotes: 0

Views: 696

Answers (1)

gwinyai
gwinyai

Reputation: 2580

UIView animations do not queue. So everytime you process it through a loop the next animations cancels the one before and you do not see the delay. Instead of using a loop you should chain your animations so that the next one is called in the completion handler. Remove the loop and call the next animation to occur in the completion handler.

    var currentCount = 0

    UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: ({
        let newElement = tempArray[self.currentCount]
        self.subArray.append(newElement)
        self.collectionView.insertItems(at:[IndexPath(row: self.currentCount, section: 0)])
    }), completion: { _ in
       //next animation follows here 
       self.currentCount += 1
         UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: ({
        let newElement = tempArray[self.currentCount]
        self.subArray.append(newElement)
        self.collectionView.insertItems(at:[IndexPath(row: self.currentCount, section: 0)])
    }), completion: { _ in //and so on

     })

    }) 

If you want to cleanly control how many times the animation should happen, then you can use a recursive callback in the completion handler. To make this work, move your animation code into a function.

    let loopCount = 3

    var currentCount = 0

    func animateMyLabel() {
    UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: ({
        let newElement = tempArray[self.currentCount]
        self.subArray.append(newElement)
        self.collectionView.insertItems(at:[IndexPath(row: self.currentCount, section: 0)])
    }), completion: { _ in 
        if self.currentCount < self.loopCount {
          self.currentCount += 1
          //call the animation again
          self.animateMyLabel()
        }

    })

    }

EDIT

Having faced a similar delay issue in a current project I am doing, I have learnt a new way to force a delay if it is not working as expected which might help anyone that comes across this question. The solution is to force a delay in the completion handler just before the next animation should occur by adding delay(seconds: n) { } where n is the number of seconds the delay should happen for. So to add this to the problem:

   let loopCount = 3

    var currentCount = 0

    func animateMyLabel() {
    UIView.animate(withDuration: 2, delay: 2, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: ({
        let newElement = tempArray[self.currentCount]
        self.subArray.append(newElement)
        self.collectionView.insertItems(at:[IndexPath(row: self.currentCount, section: 0)])
    }), completion: { _ in 
        if self.currentCount < self.loopCount {
          //add the delay here
          delay(seconds: 2) {
          self.currentCount += 1
          //call the animation again
          self.animateMyLabel()
          }
        }

    })

    }

Furthermore, although animations do not queue, it is possible to use a loop for an animation provided the next animation in sequence has a delay which allows the previous animation to complete.

Upvotes: 1

Related Questions