IOS开发之SwiftUI学习笔记(四)

Animations and States

终于来到了SwiftUI的重头戏了,这篇主要内容是动画和状态,重中之重是状态。
视频教程地址:教程链接
SwiftUI整体上是一个MVVM的框架了,有别于微软的MVVM框架,Android的LiveData,LifeCycle,DataBinding,Flutter的Stateful这一套概念,SwiftUI的MVVM更加的简便,封装程度更高,类似于web上的vue搞的这一套。
微软基于.net平台的MVVM框架有prism,MVVNLight等,他们在属性改变的时候都需要手动编写代码来通知界面刷新。
Android这一套MVVM框架简直堪称反面教材,这一套东西看下来能把人绕晕。
Flutter的状态机制,要首先写一个类继承StatefulWidget,然后再写一个类继承State的泛型类,总之也是挺麻烦的。
而SwiftUI只需要在需要改变的状态上加一个@State的注解,就能在状态改变的时候立即刷新界面,相当易用。

状态的使用

这个地方我们需要创建一个效果,就是点击卡片之后,旋转角度变成0,再点一下,则恢复旋转。
按照以前的MVC的做法,给卡片添加一个点击事件监听器,在监听器中判断是否旋转卡片,然后用旋转方法,来改变卡片的角度。
现在MVVM框架的做法是,设置一个状态,这个状态在View层面跟卡片的旋转角度绑定,然后在后台又跟点击事件关联在一起,点击的时候,状态发生改变,就会自动通知卡片的旋转角度发生变化。

首先在ContentView结构体内创建一个全局布尔类型变量show,并且在前面用 @State注解 修饰:

struct ContentView: View {
    @State var show = false
    
    var body: some View {
        ...
    }
}

这样就创建了一个是否展示卡片的状态。
然后给CardView卡片写一个点击事件的回调,这里用到onTapGesture这个modifier:

            CardView()
                .blendMode(.hardLight)
                .onTapGesture {
                    show.toggle()
                }

toggle这个函数的作用就是切换真值,即true变false,false变true。
在XCode11中,show.toggle() 这句代码必须前面加上范围限定self.才能正常编译。到了XCode12中,已经不需要再这样操作。
这一步就是上面说到的把状态和点击事件关联起来,接下来要做的就是把状态和旋转角度绑定,这一步也很简单,只需要一个 三目运算符

BackCardView()
                .background(Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: -20)
                .scaleEffect(0.95)
                .rotationEffect(.degrees(show ? 0 : 5))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)

show ? 0 : 5 这一个三目表达式,就将状态和旋转角度绑定了起来。
点击预览界面上的三角形,点击一下可以看到效果:
在这里插入图片描述
同理,将这个state和卡片的偏移值(offset) 绑定起来,则状态改变时,会同时影响旋转角度和偏移值:

BackCardView()
                .background(Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -200 : -20)
                .scaleEffect(0.95)
                .rotationEffect(.degrees(show ? 0 : 5))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)

效果是这样的:
在这里插入图片描述

动画的使用

上面状态刷新的时候,界面变化挺突兀的,有一些渐变的动画是最好不过的,这个时候要用到动画(animation):

            BackCardView()
                .background(Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -200 : -20)
                .scaleEffect(0.95)
                .rotationEffect(.degrees(show ? 0 : 5))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)
                .animation(.easeInOut(duration: 0.3))

这里的easeInOut是指定了动画的一个interpolator(插值器),影响的是动画的快慢节奏,类似于Android动画里面的插值器,有线性插值器,先快后慢插值器,先慢后快插值器,等等。这里的easeInOut指的是一种开头和结束都比较缓慢,中间比较快的插值器。
效果是这样的:
在这里插入图片描述
当然这里的duration只用了0.3秒,看不太出来速率的变化,如果把时间拉长,速率的变化就会比较明显了。
同理,把这张卡片上用到的动画全部应用于另外一张背景卡片:

BackCardView()
                .background(Color("card4"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -400 : -40)
                .scaleEffect(0.9)
                .rotationEffect(.degrees(show ? 0 : 10))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)
                .animation(.easeInOut(duration: 0.5))
            BackCardView()
                .background(Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -200 : -20)
                .scaleEffect(0.95)
                .rotationEffect(.degrees(show ? 0 : 5))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)
                .animation(.easeInOut(duration: 0.5))

这里把动画时长改成了0.5s,效果:
在这里插入图片描述
同理,状态也可以绑定颜色,状态改变并应用动画时,颜色会在两种颜色之间变化:

            BackCardView()
                .background(show ? Color("card4") : Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -200 : -20)
                .scaleEffect(0.95)
                .rotationEffect(.degrees(show ? 0 : 5))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)
                .animation(.easeInOut(duration: 0.5))

效果:
在这里插入图片描述
同样把颜色的动画应用于另外一张卡片:

            BackCardView()
                .background(show ? Color("card3") : Color("card4"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -400 : -40)
                .scaleEffect(0.9)
                .rotationEffect(.degrees(show ? 0 : 10))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)
                .animation(.easeInOut(duration: 0.5))
            BackCardView()
                .background(show ? Color("card4") : Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: show ? -200 : -20)
                .scaleEffect(0.95)
                .rotationEffect(.degrees(show ? 0 : 5))
                .rotation3DEffect(
                    .degrees(5),
                    axis: (x: 10, y: 1.0, z: 0.0)
                )
                .blendMode(.hardLight)
                .animation(.easeInOut(duration: 0.5))

效果:
在这里插入图片描述
当然上一篇中最后用到的高斯模糊(blur) 也可以用同样的手段加上动画:

            TitleView()
                .blur(radius: show ? 20 : 0)
                .animation(.easeInOut(duration: 0.3))
            BottomCardView()
                .blur(radius: show ? 20 : 0)
                .animation(.easeInOut(duration: 0.3))

效果:
在这里插入图片描述
下一篇是手势动作,和事件。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值