SwiftUI 初体验 5 动画转场

mac2022-06-30  24

首先来一个简单的按钮交互效果:点击按钮,按钮旋转放大,再次点击,回复原状。

struct AnimationView: View { @State private var animate = false var body: some View { VStack { HStack { Spacer() Text("动画测试") Button(action: { withAnimation { self.animate.toggle() } }) { Image(systemName: "chevron.right.circle") .frame(width: 44, height: 44, alignment: .center) .rotationEffect(.degrees(animate ? 90 : 0)) .scaleEffect(animate ? 1.5 : 1) } Spacer() } Spacer() } } }

可以看出比OC和Swift下简洁很多,只需要在Button的action里面指定State属性的切换是withAnimation的,并且在button的内容中关联State属性即可由SwiftUI完成相关的动画逻辑。

动画比较重要的内容有animation和transition,一个是常见的图形动画像形变一类的,一个是转场动画,适用于进场退场的。官方的demo动画定义的结构体没那么小白,这里做一个柱状数据的动画变化来供参考。先定义一个柱状的类:

struct DataColumn: View { var color: Color var height: CGFloat var body: some View { VStack { Spacer() RoundedRectangle(cornerRadius: 2) .fill(color) .frame(width: 4, height: height, alignment: .bottom) } } }

因为重点是animation所以不要介意数字的hard-coding,然后用一个列表把数据组织起来:

struct DataList: View { @State var floatData: [CGFloat] var body: some View { VStack { HStack { ForEach(floatData.indices) { i in DataColumn(color: .blue, height: self.floatData[i]) .animation( Animation .spring(dampingFraction: 0.8) .speed(2) .delay(0.03 * Double(i))) } } .clipped() .frame(width: 400, height: 300, alignment: .bottom) HStack { Button(action: { withAnimation { self.floatData = { () -> [CGFloat] in var list = [CGFloat]() for _ in 0 ..< 30 { list.append(CGFloat.random(in: Range<CGFloat>.init(uncheckedBounds: (0, 300)))) } return list }() } }) { Text("修改数据") } } .offset(x: 0, y: 50) Spacer() } } }

按钮绑定State属性用withAnimation包裹绑定视图和动态数据这个已经很熟悉了。clipped函数可以对图层的边缘进行裁剪,ForEach是SwiftUI中特有的控制流,如果想使用下标,可以ForEach一个集合的indices。这里使用下标的原因是为了给多个柱状形状提供递增的动画延时,让一连串的形状变化有线性规律。 然后就可以组织效果了:

struct AnimationView: View { @State private var animate = false var transition: AnyTransition { let insertion = AnyTransition.move(edge: .trailing) .combined(with: .opacity) let removal = AnyTransition.scale .combined(with: .opacity) return .asymmetric(insertion: insertion, removal: removal) } var body: some View { VStack { HStack { Spacer() Text("动画测试") Button(action: { withAnimation { self.animate.toggle() } }) { Image(systemName: "chevron.right.circle") .frame(width: 44, height: 44, alignment: .center) .rotationEffect(.degrees(animate ? 90 : 0)) .scaleEffect(animate ? 1.5 : 1) } Spacer() } if animate { DataList(floatData: { () -> [CGFloat] in var list = [CGFloat]() for _ in 0 ..< 30 { list.append(CGFloat.random(in: Range<CGFloat>.init(uncheckedBounds: (0, 300)))) } return list }()) .transition(transition) } Spacer() } } }

同样State属性绑定动态数据,button action控制状态切换,transition转场的封装可以定义更详细的效果,animation的modifier可以修改多种形变属性如rotation、scale等,这些可以变化的属性在动画执行的时候都可以被SwiftUI识别并执行,只需要把它们跟动态的属性绑定起来就可以。这里用到的转场效果是进场从右边缘进入,离场是变小消失。动画效果是spring带有阻尼弹性,从第一个柱状到最后一个柱状逐个变化成新的随机值。按钮的点击动画是放大旋转效果。

最新回复(0)