如何使用 matchedGeometryEffect() 将一个视图的动画同步到另一个视图

温馨提示:本文最后更新于2025-01-14 10:22:08,某些文章具有时效性,若有错误或已失效,请在下方留言

如果你在视图层次结构的两个不同部分中有相同的视图,并且希望在这两者之间进行动画过渡——例如,从列表视图切换到放大的详细视图——那么你应该使用 SwiftUI 的 matchedGeometryEffect() 修饰符,这有点像 Keynote 中的 Magic Move

要使用修饰符,将其附加到层次结构中不同部分的两个相同视图上。完成后,当您在两个视图状态之间切换时,会发现SwiftUI平滑地动画您的同步视图。

要尝试它,首先创建一个布局,使你的视图出现在两个位置。在这个例子中,我有一个红色的圆,然后是一些文本在一个视图状态中,但在另一个视图状态中,圆圈出现在文本之后并且颜色发生变化:

struct ContentView: View {
    @State private var isFlipped = false
    var body: some View {
        VStack {
            if isFlipped {
                Circle()
                    .fill(.red)
                    .frame(width: 44, height: 44)
                Text("Taylor Swift - 1989")
                    .font(.headline)
            } else {
                Text("Taylor Swift - 1989")
                    .font(.headline)
                   
                Circle()
                    .fill(.blue)
                    .frame(width: 44, height: 44)
            }
        }
        .onTapGesture {
            withAnimation {
                isFlipped.toggle()
            }
        }
    }
}

如果你运行那段代码,你会看到 SwiftUI 会通过淡入和淡出视图来创建一个过渡效果——这没问题,但我们可以让它更好。

图片[1]-如何使用 matchedGeometryEffect() 将一个视图的动画同步到另一个视图-Stewed Noodles 资源

首先,你需要使用@Namespace属性包装器为你的视图创建一个全局命名空间。实际上,这只不过是视图上的一个属性,但在幕后,这使得我们可以将视图连接在一起。

所以,你可能会添加一个类似的属性:@Namespace private var animation

接下来,你需要将.matchedGeometryEffect(id: YourIdentifierHere, in: animation) 添加到所有你想用同步效果进行动画处理的视图中。YourIdentifierHere 部分应替换为每个配对部分共享的唯一编号。

在我们的示例中,我们可以将其用于圆圈:

.matchedGeometryEffect(id: "Shape", in: animation)

并将其用于文本:

.matchedGeometryEffect(id: "AlbumTitle", in: animation)

就是这样 – 当您再次运行示例时,您将看到两个视图平稳地移动。

以下是最终代码的样子:

struct ContentView: View {
    @Namespace private var animation
    @State private var isFlipped = false
    
    var body: some View {
        VStack {
            if isFlipped {
                Circle()
                    .fill(.red)
                    .frame(width: 44, height: 44)
                    .matchedGeometryEffect(id: "Shape", in: animation)
                Text("Taylor Swift - 1989")
                    .font(.headline)
                    .matchedGeometryEffect(id: "AlbumTitle", in: animation)
            } else {
                Text("Taylor Swift - 1989")
                    .font(.headline)
                    .matchedGeometryEffect(id: "AlbumTitle", in: animation)
                   
                Circle()
                    .fill(.blue)
                    .frame(width: 44, height: 44)
                    .matchedGeometryEffect(id: "Shape", in: animation)
            }
        }
        .onTapGesture {
            withAnimation {
                isFlipped.toggle()
            }
        }
    }
}

运行的效果

图片[2]-如何使用 matchedGeometryEffect() 将一个视图的动画同步到另一个视图-Stewed Noodles 资源

有关更高级的示例,请尝试此操作 – 它借用了 Apple Music 的专辑显示样式,在点击时将小视图扩展到更大的视图。在此示例中,只有文本是动画的,因为它正在更改位置:

struct ContentView: View {
    @Namespace private var animation
    @State private var isZoomed = false
    
    var frame: Double {
        isZoomed ? 300 : 44
    }
    
    var body: some View {
        VStack {
            Spacer()
            
            VStack {
                HStack {
                    RoundedRectangle(cornerRadius: 10)
                        .fill(.blue)
                        .frame(width: frame, height: frame)
                        .padding(.top, isZoomed ? 20 : 0)
                    
                    if isZoomed == false {
                        Text("Taylor Swift - 1989")
                            .matchedGeometryEffect(id: "AlbumTitle", in: animation)
                            .font(.headline)
                        
                        Spacer()
                    }
                }
                
                if isZoomed == true {
                    Text("Taylor Swift - 1989")
                        .matchedGeometryEffect(id: "AlbumTitle", in: animation)
                        .font(.headline)
                        .padding(.bottom, 60)
                    
                    Spacer()
                }
            }
            .onTapGesture {
                withAnimation {
                    isZoomed.toggle()
                }
            }
            .padding()
            .frame(maxWidth: .infinity)
            .frame(height: 400)
            .background(Color(white: 0.9))
            .foregroundStyle(.black)
        }
    }
}

运行效果

图片[3]-如何使用 matchedGeometryEffect() 将一个视图的动画同步到另一个视图-Stewed Noodles 资源
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容