I want to animate images in SwiftUI's Image view

First, I tried creating some variables and a function to toggle the Image("imageVariable"). It changes but there is no animation even tried the withAnimation { } method

Secondly, I tried to use a UIKit view. Here, the animation works but I can't apply the resizable() modifier or a set a fixed frame

var images: [UIImage]! = [UIImage(named: "pushup001")!, UIImage(named: "pushup002")!] let animatedImage = UIImage.animatedImage(with: images, duration: 0.5) struct workoutAnimation: UIViewRepresentable { func makeUIView(context: Self.Context) -> UIImageView { return UIImageView(image: animatedImage) } func updateUIView(_ uiView: UIImageView, context: UIViewRepresentableContext<workoutAnimation>) { } } struct WorkoutView: View { var body: some View { VStack { workoutAnimation().aspectRatio(contentMode: .fit) } } } 

In method 1 I can change the image but not animate, while, in method 2 I can animate but not control it's size

4

4 Answers

If you want a robust and cross-platform SwiftUI implementation for animated images, like GIF/APNG/WebP, I recommend using SDWebImageSwiftUI. This framework is based on exist success image loading framework SDWebImage and provides a SwiftUI binding.

To play the animation, use AnimatedImage view.

var body: some View { Group { // Network AnimatedImage(url: URL(string: "")) .onFailure(perform: { (error) in // Error }) } } 

I solved this using UIViewRepresentable protocol. Here I returned a UIView with the ImageView as it's subview. This gave me more control over the child's size, etc.

import SwiftUI var images : [UIImage]! = [UIImage(named: "pushup001")!, UIImage(named: "pushup002")!] let animatedImage = UIImage.animatedImage(with: images, duration: 0.5) struct workoutAnimation: UIViewRepresentable { func makeUIView(context: Self.Context) -> UIView { let someView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) let someImage = UIImageView(frame: CGRect(x: 20, y: 100, width: 360, height: 180)) someImage.clipsToBounds = true someImage.layer.cornerRadius = 20 someImage.autoresizesSubviews = true someImage.contentMode = UIView.ContentMode.scaleAspectFill someImage.image = animatedImage someView.addSubview(someImage) return someView } func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<workoutAnimation>) { } } struct WorkoutView: View { var body: some View { VStack (alignment: HorizontalAlignment.center, spacing: 10) { workoutAnimation() Text("zzzz") } } } 
1

I have created an image animation class that can be easily reused

import SwiftUI struct ImageAnimated: UIViewRepresentable { let imageSize: CGSize let imageNames: [String] let duration: Double = 0.5 func makeUIView(context: Self.Context) -> UIView { let containerView = UIView(frame: CGRect(x: 0, y: 0 , width: imageSize.width, height: imageSize.height)) let animationImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)) animationImageView.clipsToBounds = true animationImageView.layer.cornerRadius = 5 animationImageView.autoresizesSubviews = true animationImageView.contentMode = UIView.ContentMode.scaleAspectFill var images = [UIImage]() imageNames.forEach { imageName in if let img = UIImage(named: imageName) { images.append(img) } } animationImageView.image = UIImage.animatedImage(with: images, duration: duration) containerView.addSubview(animationImageView) return containerView } func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<ImageAnimated>) { } } 

The way to use it:

ImageAnimated(imageSize: CGSize(width: size, height: size), imageNames: ["loading1","loading2","loading3","loading4"], duration: 0.3) .frame(width: size, height: size, alignment: .center) 
2

in model :

var publisher : Timer? @Published var index = 0 func startTimer() { index = 0 publisher = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: {_ in if self.index < count/*count of frames*/{ self.index += 1 } else if let timer = self.publisher { timer.invalidate() self.publisher = nil } }) } } 

in view :

struct MyAnimationView : View { let width : CGFloat let images = (0...60).map { UIImage(named: "tile\($0)")! } @StateObject var viewmodel : MyViewModel var body: some View { Image(uiImage: images[viewmodel.index]) .resizable() .frame(width: width, height: width, alignment: .center) } } 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy