美文网首页
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