我想将默认过渡 (
AnyTransition.opacity
) 替换为也应用了 matchedGeometryEffect()
修饰符的视图的另一个过渡。我尝试了几种不同的技术,但似乎没有一个能实现这一点。
首先,这是
matchedGeometryEffect
本身。 ItemView
使用默认动画从一个容器“移动”到另一个容器。
struct ContentView: View {
@Namespace private var itemMovement
@State private var isInFirst = true
var body: some View {
VStack {
Text("First Container")
HStack {
if isInFirst {
ItemView()
.matchedGeometryEffect(id: "item", in: itemMovement)
}
}
.padding()
.border(.red)
Text("Second Container")
HStack {
if !isInFirst {
ItemView()
.matchedGeometryEffect(id: "item", in: itemMovement)
}
}
.padding()
.border(.red)
Button("Move Item") {
withAnimation {
isInFirst.toggle()
}
}
}
}
}
struct ItemView: View {
var body: some View {
Circle()
.foregroundStyle(.blue)
.frame(width: 100, height: 100)
}
}
仔细观察圆圈,您可以看到动画中间颜色变浅。我相信这是默认转换的效果,
AnyTransition.opacity
。为了尝试删除该过渡,我尝试在圆形视图上显式添加 AnyTransition.identity
。
struct ContentView: View {
@Namespace private var itemMovement
@State private var isInFirst = true
var body: some View {
VStack {
Text("First Container")
HStack {
if isInFirst {
ItemView()
.transition(.identity) // <-- here
.matchedGeometryEffect(id: "item", in: itemMovement)
}
}
.padding()
.border(.red)
Text("Second Container")
HStack {
if !isInFirst {
ItemView()
.transition(.identity) // <-- here
.matchedGeometryEffect(id: "item", in: itemMovement)
}
}
.padding()
.border(.red)
Button("Move Item") {
withAnimation {
isInFirst.toggle()
}
}
}
}
}
然而,结果似乎圆形视图不再使用
matchedGeometryEffect
进行动画处理。我尝试了几种变体,例如将 .transition()
修改器放在 .matchedGeometryEffect()
修改器之后,并一次仅在一个视图上使用 .transition()
修改器 - 希望了解为什么会发生这种跳转到最终位置的情况,并且制定新的解决方案。我还没想明白。
任何人都可以帮我弄清楚如何将过渡与匹配的几何效果结合起来吗?除了这个简化的示例之外,我希望创建一个基于可动画视图修改器的自定义过渡,该修改器在内部将
.rotation3DEffect()
应用于视图。
此示例似乎显示单个圆圈从一个容器“移动”到另一个容器。事实上,它显示了两个圆圈,其中
matchedGeometryEffect
用于协调位置并平滑过渡。您观察到较小的颜色变化的原因是因为第一个圆圈在第二个圆圈淡入的同时淡出。如果仔细观察,HStack 的红色边框和第二个容器的标签也可以在过渡时可以在圆形形状下方看到,因为两个圆在中间点都不是不透明的。
您使用
matchedGeometryEffect
的方式不太正确,因为您没有识别来源。根据 documentation,如果“isSource = true 的组中当前插入的视图数量不完全是一个”,结果是未定义的。您可能会争辩说一次只能看到一个视图,但将 isSource: isInFirst
添加到第一种情况并将 isSource: !isInFirst
添加到第二种情况仍然会更清晰。
实现动画的另一种方法是将位置变化应用于单个圆圈,例如:
var body: some View {
VStack {
Text("First Container")
VStack {
HStack {
if isInFirst {
ItemView().hidden()
}
}
.padding()
.border(.red)
Text("Second Container")
HStack {
if !isInFirst {
ItemView().hidden()
}
}
.padding()
.border(.red)
}
.overlay(alignment: isInFirst ? .top : .bottom) {
ItemView().padding()
}
Button("Move Item") {
withAnimation {
isInFirst.toggle()
}
}
}
}
注意事项:
matchedGeometryEffect
根本不需要rotation3DEffect
应用于移动视图(正如您所描述的),这也会平滑地进行动画处理。