在上一节中,我们通过使用 .animate()
修饰符和修改 changed
变量来动态改变小球的「偏移量」,从而创造出动画效果。实际上,SwiftUI 提供了众多类似的数值型属性,它们可以被用来控制和自定义各种动态交互和视觉表现。本节将详细介绍一些常见且极具实用性的属性,以帮助开发者充分利用 SwiftUI 的强大功能,创造出更加丰富和流畅的用户体验。
偏移量(Offset)
怎么「理解」偏移量?
每个 SwiftUI 视图在其父视图中都有一个「原始位置」,这个位置是根据布局系统(如堆栈、网格、对齐等)决定的。
当应用一个偏移量到一个视图上时,相当于是在告诉 SwiftUI:在渲染这个视图时,应该在其「原始位置的基础上」,在屏幕上向「指定方向」移动「指定的距离」。例如:
1 | swift复制代码Text("Hello, World!") |
在这个例子中,无论文本的原始位置在哪里,都会在屏幕上向右移动 20 个单位,向下移动 50 个单位。这种移动是视觉上的,不影响文本在布局系统中的「逻辑」位置。
重要的是要理解,偏移量的应用不会改变视图在其父视图中的布局属性。也就是说,尽管视觉上文本被移动了,它仍然「占据」着原来的空间位置。这意味着其他视图的布局不会因为这个视图的偏移而受到影响。
理解了这点后,再使用偏移动画时,可以放心大胆的使用,因为无论当前视图怎么偏移都不会影响到其它的视图布局。
除了offset(x:y:)
的方法外,还有一种offset(_ offset:CGSize)
方法可实现位置偏移。
1 | swift复制代码 Circle() |
这两者在使用上是等价的,「区别」就是CGSize
可以作为一个单一的数据结构处理,而offset(x:y:)
更直观。
框架(Frame)
在 SwiftUI 中,frame
修饰符用来指定视图的「尺寸」和子视图的「对齐」。所以它在动画中的使用,也分为两种场景:
1. 设置尺寸
frame
最常见的用途是定义视图的「宽度」和「高度」。当你为视图设置一个 frame
,你可以指定宽度(width
)、高度(height
),或者两者都指定。如果不指定,视图将尽可能地适应其内容或填满可用空间,当然这取决于其父视图的布局特性。
如下例,通过改变高和宽能够实现一个放大和缩小的动画效果:
1 | swift复制代码 Rectangle() |
2. 对齐方式
frame
当用来设置容器视图的尺寸时,其中的子视图默认居中展示,一般子视图的尺寸会小于父容器的尺寸,所以改变子视图在其中的「对齐方式」可以间接实现视图的「移动」效果。
如下例所示:
1 | swift复制代码VStack { |
位置(Position)
position
指定当前视图「中心」的横纵坐标(x,y)时,是基于其所在的「父容器」进行计算的,如下例圆心的位置正位于父容器(红色矩形)的左上角(x:0,y:0)
与offset
类似,它同样可用于移动视图,而且移动视图后不会影响到其它「兄弟」视图的布局,如下例:
如果只需要移动当前视图,用offset
最为简单直观;如果需要依据父容器的大小或在父容器中的位置来判断移动带来的影响,那最好用position
,如下例:
整个浅蓝色背景是「父容器」,当将小球向下移动,如果纵坐标「超过 400」,则小球变为红色并放大 3 倍;否则当纵坐标小于 400,小球变为绿色并恢复原始大小。
GeometryReader
很多时候我们并不关注具体的坐标值,因为在不同的平台(iPhone、iPad 或 Mac)上,由于设备尺寸的不同,所以更需要一个相对的位置或坐标,比「居中」、「左下角」、「底部」等。
SwiftUI 提供了一个容器视图 GeometryReader,它的构造器参数 「GeometryProxy」 可以用来读取其父视图的「尺寸」和「位置」信息。这里面方法很多,不一一列举,只说一下与position
配合常用的两个方法:
size
用来获取父容器的尺寸,比如宽(width)或高(height)。frame
用来获取父容器的「边界」的坐标,比如minX
、midX
、maxY
等。
两者某种情况下是等价的,比如「宽」等价于「maxX」,「宽」的一半就是「midX」等等。
如下例所示,两种方法都可以获取到父容器中心位置的坐标,黑球和白球都位于父容器「ZStack」(橘色背景)的中心位置:
1 | swift复制代码 ZStack { |
利用这种方式,前例中当小球纵坐标大于「400」(预估的一个中间值),就可以使用「midY」来代替,这样在不同设备、不同方向(横屏或竖屏)都能完美兼容。
颜色(Color)
Color
在内部使用数值来定义颜色的各个组成部分,如红、绿、蓝和透明度(RGBA)或其他颜色模型。这些数值可以在动画中平滑地过渡,使得 Color
可以被动画化。
比如「红色」转变为「绿色」时,是穿插了一些列的「插值」,然后在一定「间隔」时间内,缓缓从起始切换到终止(起始 -> 插值 1 -> 插值 2 -> …插值n -> 终止)。
如下例,当点击「红色」时,背景由「红色」转变为「绿色」,产生一个渐变的动画效果:
1 | swift复制代码View |
应用颜色的修饰符,最常用的就是前景色(foregroundStyle)、背景色(background)、主题色(tint),如下例:
1 | swift复制代码Text("foregroundStyle") |
「前景色」、「背景色」都可以响应颜色渐变的动画,「主题色」则不支持动画,所以如果想要设置某个视图的主题色,比如「按钮」、「开关」等,可以通过重写其Style
方法来实现,如下例:
1 | swift复制代码Toggle(isOn: $changed, label: { |
透明度(Opacity)
opacity
控制视图的透明度,取值范围从 0.0(完全透明)到 1.0(完全不透明)。在动画中,可以用其来实现视图的渐进渐出效果,如下例:
1 | swift复制代码View.opacity(changed ? 1 : 0) |
虽然视觉上不可见了,但其依然保持空间的占用,不会影响到「兄弟」视图的布局,比如下例中,「红色」不会因为「绿色」的消失而「浮动」(上浮)。
所以,如果想要实现类似于弹窗、模态窗、提示窗口等视图,可以使用ZStack
容器或者.overlay
修饰符来实现。
旋转(RotationEffect)
.rotationEffect
修饰符允许你对视图进行旋转变换,比如实现一个旋转的按钮:
1 | swift复制代码View |
再结合「偏移量」、「颜色」、「透明度」实现一个多功能按钮:
1 | swift复制代码 |
三维旋转(Rotation3DEffect)
使用 rotation3DEffect
可以为视图添加立体旋转效果,这种效果特别适合制作翻转动画。如下例:
1 | swift复制代码View |
缩放(ScaleEffect)
scaleEffect
用于改变视图的尺寸,当结合动画使用时,可以创造出视图缩放进入或退出屏幕的动态效果。如下例:
1 | swift复制代码View |
scaleEffect
与frame
都可以实现视图的缩放效果,区别在于frame
缩放后会影响兄弟视图的布局,如下例:
字体(Font)
动画化字体大小变化是一个很好的方式来吸引用户的注意或者提供动态的视觉反馈。如下例:
1 | swift复制代码Text("Hello, SwiftUI") |
仔细观察会发现,字体在放大的过程中有些诡异的左右抖动,这是因为字体的缩放过程中,也会缩放它占用的空间,换句话说,它的缩放也会影响到其它兄弟视图的布局,所以解决的办法就是预先设置好它的frame
尺寸(能装得下最大的字体)。
1 | swift复制代码Text("Hello, SwiftUI") |
字体权重也可以动画化,如下例:
1 | swift复制代码Text("Hello, SwiftUI") |
字体类型
1 | swift复制代码Text("Hello, SwiftUI") |
字体风格
1 | swift复制代码Text("Hello, SwiftUI") |
裁剪(Trim)trim
修饰符通常与 Shape
结构体一起使用,比如 Circle
, Rectangle
, Path
等,它的功能是根据起始和结束点来截取形状的一部分。所以可以动画化这些起始和结束点的参数来创造出动态的效果,例如一个进度条。
1 | swift复制代码Circle() |
网格布局(Grid)
当在网格布局中,根据特定条件显示或隐藏某个网格视图,也可以增加一些动画效果,例如实现一个帷幕。
1 | swift复制代码Grid { |
本文转载自: 掘金