美文网首页
Drag Gesture & Matched Geometry

Drag Gesture & Matched Geometry

作者: _浅墨_ | 来源:发表于2023-10-31 19:15 被阅读0次

    Drag Gesture

    struct ContentView: View {
        @State var viewState = CGSize.zero
    
        var body: some View {
            RoundedRectangle(cornerRadius: 30)
                .fill(Color.blue)
                .frame(width: 300, height: 400)
                .offset(x: viewState.width, y: viewState.height)
                .gesture(
                    DragGesture().onChanged { value in
                        viewState = value.translation
                    }
                    .onEnded { value in
                        withAnimation(.spring()) {
                            viewState = .zero
                        }
                    }
                )
        }
    }
    

    .gesture(...):这是一个手势修饰符,添加手势识别器以响应用户的交互。

    DragGesture().onChanged { value in ... }:这是一个拖动手势(DragGesture),当用户拖动视图时,onChanged闭包将被调用。在这里,我们捕获手势的translation属性,它表示拖动的位移,并将这个位移赋给viewState,以便移动矩形。

    .onEnded { value in ... }:这是拖动手势结束时的处理闭包。在这里,我们使用withAnimation来添加动画效果,将viewState重置为.zero,这会导致矩形平滑地返回原始位置。

    以上这段代码创建了一个蓝色圆角矩形,用户可以通过拖动手势来移动它。当用户停止拖动时,矩形会以弹簧动画效果返回原始位置。这是一个简单的示例,演示了如何使用SwiftUI创建可交互的视图。

    Matched Geometry Effect

    @State var show = false
    @Namespace var namespace
    
    var body: some View {
        ZStack {
            if !show {
                ScrollView {
                    HStack {
                        VStack {
                            Text("Title").foregroundColor(.white)
                                .matchedGeometryEffect(id: "title", in: namespace)
                        }
                        .frame(width: 100, height: 100)
                        .background(
                            Rectangle().matchedGeometryEffect(id: "shape", in: namespace)
                        )
                        Rectangle()
                            .frame(width: 100, height: 100)
                        Spacer()
                    }
                    .padding(8)
                }
            } else {
                VStack {
                    Text("Title").foregroundColor(.white)
                        .matchedGeometryEffect(id: "title", in: namespace)
                        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                        .padding()
                }
                .frame(maxWidth: .infinity, maxHeight: 400)
                .background(
                    Rectangle().matchedGeometryEffect(id: "shape", in: namespace)
                )
            }
        }
        .onTapGesture {
            withAnimation(.spring(response: 0.5, dampingFraction: 0.7)) {
                show.toggle()
            }
        }
    }
    

    这段代码演示了如何使用 matchedGeometryEffect 在视图切换时实现平滑的共享元素动画:

    1. @State var show = false:这是一个布尔类型的状态属性,用于控制视图的切换。初始值为false,表示初始状态下不显示下方的视图。

    2. @Namespace var namespace:这是一个命名空间,它用于关联不同状态下的共享元素,以便在切换时执行平滑的共享元素动画。

    3. if !show { ... }:这是一个条件语句,根据 show 属性的值来决定显示哪个子视图。如果 showfalse,则显示下方的 ScrollView 子视图,否则显示上方的 VStack 子视图。

    4. ScrollView:这是一个滚动视图,它包含了一个 HStack,其中包含了标题和形状视图。

    5. VStack:这是一个垂直排列的容器,它包含了标题的大视图。

    6. Text("Title"):显示标题文本。

    7. .foregroundColor(.white):设置文本的颜色为白色。

    8. .matchedGeometryEffect(id: "title", in: namespace):这是一个共享元素效果,用于关联标题文本。id 参数指定了元素的唯一标识符,而 namespace 参数指定了命名空间。

    9. withAnimation(...):这是一个动画块,它用于包装状态属性的更改。在这里,使用 .spring 动画效果来实现平滑的过渡。

    整体上,这段代码创建了一个视图,用户可以点击以切换两种状态,其中标题和形状的共享元素通过 matchedGeometryEffect 实现平滑的切换动画。在切换时,标题文本和形状会在两个不同状态之间进行平滑的共享元素过渡。

    Advanced Matched Geometry Effect

    // ContentView.swift
    struct ContentView: View {
        @State var show = false
        @Namespace var namespace
    
        var body: some View {
            ZStack {
                if !show {
                    VStack {
                        HStack(spacing: 16) {
                            RoundedRectangle(cornerRadius: 10)
                                .matchedGeometryEffect(id: "shape", in: namespace)
                                .frame(width: 44)
                            Text("Fever")
                                .matchedGeometryEffect(id: "title", in: namespace)
                            Spacer()
                            Image(systemName: "play.fill")
                                .matchedGeometryEffect(id: "play", in: namespace)
                                .font(.title)
                            Image(systemName: "forward.fill")
                                .matchedGeometryEffect(id: "forward", in: namespace)
                                .font(.title)
                        }
                    }
                    .frame(maxWidth: .infinity, maxHeight: 44)
                    .padding(8)
                    .background(
                        RoundedRectangle(cornerRadius: 0)
                            .fill(Color.blue)
                            .matchedGeometryEffect(id: "background", in: namespace)
                    )
                    .frame(maxHeight: .infinity, alignment: .bottom)
                } else {
                    PlayView()
                }
            }
            .foregroundColor(.white)
            .onTapGesture {
                withAnimation(.spring(response: 0.7, dampingFraction: 0.8)) {
                    show.toggle()
                }
            }
        }
    }
    
    // PlayView.swift
    struct PlayView: View {
        var namespace: Namespace.ID
    
        var body: some View {
            VStack(spacing: 20) {
                RoundedRectangle(cornerRadius: 30)
                    .matchedGeometryEffect(id: "shape", in: namespace)
                    .frame(height: 300)
                    .padding()
                Text("Fever")
                    .matchedGeometryEffect(id: "title", in: namespace)
                HStack {
                    Image(systemName: "play.fill")
                        .matchedGeometryEffect(id: "play", in: namespace)
                        .font(.title)
                    Image(systemName: "forward.fill")
                        .matchedGeometryEffect(id: "forward", in: namespace)
                        .font(.title)
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(
                RoundedRectangle(cornerRadius: 30)
                    .fill(Color.blue)
                    .matchedGeometryEffect(id: "background", in: namespace)
            )
            .ignoresSafeArea()
        }
    }
    
    struct PlayView_Previews: PreviewProvider {
        @Namespace static var namespace
    
        static var previews: some View {
            PlayView(namespace: namespace)
        }
    }
    

    matchedGeometryEffect

    matchedGeometryEffect 是 SwiftUI 中的一个功能,用于在不同视图之间创建共享元素的平滑过渡动画。它能够在两个不同的视图之间共享相同的唯一标识符,并在这两个视图之间的状态变化时,创建平滑的过渡效果。下面是对 matchedGeometryEffect 的详细解释和示例用法:

    matchedGeometryEffect(id:in:) 方法有两个参数:

    id:这是一个字符串,用于标识要共享的元素。两个视图之间的 id 必须相同,以确保它们共享相同的元素。

    in:这是一个 Namespace 对象,用于指定元素的命名空间。不同的命名空间允许在不同的上下文中定义相同的 id,以避免冲突。

    使用示例:

    假设您有两个视图,一个在初始状态下是一个小矩形,另一个在切换后是一个大矩形,您可以使用 matchedGeometryEffect 来创建平滑的大小变化过渡效果。

    
    import SwiftUI
    
    struct ContentView: View {
        @State private var isExpanded = false
        @Namespace private var namespace
    
        var body: some View {
            VStack {
                if isExpanded {
                    Rectangle()
                        .fill(Color.blue)
                        .frame(width: 200, height: 200)
                        .cornerRadius(10)
                        .matchedGeometryEffect(id: "shape", in: namespace)
                } else {
                    Rectangle()
                        .fill(Color.blue)
                        .frame(width: 100, height: 100)
                        .cornerRadius(10)
                        .matchedGeometryEffect(id: "shape", in: namespace)
                }
    
                Button("Toggle Size") {
                    withAnimation {
                        isExpanded.toggle()
                    }
                }
            }
        }
    }
    
    @main
    struct YourApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    

    在上面的示例中,matchedGeometryEffect 用于将两个不同状态下的矩形视图连接起来,其中 id 参数设置为 "shape",并且两个状态下的视图都使用相同的 namespace。当点击按钮以切换大小时,会触发动画,同时两个视图之间的大小过渡将平滑进行。这是 matchedGeometryEffect 用于创建共享元素动画的一个典型示例。

    相关文章

      网友评论

          本文标题:Drag Gesture & Matched Geometry

          本文链接:https://www.haomeiwen.com/subject/aeqwidtx.html