【问题标题】:How to animate a sequence of images using SwiftUI?如何使用 SwiftUI 为一系列图像设置动画?
【发布时间】:2019-11-05 10:35:52
【问题描述】:

如何使用 SwiftUI 框架为一系列图像(比如 Frame-1.png 到 Frame-6)制作动画?

我尝试过创建一个“图像”数组。然后我将 UIImage.animatedImage(with: images, duration: 1.0) 方法分配给一个名为“animatedImage”的变量 最后我在“ContentView.swift”的“body”中尝试了 Image(uiImage: animatedImage)


var images: [UIImage]! = [UIImage(named: "Sequence/frame-1")!,
                          UIImage(named: "Sequence/frame-2")!,
                          UIImage(named: "Sequence/frame-3")!,
                          UIImage(named: "Sequence/frame-4")!,
                          UIImage(named: "Sequence/frame-5")!,
                          UIImage(named: "Sequence/frame-6")!
]

let animatedImage : UIImage! = UIImage.animatedImage(with: images, duration: 1.0)

//////Then in the ContentView.swift I've tried this code:

struct ContentView : View {
    var body: some View {

        Image(uiImage: animatedImage)

    }
}

当我运行程序时,它只显示第一帧,但我期望帧的动画

【问题讨论】:

    标签: ios swift xcode animation swiftui


    【解决方案1】:

    接受的答案效果很好,但 bhagyash ingale 提到的不幸问题使得它很难使用。如果Image 的特定方法可以通过协议或其他方式重用,那将很有用。我有一个非常差而且可能很大的大炮来解决这个问题,也许它会及时更容易,但现在......

    class LoadingTimer {
    
        let publisher = Timer.publish(every: 0.1, on: .main, in: .default)
        private var timerCancellable: Cancellable?
    
        func start() {
            self.timerCancellable = publisher.connect()
        }
    
        func cancel() {
            self.timerCancellable?.cancel()
        }
    }
    
    struct LoadingView: View {
    
        @State private var index = 0
    
        private let images = (0...7).map { UIImage(named: "Image-\($0).jpg")! }
        private var timer = LoadingTimer()
    
        var body: some View {
    
            return Image(uiImage: images[index])
                .resizable()
                .frame(width: 100, height: 100, alignment: .center)
                .onReceive(
                    timer.publisher,
                    perform: { _ in
                        self.index = self.index + 1
                        if self.index >= 7 { self.index = 0 }
                    }
                )
                .onAppear { self.timer.start() }
                .onDisappear { self.timer.cancel() }
        }
    }
    

    我不喜欢这样,但它可以完成工作并依赖于 Image

    【讨论】:

    • 不错的答案,在你的回答中喜欢两件事:1- 使用 .map 方法,2- 使用 Publisher 来自 Combine 框架。顺便说一句,我不明白你为什么写private var image = Image(uiImage: images[index])(我猜不需要)
    • 啊,你是对的。它来自以前的尝试,忘记删除它。我会更新答案。
    • 这看起来很有希望。我收到错误 Cannot find type 'Cancellable' in scope - 需要为此加载任何库吗?
    • 需要导入combine。
    【解决方案2】:

    没有合并的版本

    import SwiftUI
    
    struct AnimatingImage: View {
        let images: [Image]
    
        @ObservedObject private var counter = Counter(interval: 0.05)
            
        var body: some View {
            images[counter.value % images.count]
        }
    }
    
    private class Counter: ObservableObject {
        private var timer: Timer?
    
        @Published var value: Int = 0
        
        init(interval: Double) {
            timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in self.value += 1 }
        }
    }
    

    你可以使用它

    struct LoadingView: View {
        private let images = (1...20).map { String(format: "i_%02d", $0) }.map { Image($0) }
        
        var body: some View {
            AnimatingImage(images: images)
        }
    }
    

    【讨论】:

      【解决方案3】:

      您是否尝试过制作自己的视图包装器?

      类似这样的:

      struct AnimatedImage: UIViewRepresentable {
          func makeUIView(context: Self.Context) -> UIImageView {
              return UIImageView(image: animatedImage)
          }
      
          func updateUIView(_ uiView: UIImageView, context: UIViewRepresentableContext<AnimatedImage>) {
          }
      }
      

      而用法是:

      struct ContentView : View {
          var body: some View {
              AnimatedImage()
          }
      }
      

      不确定这是否能解决您的问题,至少它不再是静态图像了:D

      在 Xcode Beta 2 模拟器上试过。

      【讨论】:

      • 非常感谢 Anton,您的回答完成了这项工作(很高兴成为拥有像您这样的人的社区的一员)
      【解决方案4】:

      我想要按顺序重复命名三个图像资产:thinking1、thinking2 和 thinking3。根据 nightwill 的回答,这适用于 iOS 14.4

      // images[counter.value % images.count] mod ensures counter never exceeds images.count
      
      import SwiftUI
      
      struct ThinkingView: View {
          
          let images: [Image] = (1...3).map { String(format: "thinking%01d", $0) }.map { Image($0) }
          
          @ObservedObject private var counter = Counter(interval: 0.1)
          
          var body: some View {
              images[counter.value % images.count]
          }
      }
      
      private class Counter: ObservableObject {
          private var timer: Timer?
      
          @Published var value: Int = 0
          
          init(interval: Double) {
              timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in self.value += 1 }
          }
      }
      
      struct ThinkingView_Previews: PreviewProvider {
          static var previews: some View {
              ThinkingView()
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-05-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多