Search code examples
iosimageanimationswiftuidelay

How to set a delay for the animated Image in SwiftUI?


I want to make an animal blink the eyes with different delays Int.random(in: 1..<9)

The problem is:

There are solutions for UIView animations and async thread delays... But is there any method for UIImage.animatedImage to make a delay between the blinks?

If there are no methods at all - how can I solve my issue?

The code:

File for all animations:

 import SwiftUI
  struct AnimalAnimation: UIViewRepresentable {
    @Binding var animalImages: [UIImage?]
    @Binding var size: CGRect

    func makeUIView(context: Self.Context) -> UIView {

        let someView = UIView(frame: CGRect(x: size.minX, y: size.minY , width: size.width, height: size.height))
        let someImage = UIImageView(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let animated = UIImage.animatedImage(with: animalImages as! [UIImage], duration: 0.9)!

        someImage.clipsToBounds = true
        someImage.autoresizesSubviews = true
        someImage.contentMode = UIView.ContentMode.scaleAspectFit
        someImage.image = animated
        someView.addSubview(someImage)
        return someView
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<AnimalAnimation>) {
    }
  }

I'm using this in ContentView's body

            VStack {
                    AnimalAnimation(animalImages: $animal1Array, size: $animal1Size)
            }

The images:

animalImages = [UIImage(named: "eyes-open"),UIImage(named: "eyes-closed")]

Solution

  • Here is SwiftUI only variant. Tested with Xcode 11.4 / iOS 13.4. (Sizes/frames if needed can be configured, for demo it does not matter)

    demo

    struct EyesBlinkView: View {
    
        enum EyesState: String {
            case open = "eyes-open"
            case closed = "eyes-closed"
        }
    
        @State private var eyesState = EyesState.open
        var body: some View {
            Image(eyesState.rawValue).resizable().scaledToFit()
                .onAppear { self.blink() }
        }
    
        private func blink() {
            self.eyesState = .closed
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
                self.eyesState = .open
    
                DispatchQueue.main.asyncAfter(deadline: 
                      DispatchTime.now().advanced(by: .seconds(Int.random(in: 1..<9)))) {
                    self.blink()
                }
            }
        }
    }