译自 Showing and hiding views with transitions
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀
SwiftUI 一个最强大的特性是可以定制视图的显示和隐藏。之前你已经见过我们如何使用常规的 if 条件来实现条件化视图,也就是我们可以根据条件变化在视图体系中插入或者移除视图。
过渡控制这些插入和移除如何发生。我们可以使用内建的过渡,以各种方式组合它们,甚至完全自定义过渡。
下面用一个带按钮和矩形块的 VStack 来演示:
struct ContentView: View { var body: some View { VStack { Button("Tap Me") { // do nothing } Rectangle() .fill(Color.red) .frame(width: 200, height: 200) } } }复制代码
我们可以让矩形块在某个条件满足时才出现。首先,添加一个状态:
@State private var isShowingRed = false复制代码
然后用状态作为条件来显示矩形块:
if isShowingRed { Rectangle() .fill(Color.red) .frame(width: 200, height: 200) }复制代码
最后,在按钮的 action 中触发 isShowingRed 在 true 和 false 间转换:
self.isShowingRed.toggle()复制代码
运行程序,你会看到点击按钮会显示或者隐藏一个红色矩形块。但没有动画,它只是突然地出现或者消失。
我们可以用 SwiftUI 的默认视图过渡来包装状态变化过程,像这样:
withAnimation { self.isShowingRed.toggle() }复制代码
通过这个小改变,应用现在可以渐显或者渐隐矩形块,同时将按钮上移或者下移来调整空间。看起来还不错,但我们可以利用 transition() modifier 做到更好。
举个例子,我们可以让矩形块以缩放的方式来出场和离场,添加 transition() modifier 给矩形块:
Rectangle() .fill(Color.red) .frame(width: 200, height: 200) .transition(.scale)复制代码
现在点击按钮,看起来更好了:矩形块随着按钮给它腾地方的时候变大,再点击按钮,矩形块又以缩小的方式消失。
如果你想实验的话,有许多别的过渡可以尝试。其中一个很有用的过渡是 .asymmetric ,它可以让我们用一个过渡配置视图显示,另一个过渡配置视图隐藏。尽管尝试一下,把矩形块的退出过渡换成下面的:
.transition(.asymmetric(insertion: .scale, removal: .opacity))复制代码
译自 Building custom transitions using ViewModifier
用 SwiftUI 创建一个全新的过渡,是可能的,实际上非常容易。自定义过渡允许我们用完全自定义的动画来添加或者移除视图。
这个功能是通过 .modifier 过渡来实现的,它可以接收任意视图 modifier 。要点在于我们需要实例化这个 modifier ,也就是自行创建它们。
让我们动手尝试。写一个 view modifier ,实现一个类似 Keynote 里最简单的 Pivot 动画 —— 让一个新的幻灯片以旋转的方式从左上角进入画面 。用 SwiftUI 的术语来说,就是创建一个 view modifier,让我们的视图旋转着从某一个角进入屏幕,但不逃出它本该有的边界。 SwiftUI 直接提供了这样的 modifier :rotationEffect() ,这个 modifier 让我们在 2D 空间中旋转视图,而 clipped() 可以阻止超出视图区域的部分被绘制。
rotationEffect() 和 rotation3DEffect() 相似,除了它只围绕 Z 轴旋转。不过,它还提供了控制旋转锚点的能力 —— 锚点就是视图中作为旋转中心固定的部分。对此,SwiftUI 提供了 UnitPoint 类型,用于设置锚点,即可以指定精确的 X,Y 坐标,也可以用许多内置的选项 —— .topLeading,.bottomTrailing,.center 等等。
让我们创建一个 CornerRotateModifier 结构体,把上面介绍的知识点都放进去演示。这个 modifier 用一个锚点来控制旋转中心,并且可以设置旋转角度:
struct CornerRotateModifier: ViewModifier { let amount: Double let anchor: UnitPoint func body(content: Content) -> some View { content.rotationEffect(.degrees(amount), anchor: anchor).clipped() } }复制代码
额外的 clipped() 表示当视图旋转时,超出它本来的矩形区域的部分不做绘制。
我们可以利用 .modifier 过渡直接尝试,但那样的方式不常用。更好的方式通过扩展 AnyTransition 来使用。下面的扩展实现以左上角为锚点,从 -90 度旋转到 0 度的过渡。
extension AnyTransition { static var pivot: AnyTransition { .modifier( active: CornerRotateModifier(amount: -90, anchor: .topLeading), identity: CornerRotateModifier(amount: 0, anchor: .topLeading) ) } }复制代码
上面的代码就绪后,我们可以用下面的代码附加这个过渡到任意视图上:
.transition(.pivot)复制代码
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~