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
在视图切换时实现平滑的共享元素动画:
-
@State var show = false
:这是一个布尔类型的状态属性,用于控制视图的切换。初始值为false
,表示初始状态下不显示下方的视图。 -
@Namespace var namespace
:这是一个命名空间,它用于关联不同状态下的共享元素,以便在切换时执行平滑的共享元素动画。 -
if !show { ... }
:这是一个条件语句,根据show
属性的值来决定显示哪个子视图。如果show
为false
,则显示下方的ScrollView
子视图,否则显示上方的VStack
子视图。 -
ScrollView
:这是一个滚动视图,它包含了一个HStack
,其中包含了标题和形状视图。 -
VStack
:这是一个垂直排列的容器,它包含了标题的大视图。 -
Text("Title")
:显示标题文本。 -
.foregroundColor(.white)
:设置文本的颜色为白色。 -
.matchedGeometryEffect(id: "title", in: namespace)
:这是一个共享元素效果,用于关联标题文本。id
参数指定了元素的唯一标识符,而namespace
参数指定了命名空间。 -
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 用于创建共享元素动画的一个典型示例。
网友评论