如何在SwiftUI中实现图像动画,以播放帧动画

9

我想在SwiftUI的ImageView中制作动画

首先,我尝试创建一些变量和一个函数来切换Image("imageVariable")。它确实发生了变化,但没有动画,即使尝试使用withAnimation { }方法。

其次,我尝试使用UIKit视图。在这里,动画起作用,但我无法应用resizable()修饰符或设置固定框架。

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)
        }
    }
}

在方法1中,我可以更改图像但无法进行动画,而在方法2中,我可以进行动画但无法控制其大小。


1
关于第二次尝试...你能不能把UI动画和contentMode都移动到UIImageView中呢?这样UIViewRelatable就可以简单地将内容渲染为View的全尺寸。 - user7014451
@dfd 我会尝试在UIView中完成大部分工作,然后在SwiftUI中使用它进行显示。 - arthas
@kontiki,我所说的动画图像是指在同一视图中播放gif/帧动画,而不是视图转换。目前在SwiftUI中似乎不可能实现,因此我放弃了。您认为可以在没有UIKit的情况下完成吗? - arthas
哦,如果是这样的话,那么你现在最好还是使用UIKit(至少暂时是这样)。 - kontiki
4个回答

9

我使用UIViewRepresentable协议解决了这个问题。在此,我返回了一个带有ImageView作为其子视图的UIView。这使我对子元素的大小等有更多的控制。

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
这个方法对我非常有帮助,经过长时间的查找和研究。谢谢!我不得不将图像和animatedImage移动到makeUIView函数中。虽然并不理想,但否则代码无法编译。 - agrippa
不适用于watchOS,因为SwiftUI中不存在UIImageView。 - Bill Chan

6
如果你需要一个功能强大的、跨平台的SwiftUI实现动态图片(如GIF/APNG/WebP)的解决方案,我建议使用SDWebImageSwiftUI。这个框架基于现有成功的图片加载框架SDWebImage,并提供了SwiftUI绑定。
要播放动画,请使用AnimatedImage视图。
var body: some View {
    Group {
        // Network
        AnimatedImage(url: URL(string: "https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif"))
        .onFailure(perform: { (error) in
            // Error
        })
    }
}

2

在模型中:

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
            }
        })
  }
}

在视图中:

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)
         
}
}

1
我已经创建了一个图像动画类,可以轻松地重复使用。
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>) {

    }
}

使用方法如下:
ImageAnimated(imageSize: CGSize(width: size, height: size), imageNames: ["loading1","loading2","loading3","loading4"], duration: 0.3)
                        .frame(width: size, height: size, alignment: .center)

我尝试了图像动画类,并且在单个视图上运行良好。但如果我在TabView上实现它,则动画不会启动。如果我关闭iPhone并重新打开它,则动画开始。也许我需要某种.onAppear(),但我不知道应该把它放在哪里... - Michael
我不知道为什么在TabView()中无法使用ImageAnimated(),我做错了什么? - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接