SwiftUI 如何使用 ScrollView 和 GeometryReader 创建3D效果(如Cover Flow)?

如果将 GeometryReader 与任何可以改变位置的视图(例如具有拖动手势或位于 List 内的视图)结合使用,我们可以创建在屏幕上看起来很棒的3D效果。GeometryReader 允许我们读取视图的坐标,并将这些值直接输入到 rotation3DEffect() 修饰符中。

例如,我们可以通过在滚动视图中水平堆叠许多文本视图,然后应用 rotation3DEffect() 来创建 Cover Flow 样式的滚动效果,这样当它们在滚动视图中移动时,它们会缓慢地旋转,如下所示:

ScrollView(.horizontal, showsIndicators: false) {
    HStack(spacing: 0) {
        ForEach(1..<20) { num in
            VStack {
                GeometryReader { geo in
                    Text("Number \(num)")
                        .font(.largeTitle)
                        .padding()
                        .background(Color.red)
                        .rotation3DEffect(.degrees(-Double(geo.frame(in: .global).minX) / 8), axis: (x: 0, y: 1, z: 0))
                        .frame(width: 200, height: 200)
                }
                .frame(width: 200, height: 200)
            }
        }
    }
}

您不必总是使用 GeometryReader 来获得有趣的效果,例如,可以使用 DragGesture() 进行类似的处理。 因此,此代码创建了一个可以在 XY 轴上拖动的类似于卡片的矩形,并使用了两个 rotation3DEffect() 修饰符从该拖动中应用值:

struct ContentView: View {
    @State var dragAmount = CGSize.zero

    var body: some View {
        VStack {
            Rectangle()
                .fill(LinearGradient(gradient: Gradient(colors: [.yellow, .red]), startPoint: .topLeading, endPoint: .bottomTrailing))
                .frame(width: 200, height: 150)
                .clipShape(RoundedRectangle(cornerRadius: 20))
                .rotation3DEffect(.degrees(-Double(dragAmount.width) / 20), axis: (x: 0, y: 1, z: 0))
                .rotation3DEffect(.degrees(Double(dragAmount.height / 20)), axis: (x: 1, y: 0, z: 0))
                .offset(dragAmount)
                .gesture(
                    DragGesture()
                        .onChanged { dragAmount = $0.translation }
                        .onEnded { _ in
                            withAnimation(.spring()) {
                                dragAmount = .zero
                            }
                        }
                )
        }
        .frame(width: 400, height: 400)
    }
}

拖动卡片时,会看到它旋转以产生透视效果。