SwiftUI中matchedGeometryEffect内scaleEffect无法动画的问题排查
问题:matchedGeometryEffect下scaleEffect无动画效果
我的代码中,Square视图的颜色能通过matchedGeometryEffect正常触发动画,但scaleEffect完全没动画效果。想问是scaleEffect本身无法通过matchedGeometryEffect实现动画,还是我的代码写法有问题?
演示动图:
代码示例:
import SwiftUI struct GeoEffectPractice: View { @Namespace var namespace @State var isSquareAtTop = false @State var dragY: CGFloat = .zero var body: some View { if isSquareAtTop { VStack { Square(atTop: isSquareAtTop) .matchedGeometryEffect(id: "box", in: namespace) .scaleEffect(0.5) Spacer() Button { isSquareAtTop = false } label: { Text("go back down") } } } else { Square(atTop: isSquareAtTop) .matchedGeometryEffect(id: "box", in: namespace) .scaleEffect(1) .offset(y: dragY) .gesture(DragGesture() .onChanged({ value in dragY = value.translation.height if dragY < -100 { withAnimation(.linear(duration: 3)) { isSquareAtTop = true dragY = 0 } } }) .onEnded({ value in withAnimation { dragY = 0 } })) } } } struct Square: View { var atTop: Bool var body: some View { Color(atTop ? .red : .blue) .frame(width: 100, height: 100) } } #Preview { GeoEffectPractice() }
解答
不是scaleEffect本身不支持,是你的代码修饰符顺序错了。
matchedGeometryEffect的作用是让两个视图的几何属性(位置、尺寸)平滑过渡,但它只会捕捉自己前面的视图状态变化。你现在把scaleEffect放在matchedGeometryEffect后面,相当于视图先完成几何匹配,再直接应用缩放,这就不是动画过渡,而是瞬间切换。
解决方法有两种:
方法1:调整修饰符顺序
把scaleEffect移到matchedGeometryEffect前面,让matchedGeometryEffect能捕捉到缩放带来的尺寸变化:
// 顶部视图部分 Square(atTop: isSquareAtTop) .scaleEffect(0.5) // 先设置缩放 .matchedGeometryEffect(id: "box", in: namespace) // 再匹配几何属性 // 底部视图部分 Square(atTop: isSquareAtTop) .scaleEffect(1) // 先设置缩放 .matchedGeometryEffect(id: "box", in: namespace) // 再匹配几何属性
方法2:把缩放整合到Square的frame中
直接在Square内部根据atTop调整frame大小,这样matchedGeometryEffect会自动识别尺寸变化并生成动画:
struct Square: View { var atTop: Bool var body: some View { Color(atTop ? .red : .blue) .frame(width: atTop ? 50 : 100, height: atTop ? 50 : 100) // 直接调整尺寸 } } // 此时主视图中可以去掉scaleEffect修饰符
两种方法都能让缩放效果和位置、颜色一起平滑过渡,你可以根据需求选择。
内容的提问来源于stack exchange,提问作者Plato
相关产品推荐
相关产品推荐

