让我们在SwiftUI
上制作带有手势支持的动画卡:
我想添加一个详细的预览,但是gif的大小不是Orthodox。 可以在链接或视频教程中查看大预览。
将被要求
SwiftUI
现在处于beta版本,并随新的Xcode一起安装,后者也处于beta版本。 好消息是,新的Xcode可以放在旧的Xcode旁边,您几乎不会感到任何痛苦。

您可以从“ Applications
部分中的链接下载它。
使用SwiftUI
时,您可能已经看到了实时预览。 要激活它以及一些上下文菜单,您需要安装beta macOS Catalina
。 它不会没有痛苦。 我没有打赌,所以我将以老式的方式运行模拟器。
新项目
创建带有附加复选标记的Single View Application
SwiftUI
:
转到ContentView.swift文件。 PreviewProvider
的继承人负责预览。 由于我们将不使用它,因此将保留最少的必要代码:
import SwiftUI struct ContentView: View { var body: some View { } }
我希望已经对SwiftUI
有了一个共识,我们不会SwiftUI
琐碎的SwiftUI
。
卡片
卡是一张一张地排列的,因此我们将使用ZStack
。 让我提醒您,还有两个用于分组元素的选项:水平的VStack
和垂直的VStack
。 为了清楚起见:

添加第一张卡:
struct ContentView: View { var body: some View { return ZStack { Rectangle() .fill(Color.black) .frame(height: 230) .cornerRadius(10) .padding(16) } } }
在这里,我们添加了一个矩形,涂成黑色,高度为230pt
,将边缘四舍五入为10pt
,并将所有边距设置为16pt
。
卡中的文本在矩形之后添加到ZStack
块中:
Text("Main Card") .color(.white) .font(.title) .bold()
运行项目以查看中间结果:
但是有三个!

为了方便起见,我们将MainCard
代码:
struct MainCard: View { var title: String var body: some View { ZStack { Rectangle() .fill(Color.black) .frame(height: 230) .cornerRadius(10) .padding(16) Text(title) .color(.white) .font(.largeTitle) .bold() } } }
title
将出现在初始化器中。 此文字将显示在卡片上。 将卡添加到ContentView
,同时我们将在初始化程序中看到一个新参数:
struct ContentView: View { var body: some View { return MainCard(title: "Main Card") } }
我们已经知道如何编写代码,因此立即为背景卡定义一个类:
struct Card: View { var title: String var body: some View { ZStack { Rectangle() .fill(Color(red: 68 / 255, green: 41 / 255, blue: 182 / 255)) .frame(height: 230) .cornerRadius(10) .padding(16) Text(title) .color(.white) .font(.title) .bold() } } }
为文本设置不同的颜色和样式。 其余代码重复黑色主MainCard
。 将两个背景卡添加到ContentView
。 卡是一张一张地排列的,因此我们将它们放置在ZStack
。 ContentView
代码:
struct ContentView: View { var body: some View { return ZStack { Card(title: "Third card") Card(title: "Second Card") MainCard(title: "Main Card") } } }
背景卡位于黑色下方,至今尚不可见。 从边缘添加向上偏移和填充:
Card(title: "Third card") .blendMode(.hardLight) .padding(64) .padding(.bottom, 64) Card(title: "Second Card") .blendMode(.hardLight) .padding(32) .padding(.bottom, 32) MainCard(title: "Main Card")
现在,结果类似于本教程开始时的声明:
让我们继续进行手势,并同时进行动画。
手势
手势的实施方式将迫使我减少业力并留下有害的评论。

在查看代码之前,请注意-作为示例,它在developer.apple.com上列出。 第一印象是欺骗性的,实际上我喜欢它。
在ContentView
声明枚举:
enum DragState { case inactive case dragging(translation: CGSize) var translation: CGSize { switch self { case .inactive: return .zero case .dragging(let translation): return translation } } var isActive: Bool { switch self { case .inactive: return false case .dragging: return true } } }
DragState
将使手势工作更加舒适。 将dragState dragState
添加到ContentView
:
@GestureState var dragState = DragState.inactive
会有魔术。

无论在何处使用dragState
,新值都将自动应用。 声明一个手势:
let dragGester = DragGesture() .updating($dragState) { (value, state, transaction) in state = .dragging(translation: value.translation) }
向主卡添加手势并设置offset
:
MainCard(title: "Main Card") .offset( x: dragState.translation.width, y: dragState.translation.height ) .gesture(dragGester)
偏移量是否等于访问参数时的值? 不,它将永远相等 。 这是魔术。
卡将跟随 用手指 用鼠标但没有动画:
背景卡还应该更改其位置( 至少我们需要这样做 )。 为他们添加与手势状态相关的代码。 rotation3DEffect
将旋转卡,直到手势变为活动状态:
Card(title: "Third card") .rotation3DEffect(Angle(degrees: dragState.isActive ? 0 : 60), axis: (x: 10.0, y: 10.0, z: 10.0)) .blendMode(.hardLight) .padding(dragState.isActive ? 32 : 64) .padding(.bottom, dragState.isActive ? 32 : 64) Card(title: "Second Card") .rotation3DEffect(Angle(degrees: dragState.isActive ? 0 : 30), axis: (x: 10.0, y: 10.0, z: 10.0)) .blendMode(.hardLight) .padding(dragState.isActive ? 16 : 32) .padding(.bottom, dragState.isActive ? 0 : 32)
您还可以如何使用3D呢? 我还添加了blendMode
。 这些模式类似于Photoshop和Sketch中的工具。
尽管更改未应用动画,但让我们对其进行修复。
动画制作
您会惊讶它是如此简单。 只需添加一行:
.animation(.spring())
为每张卡添加。 现在,所有更改都将以动画方式应用,在我们的示例中,这些是缩进大小,3D旋转和offset
。 如果需要带有曲线的动画,请使用basic
模式。
装饰物
添加与偏移量相关的主视图的阴影和旋转:
.rotationEffect(Angle(degrees: Double(dragState.translation.width / 10))) .shadow(radius: dragState.isActive ? 8 : 0)
如果您运行该项目,您将在本教程的开头看到参考。 打开预览不可见,将卡拉到一边以查看效果。
对于寻求者
→可在存储库中找到项目代码
将文件复制到项目就足够了,不需要其他设置。 不要担心,很少的代码是SwiftUI
。
如果您更方便观看视频,请看一下教程。 顺便说一句,在视频中,我使用了实时预览。