SwiftUI:动画依赖于@ObjectBinding的更改

时间:2019-06-15 19:37:26

标签: swiftui combine

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

如果ImageuiImage)中没有数据,则将此old-logo的{​​{1}}参数传递给imageLoader(占位符),并将其替换与正确的异步加载一次:

BindableObject

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的那一刻起,我们如何制作此更改的动画?说我要淡入动画。

2 个答案:

答案 0 :(得分:1)

您不必调用.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)添加到图像中作为其视图修改器

答案 1 :(得分:0)

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

您可以使用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)!
                    }
                }
            }
    }
}