带有动画层的 UIViewRepresentable 不会在工作表内部进行动画处理

问题描述 投票:0回答:1

我有一个遗产

UIImageView
使用
CABasicAnimation
制作动画。我将其包装在
UIViewRepresentable
中,以便将其嵌入到 SwiftUI 视图中。它对于主视图效果很好,但在使用
.sheet
修改器呈现的视图中,它不会产生动画。我的整个代码如下(用 Xcode 15.3 编译)。

import SwiftUI

@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

struct Microphone: UIViewRepresentable {
  func updateUIView(_ uiView: UIImageView, context: Context) {
  }
  
  func makeUIView(context: Context) -> UIImageView {
    let view = UIImageView(
      image:
        UIImage(
          systemName: "mic.circle.fill",
          withConfiguration: UIImage.SymbolConfiguration(pointSize: 50,weight: .bold, scale: .large)
        )?
        .withRenderingMode(.alwaysTemplate)
    )
    view.translatesAutoresizingMaskIntoConstraints = false
    view.tintColor = .green

    let animation = CABasicAnimation(keyPath: "transform.scale")
    animation.duration = 1.3
    animation.repeatCount = .greatestFiniteMagnitude
    animation.autoreverses = true
    animation.fromValue = 0.9
    animation.toValue = 1.1

    view.layer.add(animation, forKey: "scale")
    return view
  }
}

struct ContentView: View {
  @State var showSheet = false

  var body: some View {
    VStack {
      Microphone() // <-- animates perfectly
        .frame(width: 50, height: 50)

      Button("Show Sheet") { showSheet = true }
        .padding(.top)
    }
    .sheet(isPresented: $showSheet) {
      VStack {
        Microphone() // <-- does not animate
          .frame(width: 50, height: 50)
      }
    }
  }
}

#Preview {
    ContentView()
}

我选择

UIViewRepresentable
的原因是

  1. 该代码已经编写、测试并接受。
  2. 我无法制作一个流畅的原生 SwiftUI 视图,这可能是因为我的 SwiftUI 知识还很初级。我很乐意采用一种解决方案,在纯 SwiftUI 中为我提供流畅的动画。
ios swiftui uikit
1个回答
0
投票

检查内存图调试器,为工作表中的图像视图创建的

CABasicAnimation
由于某种原因被释放。从我的实验中,我还发现,如果在延迟一段时间后调用
layer.add(animation)
,动画确实会添加。我认为这要么是 SwiftUI 错误,要么视图可能还没有“准备好”添加动画。

layoutSubviews
中添加动画至少可以:

class ImageViewSubclass: UIImageView {
    override func layoutSubviews() {
        super.layoutSubviews()
        // optionally, check layer.animation(forKey: "scale") == nil first
        let animation = CABasicAnimation(keyPath: "transform.scale")
        animation.duration = 1.3
        animation.repeatCount = .greatestFiniteMagnitude
        animation.autoreverses = true
        animation.fromValue = 0.9
        animation.toValue = 1.1
        layer.add(animation, forKey: "scale")
    }
}

// in makeUIView:

let view = ImageViewSubclass(...)
...
return view

可能还有其他生命周期方法可以执行此操作。


执行此类动画的纯 SwiftUI 方法是:

struct Microphone: View {
    @State private var scale = 0.9
    
    var body: some View {
        Image(systemName: "mic.circle.fill")
            .imageScale(.large)
            .font(.system(size: 50, weight: .bold))
            .foregroundStyle(.green)
            .scaleEffect(scale)
            .onAppear {
                withAnimation(.linear(duration: 1.3).repeatForever(autoreverses: true)) {
                    scale = 1.1
                }
            }
            
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.