我试图在SwiftUI视图的两个状态之间实现平滑过渡。下面的代码工作得很好,当偏移量改变时,SwiftUI将其解释为相同视图的两个状态(相同的标识),并通过视图的幻灯片进行转换。
import SwiftUI
struct AnimationView: View {
@State var number: Int = 0
var body: some View {
VStack {
// VIEW #1
Image(systemName: "0.circle.fill")
// VIEW #2
// Image(systemName: "\(self.number).circle.fill")
.id("my-view-identity")
.offset(x: self.number%2==0 ? 50 : -50, y: 0)
Button {
self.number += 1
} label: {
Text("Add 1")
}
}
.animation(.linear(duration: 1), value: self.number)
}
}
struct AnimationView_Previews: PreviewProvider {
static var previews: some View {
AnimationView()
}
}
问题是,当我尝试使用VIEW #2时,SwiftUI并没有将2个状态Image(systemName: "0.circle.fill")
和Image(systemName: "1.circle.fill")
解释为同一视图的2个状态,而是2个具有不同标识的不同视图,即使我手动设置了ID。这会导致第一个视图的淡出和第二个视图的淡入。
我还尝试使用子视图来创建某种视图代理,并将固定id设置为该视图,但这没有任何作用。
我看过WWDC21的讨论Demystify SwiftUI,我认为我对视图标识有很好的把握,但这一个我无法理解,我的猜测是,我们可以手动为单个视图设置不同的id,但我们不能做相反的事情:为不同的视图设置相同的id.我说的对吗有没有什么窍门可以让它成为可能?
1条答案
按热度按时间li9yvcax1#
方法一:
ZStack
和.opacity()
这个“诡计”奏效了。将所有图像视图放在
ZStack
中,并使用.opacity()
仅使当前视图可见:这样做是因为所有的视图都在屏幕上(只是不可见),所以当你移动它们时,它们不会被视为一个新的视图,而只是改变了不透明度。这将保留对象标识并允许移动动画正确工作。
方法二:
.id()
和.matchedGeometryEffect()
将
.id(self.number)
指定给视图以区分视图,并使用.matchedGeometryEffect()
统一动画的视图。在这里,视图修改器的顺序非常重要。
.id()
需要在.offset()
之后才能正常工作。在这两种方法中,我更喜欢 * 方法2*,因为它不会创建50个额外的视图!