Swift中如何解决clipped图片使用matchedGeometryEffect时的动画闪烁问题?
问题:使用matchedGeometryEffect时带裁剪的图片动画闪烁
我熟悉matchedGeometryEffect在普通元素上的工作原理,但处理图片时遇到了动画闪烁问题。当前代码在图片滑出时表现正常,但对指定frame的图片启用.clipped()裁剪后,切换showing状态的动画会出现闪烁(慢放时能看到裁剪图片的边框在填满空间前显现)。我已经尝试过切换frame()与containerRelativeFrame、调整aspectRatio,并且把.clipped()放在frame设置之后,但问题仍未解决。
以下是我的代码:
import SwiftUI struct View: View { @Namespace var nm @State var showing = false @State private var offset: CGSize = .zero var body: some View { GeometryReader { geo in ZStack { if !showing { VStack { HStack { Spacer() Image("im1") .resizable() .matchedGeometryEffect(id: "im", in: nm) .aspectRatio(contentMode: .fill) .containerRelativeFrame(.horizontal) { size, axis in size * 0.224 } .containerRelativeFrame(.vertical) { size, axis in size * 0.111 } .clipped() .onTapGesture { withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) { showing = true } } Spacer() } } .padding(.top) } else { VStack { Spacer() Image("im1") .resizable() .matchedGeometryEffect(id: "im", in: nm) .aspectRatio(contentMode: .fit) .onTapGesture { withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) { showing = false } } .offset(x: offset.width, y: offset.height) .gesture ( DragGesture() .onChanged { value in if value.translation.height > 0 { withAnimation(.spring(response: 0.1, dampingFraction: 0.95)) { offset = value.translation } } } .onEnded { value in if offset.height > 100 { withAnimation(.spring(response: 0.4, dampingFraction: 0.8)){ offset = .zero showing = false } } else { withAnimation(.spring(response: 0.4, dampingFraction: 0.8)){ offset = .zero } } } ) Spacer() } } } } } }
解决方案
闪烁问题的核心是裁剪时机与动画同步不匹配,可以通过以下几种方式修复:
调整matchedGeometryEffect的位置:将
.matchedGeometryEffect移到.clipped()之后,确保裁剪在几何匹配动画开始前就生效,避免动画过程中裁剪区域的突变:Image("im1") .resizable() .aspectRatio(contentMode: .fill) .containerRelativeFrame(.horizontal) { size, axis in size * 0.224 } .containerRelativeFrame(.vertical) { size, axis in size * 0.111 } .clipped() .matchedGeometryEffect(id: "im", in: nm) // 移到裁剪之后给裁剪添加动画过渡:让裁剪区域的变化也参与到动画序列中,避免突然的裁剪切换:
.clipped() .animation(.spring(response: 0.3, dampingFraction: 0.7), value: showing)统一两张图片的裁剪状态:给第二个分支的图片也添加
.clipped(),保持前后状态的一致性,减少动画中的状态突变:Image("im1") .resizable() .matchedGeometryEffect(id: "im", in: nm) .aspectRatio(contentMode: .fit) .clipped() // 第二个分支也添加裁剪替换为clipShape:尝试用
clipShape(Rectangle())替代默认的.clipped(),部分场景下能提升动画兼容性:.clipShape(Rectangle())
内容的提问来源于stack exchange,提问作者Karam Eid
相关产品推荐
相关产品推荐

