1

Background

Thanks to the excellent answer here, I've managed to make a series of labels fade in sequentially.

However, the delay referred to in the function in the extension only triggers when the previous fade has completed (I think) so there is at least 1 second between them. Ideally I want to edit the model so that the delay refers to from the time the view loads, not the time the previous one has completed.

i.e. If I make the delay quite small, the second one would start fading in before the first one had finished.

This is the extension :

extension UIView {

    func fadeIn(duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: ((Bool)->())? = nil) {
        self.alpha = 0.0
        UIView.animate(withDuration: duration, delay: delay, options: .curveEaseIn, animations: {
            self.alpha = 1.0
        }, completion: completion)
    }
}

and this is my implementation on a VC with 6 labels :

func animateLabels() {
    self.titleOutlet.alpha = 0.0
    self.line1Outlet.alpha = 0.0
    self.line2Outlet.alpha = 0.0
    self.line3Outlet.alpha = 0.0
    self.line4Outlet.alpha = 0.0
    self.line5Outlet.alpha = 0.0
    self.line6Outlet.alpha = 0.0
    self.titleOutlet.fadeIn(delay: 0.2, completion: { _ in
    self.line1Outlet.fadeIn(delay: 0.4, completion: { _ in
    self.line2Outlet.fadeIn(delay: 0.6, completion: { _ in
    self.line3Outlet.fadeIn(delay: 0.8, completion: { _ in
    self.line4Outlet.fadeIn(delay: 1.0, completion: { _ in
    self.line5Outlet.fadeIn(delay: 1.2, completion: { _ in
    self.line6Outlet.fadeIn(delay: 1.4, completion: { _ in
    })})})})})})})
    }

What am I trying to get to?

So what I'm trying to get to is having line2outlet start to fade 0.4 seconds after the load of the page but it is starting only when line1 has finished fading in (which takes 1 second and is delayed by 0.2s).

What have I tried?

I have attempted to write my own delay function after reading up. I tried adding this to the extension :

func delay(interval: TimeInterval, closure: @escaping () -> Void) {
            DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
                closure()
            }
        }

and implementing like this :

self.line1Outlet.fadeIn(delay: 0, completion: { _ in
        delay(interval: 1, closure: ()->Void)

but that got loads of errors! I also tried setting minus numbers for the delays but that didn't work either.

Any help much appreciated!

nc14
  • 539
  • 1
  • 8
  • 26

1 Answers1

1

One possible solution that I have used before is to use a loop with an incrementing delay variable feeding into the standard UIView.animate() method. This method does not use completion handlers.

Make an array of the labels:

var labels = [titleOutlet, line1Outlet] // etc...

Use this extension:

extension UIView {
    func fadeIn(duration: TimeInterval = 1, delay: TimeInterval = 0) {
        self.alpha = 0
        UIView.animate(withDuration: duration, delay: delay, options: [], animations: { self.alpha = 1 }, completion: nil)
    }
}

Then use it like this:

var delay = 0.2
var duration = 0.5

for label in labels {
    label.fadeIn(duration: duration, delay: delay)
    delay += 0.2 // Increment delay
}
Chris
  • 4,009
  • 3
  • 21
  • 52
  • 1
    thanks for helping, on the extension it says a , separator is missing and that Incorrect argument label in call (have 'withDuration:delay:completion:', expected 'withDuration:animations:completion:') – nc14 Jun 14 '19 at 20:42
  • @nc14 Oops! That was a copy-paste error (I’m not actually at my computer). Should be fixed now. Essentially it’s the version of `UIView.animate()` that includes a delay. – Chris Jun 14 '19 at 20:46
  • @nc14 No problem :) – Chris Jun 14 '19 at 21:07