Section 03 : Components and Visual Effects

Section 03 : Components and Visual Effects - 组件和视觉效果 (23’50")

Apply transforms, blend modes, blur in SwiftUI and turn views to components.

在 SwiftUI 中使用 transforms, blend modes, blur 将视图转化为组件

1. 抽取 CardView

在代码区按住⌘ 并单击显示第一张卡片的 VStack,在菜单中选择 Extract Subview,Xcode 会将整个 VStack 抽取成一个新的结构体并等待用户命名,这里命名为 CardView。

struct CardView: View {
    var body: some View {
        VStack {
            HStack {
                VStack(alignment: .leading) {
                    Text("UI 设计")
                        .font(.title)
                        .fontWeight(.semibold)
                        .foregroundColor(.white)
                    Text("证书")
                        .foregroundColor(Color("accent"))
                }
                Spacer()
                Image("Logo1")
            }
            .padding(.horizontal, 20)
            .padding(.top, 20)
            Spacer()
            Image("Card1")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 300, height: 110, alignment: .top)
        }
        .frame(width: 340.0, height: 220.0)
        .background(Color.black)
        .cornerRadius(20)
        .shadow(radius: 20)
    }
}
  • 抽取代码只能是最外层的布局控件内,换句话说,按住⌘ 并单击最外层 Stack 不会显示 Extract Subview菜单。
  • 抽取出来的视图应该按照其实现的功能命名,如 CardView 表示这是个显示 Card 的 View。这是好的编程习惯。
  • 苹果提出的命名公约是:自定义名称 + 返回值类型

2. 抽取 BackCardView

使用同样的方法将下层的卡片视图也抽取出来,命名为 BackCardView。

struct BackCardView: View {
    var body: some View {
        VStack {
            Spacer()
        }
        .frame(width: 340.0, height: 220.0)
        .background(Color.blue)
        .cornerRadius(20)
        .shadow(radius: 20)
    }
}

抽取之后的主视图代码如下,看起来清爽了很多。

struct ContentView: View {
    var body: some View {
        ZStack {
            BackCardView()
            CardView()
        }
    }
}

在代码区输入时,如果光标离开了主视图的部分(比如编辑 CardView 或者 BackCardView 两个视图)或者切换到其他应用的窗口后,预览区的预览是会暂停的,需要点击右上角的 Resume来恢复预览。而如果一直在编辑主视图的代码,则预览会跟随编辑一起发生变化。

3. 增加第三张卡片

(1)各层的卡片是靠偏离量来区分的,所以将 BackCardView 中的 offset 修饰器剪切,这样每个新的 BackCard 都可以自己设定偏移量。

(2)在 BackCardView 后面粘贴,这样第一张 BackCard 就显示出来了。

(3)现在,再复制一个带有同样偏移量修饰符的 BackCardView,修改偏移量 y 为 -40。第三张卡片增加好了。

注意

可以看出,在 ZStack 中描述各张卡片的视图顺序刚好与预览图中的显示 相反 ,表示第一张卡片的 CardView 的代码在最后,而第一个 BackCardView 则表示处于最下层的卡片。

4. 缩放(scaleEffect)

(1)为了保证卡片都是基于同一个尺寸缩放而来,先在 BackCardView 结构体中修改 frame 修饰器的 width 为 340,使其大小与 CardView 一致。

(2)分别给两个 BackCardView 加上 scaleEffect 修饰器,(按实际显示)从上至下设定缩放比例为 0.95 和 0.9。

注意:

在元素选择窗口中,输入关键字 Scale,会有多个结果,注意区分不同修饰器修饰的对象。这里我们使用的是scaleEffect

5. 旋转(rotationEffect)

(1)给两个 BackCardView 加上 rotationEffect 修饰器,参数都选择为 5°。

注意:

同样的,输入关键字 rotation 同样会在元素选择窗口中得到多个结果,注意区分。这里我们还是使用rotationEffect

 ZStack {
     BackCardView()
     	.offset(x: 0, y: -40)
        .scaleEffect(0.9)
        .rotationEffect(.degrees(5))
     BackCardView()
         .offset(x: 0, y: -20)
         .scaleEffect(0.95)
         .rotationEffect(Angle.degrees(5))
     CardView()
 }

代码中两种写法的效果是一样的,因为 Swift 可以推测参数类型(或者说此处必为 Angle?),因此可以使用简化的写法。

如果对参数的意义不是很清楚,可以查看文档,也可以先输入点号 (.)后根据自动完成提示查看有什么参数可以用。

查看文档的方法:

  • 常用:按住 ⌥ 键点击要查看的代码。
  • 在元素选择窗口中查看。
  • 在按住 ⌘点击后的菜单里选择 Jump to Definition

(2)修改最底层的卡片旋转角度为 10°。

(3)给中间的卡片增加 3D 旋转效果。

6. 混合模式(BlendMode)

视频说了一大堆如何有趣好玩和流行……

混合模式是图像处理技术中的一个技术名词,不仅用于广泛使用的Photoshop中,也应用于AfterEffect、llustrator 、 Dreamweaver、 Fireworks等软件。主要功效是可以用不同的方法将对象颜色与底层对象的颜色混合。当您将一种混合模式应用于某一对象时,在此对象的图层或组下方的任何对象上都可看到混合模式的效果。

