SwiftUI:动画更改依赖于@ObjectBinding

Posted

技术标签:

【中文标题】SwiftUI:动画更改依赖于@ObjectBinding【英文标题】:SwiftUI: animate changes that depend on @ObjectBinding 【发布时间】:2019-06-15 19:37:26 【问题描述】:

SwiftUI 具有使用 .animate() 的隐式动画和使用 .withAnimation() 的显式动画。但是,我不知道如何为图像更改设置动画:

struct ImageViewWidget : View 
  @ObjectBinding var imageLoader: ImageLoader

  init(imageURL: URL) 
    imageLoader = ImageLoader(imageURL: imageURL)
  

  var body: some View 
    Image(uiImage:
      (imageLoader.data.count == 0) ? UIImage(named: "logo-old")! :  UIImage(data: imageLoader.data)!)
        .resizable()
        .cornerRadius(5)
        .frame(width: 120, height:120)
  

如果imageLoaderBindableObject)中没有数据,则此ImageuiImage 参数将传递给old-logo(占位符),并在异步加载后将其替换为正确的参数:

class ImageLoader : BindableObject 
  let didChange = PassthroughSubject<Data, Never>()

  var data = Data() 
    didSet 
      didChange.send(data)
    
  

  init(imageURL: URL) 
    print("Image loader being initted!")
    let url = imageURL

    URLSession.shared.dataTask(with: url)  (data, _, _) in
      guard let data = data else  return 
      DispatchQueue.main.async 
        self.data = data
      
      .resume()

  

我如何为这个变化设置动画,在data.count 不再是0 的那一刻,我们有了图像?说我想要一个淡出动画..

【问题讨论】:

在你的图片加载器中尝试设置withAnimation yourDataVariable = data 但是在哪里?那里没有 UI(我正在添加 ImageLoader 的代码) 你仍然可以这样做 你似乎很确定。你能发布一个答案,以便我测试、投票和接受吗? 【参考方案1】:

如果你想使用基于环境对象(或可观察对象)的显式动画,你需要在你的视图中创建一些状态。

您可以使用onReceive 对视图中的可观察对象的更改做出反应,然后使用显式动画修改您的状态。

struct ImageViewWidget: View 
    @ObservedObject var imageLoader: ImageLoader
    @State var uiImage: UIImage = UIImage(named: "logo-old")!

    init(imageURL: URL) 
        imageLoader = ImageLoader(imageURL: imageURL)
    

    var body: some View 
        Image(uiImage: uiImage)
            .resizable()
            .cornerRadius(5)
            .frame(width: 120, height: 120)
            .onReceive(imageLoader.$data)  data in
                if data.count != 0 
                    withAnimation 
                        self.uiImage = UIImage(data: data)!
                    
                
            
    

【讨论】:

【参考方案2】:

您不必调用 .animate() 或 .withAnimation() 因为您只是在切换图像,您可以使用 .transition() 代替。 假设您已经使用@ObjectBinding(Beta5 中的@ObservedObject)成功更新了您的图像,您可以这样做:

var body: some View 
    if imageLoader.data.count == 0 
        Image(uiImage: UIImage(named: "logo-old")!)
        .resizable()
        .cornerRadius(5)
        .frame(width: 120, height:120)
        .transition(.opacity)
        .animation(.easeInOut(duration:1))
     else 
        Image(uiImage: UIImage(data: imageLoader.data)!)
        .resizable()
        .cornerRadius(5)
        .frame(width: 120, height:120)
        .transition(.opacity)
        .animation(.easeInOut(duration:1))
    

如果你想让过渡更漂亮,你可以使用自定义视图修饰符:

struct ScaleAndFade: ViewModifier 
    /// True when the transition is active.
    var isEnabled: Bool

    // fade the content view while transitioning in and
    // out of the container.
    func body(content: Content) -> some View 
        return content
            .scaleEffect(isEnabled ? 0.1 : 1)
            .opacity(isEnabled ? 0 : 1)
            //any other properties you want to transition
    



extension AnyTransition 
    static let scaleAndFade = AnyTransition.modifier(
        active: ScaleAndFade(isEnabled: true),
        identity: ScaleAndFade(isEnabled: false))

然后在您的 ImageViewWidget 中,将 .transition(.scaleAndFade) 添加到您的 Image 作为其视图修饰符

【讨论】:

这(你的第一个建议)不起作用。图像立即被替换,没有任何效果。我也认为它(不知何故)使图像同步加载。 抱歉,我忘了在 .transition 之后添加 `.animation(.easeInOut(duration:1))`,对我有用 还是不行。动画和过渡也不起作用

以上是关于SwiftUI:动画更改依赖于@ObjectBinding的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中动态更改动画的持续时间

来自 @Published 属性的 SwiftUI 动画从视图外部更改

SwiftUI - 使用淡入淡出动画更改文本

动画不适用于 SwiftUI 视图状态更改

SwiftUI视图状态更改使用withAnimation()方法无动画效果的解决

SwiftUI视图状态更改使用withAnimation()方法无动画效果的解决