SwiftUI自定义ClipShape过渡无动画问题求助
解决SwiftUI自定义Iris过渡无动画的问题
我帮你排查了代码,发现几个关键问题导致自定义的圆形过渡(iris)没有动画效果,下面逐一说明并给出修复方案:
问题1:自定义Modifier未实现动画支持
你写的ClipShapeModifier是普通的ViewModifier,但SwiftUI需要它能识别可动画的属性才能驱动shape的变化。我们需要把它改成AnimatableModifier,并传递shape的animatableData。
问题2:ScaledCircle的路径计算逻辑有偏差
原来的ScaledCircle在计算圆的位置时,当animatableData为0时,圆的半径不是0,导致初始状态不是完全裁剪,而是一个很小的圆,而且位置计算也不对——应该让圆始终以父视图中心为圆心,而不是偏移位置。
问题3:动画触发方式不正确
在SwiftUI 3及以后的版本中,更推荐用withAnimation包裹状态变量的修改来触发过渡动画,而不是在View上单独添加.animation modifier(这种方式在新的动画系统中优先级更低,容易失效)。
修复后的完整代码
1. 修正自定义Shape和Modifier
import SwiftUI struct ScaledCircle: Shape { var animatableData: CGFloat func path(in rect: CGRect) -> Path { // 计算从中心出发的最大半径(到视图角落的距离) let maxRadius = sqrt(rect.width * rect.width + rect.height * rect.height) / 2 // 当前半径:从0到maxRadius过渡 let currentRadius = maxRadius * animatableData // 始终以视图中心为圆心绘制圆 return Circle() .path(in: CGRect( x: rect.midX - currentRadius, y: rect.midY - currentRadius, width: currentRadius * 2, height: currentRadius * 2 )) } } // 改成AnimatableModifier,支持动画传递 struct ClipShapeModifier<T: Shape & Animatable>: AnimatableModifier { var shape: T // 传递shape的animatableData给modifier var animatableData: T.AnimatableData { get { shape.animatableData } set { shape.animatableData = newValue } } func body(content: Content) -> some View { content.clipShape(shape) } } extension AnyTransition { static var iris: AnyTransition { .modifier( active: ClipShapeModifier(shape: ScaledCircle(animatableData: 0)), identity: ClipShapeModifier(shape: ScaledCircle(animatableData: 1)) ) } }
2. 修正ContentView的动画触发
let screenWidth = UIScreen.main.bounds.size.width let screenHeight = UIScreen.main.bounds.size.height struct ContentView: View { @State var rectangleShown = false var body: some View { ZStack { Button(action: { // 用withAnimation包裹状态变化,确保触发过渡动画 withAnimation(.easeInOut(duration: 2)) { rectangleShown = true } }, label: { Text("Show Rectangle") }) .zIndex(0) if rectangleShown { Rectangle() .fill(.blue) // 加个颜色方便看效果 .frame(width: screenWidth*0.9, height: screenHeight*0.9) .transition(.iris) .zIndex(1) } } } }
验证效果
现在点击按钮,矩形会从中心以圆形展开的动画效果显示,完全符合你想要的iris过渡效果。另外,如果你需要反向隐藏的动画,只需要在按钮的action里切换rectangleShown的状态,动画会自动反向执行。
内容的提问来源于stack exchange,提问作者darekm
相关产品推荐
相关产品推荐