SwiftUI 的 BlendMode 修饰器的枚举值包括:color、colorBurn、colorDodge、darken、destinationOut、destinationOver、difference、exclusion、hardLight、hue、lighten、luminosity、multiply、normal、overlay、plusDarker、plusLighter、saturation、screen、softLight、sourceAtop

百度百科词条混合模式中对这些枚举值做了部分说明。

在三张卡片的最后增加 BlendMode 修饰 .blendMode(.hardLight)

因为是三张卡片都加这个修饰,我尝试加在 ZStack 上,这样可以减少两行代码。看看👁👁后续是否会有问题。

这明显有问题,除非 ZStack 中只有这些内容!!!

7. 修改两张 BackCard 卡片的底色

虽然我们已经提取了 BackCardView,但具体应用到每张卡片时,所关心的一致性内容应该是布局控件(容器)的内部,而非其所带的修饰符。

(1)把底色、圆角和阴影的修饰从 BackCardView 中剪切出来,粘贴到每个 BackCard 上。**注意:**这些修饰应该是在最前面的,前面说过修饰的应用是有顺序的。

(2)修改两张卡片的背景色,这里我们分别使用自定义的card3card4

struct ContentView: View {
    var body: some View {
        ZStack {
            BackCardView()
                .background(Color("card4"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: -40)
                .scaleEffect(0.9)
                .rotationEffect(.degrees(10))
                .rotation3DEffect(.degrees(10), axis: (x: 10, y: 0, z: 0))
            BackCardView()
                .background(Color("card3"))
                .cornerRadius(20)
                .shadow(radius: 20)
                .offset(x: 0, y: -20)
                .scaleEffect(0.95)
                .rotationEffect(Angle.degrees(5))
                .rotation3DEffect(.degrees(5), axis: (x: 10, y: 0, z: 0))
            CardView()
        }.blendMode(.hardLight)
    }
}

struct BackCardView: View {
    var body: some View {
        VStack {
            Spacer()
        }
        .frame(width: 340.0, height: 220.0)
    }
}

缩进快捷键:

  • ⌃ + I 自动缩进
  • ⌘ + [ 减少缩进
  • ⌘ + ] 增加缩进

8. 增加 TitleView

先直接在主视图的 ZStack 中编辑,再抽取为子视图 TitleView,注意观察各个修饰器的用法。

struct TitleView: View {
    var body: some View {
        VStack {
            HStack {
                Text("证书册")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                Spacer()
            }
            .padding()
            Image("Background1")
            Spacer()
        }
    }
}

因为 TitleView 在实际显示中位于最底层,所以在主视图的 VStack 中,它应该第一个出现。

9. 增加 BottomCardView

同样的,编辑并抽取 BottomCardView。因为要在最上层显示,所以,编码应该在 VStack 中的 CardView 之后进行。

struct BottomCardView: View {
    var body: some View {
        VStack(spacing: 20) {
            Rectangle()
                .frame(width: 40, height: 6)
                .cornerRadius(3)
                .opacity(0.1)
            Text("Howard Ge 已经通过本机构《UI设计》课程学习并通过考核,特此证明。")
                .multilineTextAlignment(.center)
                .font(.subheadline)
                .lineSpacing(4)
            Spacer()
        }
        .padding(.top, 8)
        .padding(.horizontal, 20)
        .frame(maxWidth: .infinity) // 撑满
        .background(Color.white)
        .cornerRadius(30)
        .shadow(radius: 20)
        .offset(x: 0, y: 500)
    }
}

10. 模糊(blur)

分别为 TitleView 和 BottomCardView 加上模糊效果 .blur(radius: 20)。现在,除去卡片之外的内容都有了模糊的效果。

本节小结

补充知识

SwiftUI 允许向父容器中添加不多于 10 个子视图,超过时编译不会通过。这也是抽取视图的原因之一吧。

看一下主视图代码部分,会发现由于抽取了子视图,主视图的代码内容很少,更多侧重在修饰器(即展示效果)上。如果要修改某个子视图,只需按住 ⌘点击呼出快捷菜单,选择 Jump to Definition 即可跳转到子视图的定义部分,非常方便。有点像拼乐高积木哈,先关注了每个零件,再关注如何组合。

本节代码请参见 GitHub码云

  • 不同于 CSS 等网页设计,SwiftUI 不是从最外面的容器开始设计,而是从每个部件开始,逐渐逐渐将整个界面丰富起来。这些部件在整个开发的过程中都是共享的。
  • 将具有一致性的部件抽取为子视图,可以加强代码复用。这么觉着和 CSS 中的 class 还是很类似的。
  • VStack 中各个部件的代码顺序与预览中的视觉层级顺序是相反的。
  • 修饰器的应用顺序是有讲究的。应用时可能需要反复实践。
  • 对容器部件使用修饰器,可能会影响到容器内所有的部件。
  • 使用.frame(maxWidth: .infinity)可将 VStack 在水平方向撑满屏幕(无论是什么设备)。
  • 修饰器:缩放、旋转、混合模式、模糊。
  • 控件:rectangle
接下来

准备制作动画啦~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值