我有一个遗产
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
的原因是
检查内存图调试器,为工作表中的图像视图创建的
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
}
}
}
}