SwiftUI动画进阶,仿购票平台App行程目的地互换动效
项目背景
近几个月经常在国内出差,往常都是HR帮忙订好的机票和酒店,某天在打开某团搜索行程的时候发现地址切换动画蛮有意思。
当我们需要快捷切换出发地和目的地时,只需要点击中间的切换按钮,然后就会有一个出发地和目的地“文字对调”的动画,这很人性化,也很优雅。
本章中,我们就来使用MatchedGeometryEffect构建这样的过渡动画。
那么,让我们开始吧。
项目搭建
首先,创建一个新的SwiftUI
项目,命名为SwiftUIMGE
。
基础样式
先来看看基础样式部分,基础的结构我们可以看到有3个元素:起始地、切换图标、目的地。
三个元素横向排布,我们先来完成下基础的样式,示例:
HStack { Spacer() Text("广州") .font(.title) .fontWeight(.bold) .foregroundColor(.black) Spacer() Image(systemName: "paperplane.circle") .foregroundColor(.red) .font(.title) Spacer() Text("北京") .font(.title) .fontWeight(.bold) .foregroundColor(.black) Spacer() } .frame(maxWidth: .infinity) .padding()
上述代码中,我们使用HStack
横向布局视图,使用Spacer
将3个元素均衡分布。
基础动画
当我们点击中间的按钮时,需要对调出发地和目的地的位置,我们先声明一个变量表示当前的状态,示例:
@State var isSwitch: Bool = true
当isSwitch
处于不同状态时,我们展示不同的视图,同时在点击Image
图标时,切换isSwitch
的状态。
if isSwitch { HStack { Spacer() Text("广州") .font(.title) .fontWeight(.bold) .foregroundColor(.black) Spacer() Image(systemName: "paperplane.circle") .foregroundColor(.red) .font(.title) .onTapGesture { withAnimation(.linear) { self.isSwitch.toggle() } } Spacer() Text("北京") .font(.title) .fontWeight(.bold) .foregroundColor(.black) Spacer() } .frame(maxWidth: .infinity) .padding() } else { HStack { Spacer() Text("北京") .font(.title) .fontWeight(.bold) .foregroundColor(.black) Spacer() Image(systemName: "paperplane.circle") .foregroundColor(.red) .font(.title) .onTapGesture { withAnimation(.linear) { self.isSwitch.toggle() } } Spacer() Text("广州") .font(.title) .fontWeight(.bold) .foregroundColor(.black) Spacer() } .frame(maxWidth: .infinity) .padding() }
过渡动画
上述代码中,我们根据isSwitch
的状态切换两个视图,实现了基础的动画效果,但这不够优雅。
我们看到客户端的过渡动画是两个文字有一个优雅的“互调过渡”,我们先声明一个变量来存储交换位置的状态,示例:
@Namespace private var Transition
然后使用matchedGeometryEffect
修饰符进行位置变换,示例:
//广州的id为guangzhou Text("广州") .matchedGeometryEffect(id: "guangzhou", in: Transition) //北京的id为beijing Text("北京") .matchedGeometryEffect(id: "beijing", in: Transition)
我们给4个Text
都加上matchedGeometryEffect
修饰符,这里要区分ID
,过渡动画才能知道那两个视图的元素进行交换。
这里如果我们要看到最终的效果,我们需要运行模拟器,在真机环境下才能看到最终的交互效果。
项目预览
恭喜你,完成了本章的全部内容!