Components and Visual Effects
教程地址:传送门 本篇主要内容是SwiftUI里面的组件和视觉效果(一些动画比如旋转,缩放,位移,扭曲,或者高斯模糊等)。
创建组件
接着上篇的内容,现在需要把卡片的代码封装成一个组件,以便于重复使用。
按住command,然后点下面的一个VStack,然后选择抽取子视图(Extract Subview):
然后就能看到,刚才的整个VStack被抽取成为一个单独的struct,并被抽取到当前结构体的下方:
这个时候上面和下面的ExtractedView都是处于高亮状态,修改上面,下面也会跟着改,这里改成CardView:
抽取组件的时候,要特别注意的一点事,抽取出来的组件将可以在整个应用编写过程中使用,而无需导入包之类的操作,所以要给抽取的组件一个尽可能独特的名字。
上面的背景卡片,因为要重复使用, 抽成组件也是必要的,这里把上面的背景卡片抽取成BackCardView:
这里就能看出组件化的必要性了,可以使主界面看上去很简洁,还可以复用。
modifier顺序的影响
SwiftUI里面,Modifier的编写顺序是很重要的,因为modifier无论表面还是本质都是调用方法,调用一个modifier返回self,然后又可以连续调用其他modifier。
而Flutter修改属性的方式是用可选的构造器参数,所以无论什么顺序都是都所谓的。在这一点上,我感觉Flutter的方式比较好。
这里的例子是调用conerRadius会影响Shadow效果。前面说过,conerRadius会裁剪边框,阴影实质上属于组件边界之外的,如果conerRadius在shadow之后调用,会将阴影效果直接裁减掉:
上面的例子中可以看到BackCardView的阴影效果消失了(被裁剪掉了)。因为这个地方需要多个背景卡片,所以需要的位移是不一样的,所以要把**位移(offset)**这个modifier放到上面来(当然也可以把位移的参数设置成BackCardView的参数,然后在使用BackCardView的时候传入位移参数):
多张卡片,只要复制粘贴,然后修改offset的参数就行了:
视觉效果
只是让卡片堆叠起来还是不够的,由近及远,卡片的大小有一些变化,层次感会更好,这时候就要用到缩放效果(scaleEffect):
func scaleEffect(_ s: CGFloat, anchor: UnitPoint = .center) -> some View
两个参数中,s是缩放的比例,小于1为缩小,大于1为放大。anchor参数为缩放的锚点,默认为中心。
可以看到三张堆叠在一起的卡片渐次变小。但是尺寸的变化有点突然,最上面的卡片比第二张大的多,但是第二张比最下面一张大的没那么明显。
因为附加了缩放效果,其实这个时候三张卡片的尺寸完全可以设定成一样的,大小由缩放的参数来决定:
struct BackCardView: View {
var body: some View {
VStack {
Spacer()
}
.frame(width : 340,height : 220)
.background(Color.blue)
.cornerRadius(20)
.shadow(radius: 20)
}
}
这次看起来效果就缓和得多:
接下来给背景卡片添加一个角度,要用到旋转效果(rotationEffect):
func rotationEffect(_ angle: Angle, anchor: UnitPoint = .center) -> some View
上面是rotationEffect函数的定义,参数有两个,angle是一个Angle类型的变量,很容易理解这是一个角度值,第二个参数是anchior,类型为UnitPoint,按照字面意思来解读,可以知道这是旋转的锚点,即旋转的中心点,默认值为中心点。
将rotationEffect 分别应用于两个背景卡片:
这里的angle参数是使用.degree(10)来生成的。这是swift里面的一个简便写法,因为在函数定义的时候,angle参数就确定为Angle类型,所以.degree(10)就相当于调用了Angle.degree(10)。
如果要表现得更有层次感,这个时候就要用到3D旋转效果(rotation3DEffect):
函数的第一个参数依然是angle角度,第二个参数是旋转轴,因为是3D效果,所以需要三个参数。
为了看上去更自然,要用到渲染模式(blendMode):
func blendMode(_ blendMode: BlendMode) -> some View
只有一个类型为枚举类型BlendMode的参数。
这里使用的是hardLight:
跟上面的offset同理,这里的背景卡片要定制不同的背景,需要把一些与背景相关的modifier挪到上面:
改一下颜色以区分不同的卡片:
然后要创建一个卡片的具体介绍界面。效果图如下:
在根视图的ZStack最底层写一个Text,然后放入HStack,并且嵌套布局,使文字显示在左上角:
在HStack和Spacer中间插入一张背景图,然后抽取为TitleView:
在CardView上层,创建一个介绍卡片具体内容的Text:
用VStack包裹起来,然后改一下背景色,增加一个内边距:
然后是圆角和阴影:
加一个Spacer,把文字推到最上面:
用偏移(offset)将将这个界面推下来:
使简介的文字居中对齐,用到多行文字对齐(multilineTextAlignment):
改一下字体大小(font)和行距(lineSpacing):
然后是绘制上面的一个深色小长条。
直接可以用长方形(Rectangle),然后限制尺寸:
再加上圆角(conerRadius),改一下透明度(opacity):
改一下内边距(padding):
使用重载的frame来保证界面被渲染成最大宽度,然后给VStack的子View加入间隙:
然后将这个VStack抽取为BottomCardView:
给TitleView和BottomCardView添加高斯模糊(blur),可以使背景虚化:
下一篇介绍动画。