opacity()
用于设置某个 View 的透明度。
struct ContentView: View { var body: some View { Text("圆角矩形") .padding() .background(Color.red) .cornerRadius(25) .opacity(0.5) //透明度 } }
compositingGroup()
将修饰的内容组合以后再产生后续的效果。
struct ContentView: View { var body: some View { ZStack { Circle().fill(Color.red) Circle().fill(Color.green).offset(x: -40, y: 75) Circle().fill(Color.blue).offset(x: 40, y: 75) } .compositingGroup() //可以比较一下加与不加的区别 .opacity(0.5) .frame(width: 160, height: 160) } }
colorInvert()
用于颜色取反,例如原先是白色,取反后是黑色。
struct ContentView: View { var body: some View { ZStack { Circle().fill(Color.red) Circle().fill(Color.green).offset(x: -40, y: 75) Circle().fill(Color.blue).offset(x: 40, y: 75) } .compositingGroup() .opacity(0.5) .frame(width: 160, height: 160) .colorInvert() //反转颜色 } }
id()
为 View 绑定一个唯一标识符,当其改变时,表面上看,该 View 恢复到初始状态,其本质是创建了一个新的 View。需要注意的是待重置的 View 必须是一个独立封装的 View。
import SwiftUI //封装一个独立的View struct TextFieldGroup: View { @State private var text0 = "" @State private var text1 = "" @State private var text2 = "" @State private var text3 = "" @State private var text4 = "" var body: some View { VStack { TextField("text0", text: $text0) TextField("text1", text: $text1) TextField("text2", text: $text2) TextField("text3", text: $text3) TextField("text4", text: $text4) } .padding(.horizontal) .textFieldStyle(RoundedBorderTextFieldStyle()) } } struct ContentView: View { @State private var textFieldId = 0 var body: some View { VStack { TextFieldGroup() .id(textFieldId) //绑定id Button("重置") { self.textFieldId += 1 //改变id的值 } } } }
亮度、色彩、饱和度、对比度
struct ContentView: View { var body: some View { Image("jobs") .brightness(0.1) // 亮度 .colorMultiply(.red) // tint为红色 .saturation(0.5) // 饱和度 .contrast(0.5) // 对比度 } }
layoutPriority()
layoutPriority 用于控制布局的优先级,让父 View 优先对某个子 View 进行布局。在默认情况下,布局优先级都为 0。
举例来说,运行下面的代码,此时 Image 和第 1 个 Text 内容显示完整,第 2 个 Text 内容被截断。
struct ContentView: View { var body: some View { HStack { Image(systemName: "person.circle") Text("站长介绍:") .background(Color.red) Text("熟悉iOS、Swift等单词的拼写") .background(Color.green) } .lineLimit(1) .frame(width: 300) } }
如果此时希望优先考虑第 2 个 Text,让它的内容优先显示,就可以用 layoutPriority。
struct ContentView: View { var body: some View { HStack { Image(systemName: "person.circle") Text("站长介绍:") .background(Color.red) Text("熟悉iOS、Swift等单词的拼写") .layoutPriority(1.0) //优先显示 .background(Color.green) } .lineLimit(1) .frame(width: 300) } }
Modifier 自定义
前面我们解析了很多系统预置的修饰符(Modifier)。实际上我们也可以自己通过组合系统预置的 Modifier 来自定义。另外,当很多 View 有共性的 Modifier(如共同的 background、padding、font 等),此时可以通过自定义 Modifier 进行封装,避免重复,使代码更简洁。
要自定义 Modifier,首先我们要新建一个结构体遵守 ViewModifier 协议,实现 body(content: ) 方法,然后将需要的 Modifier 修饰到 content 参数上。
import SwiftUI struct DIYModifier: ViewModifier { func body(content: Content) -> some View { content .background(Color.red) .foregroundColor(Color.white) .font(.largeTitle) .padding() } } struct ContentView: View { var body: some View { Text("Hello, SwiftUI") .modifier(DIYModifier()) } }
自定义的 Modifier优先级是最低的,如果某个 View的修饰符和自定义 Modifier重复了,即使自定义 Modifier放在 View的最后修饰也不会起作用。
Modifier 的调用顺序
在 View 和 Modifier 解析的系列文章最后,我们来讨论一下 Modifier 的调用顺序问题。也许读者在之前使用的过程中碰到了将两个 Modifier 顺序调换后得到完全不同的效果的现象,在本节我们就着重探讨一下。
判断 Modifier 调用是否要关心顺序其实非常简单,有以下两条原则:
● 若调用 Modifier 后返回的仍然是这个 View 本身,这类 Modifier 的调用不必关心顺序,并且我们称它们为“原地 Modifier”。典型的例子有 font、bold、foregroundColor、italic 等。
● 若调用的 Modifier 将原来的 View 进行包装并返回新的 View,则这类 Modifier 我们往往要关心调用的顺序,并且我们称它们为“封装类 Modifier”。典型的例子有 padding、background 等。
这两点如何判断呢?我们可以借助 Xcode 的自动补全来判断。以下图为例:
我们可以看到 .font 的自动补全提示的返回类型仍然为 Text,那么我们就可以说 .font 是一个“原地 Modifier”,调用时不必关心顺序。
再来看看 .padding 的情况:
显然 .padding 提示返回的类型是一个 View 而不是原来的 Text,那么我们就可以说 .padding 是一个“封装类 Modifier”,调用时要关心顺序,否则会得到错误的结果。
若某个 Modifier的自动补全中既符合原地 Modifier的条件又符合封装类 Modifier的条件,我们视其为原地 Modifier。
为什么说封装类 Modifier 调用时要关心顺序呢?我们来看下面这个例子。
struct ContentView: View { var body: some View { Text("Hello, SwiftUI") .bold() .padding() } }
若无意外,这段程序应该是能顺利运行的。如果我们把第 5 行和第 6 行对调一下会发生什么呢?
struct ContentView: View { var body: some View { Text("Hello, SwiftUI") .padding() .bold() //Error: Value of type 'some View' has no member 'bold' } }
程序报错,因为 View 中没有 bold 这个类型。其实也非常好理解:Text 可以理解为 View 的子类,View 中有的属性 Text 肯定都有,而 Text 中有的属性 View 中则不一定有。上面的例子中,先对 Text 使用 padding,返回的已经是一个 View 了,自然也就没有 "bold" 这个 Text 独有的修饰符了。
为了更加深入理解这个特性,我们再举一个例子。
struct ContentView: View { var body: some View { Text("+") .font(.title) // 1 .foregroundColor(.white) // 2 .padding() // 3 .background(Color.orange) // 4 } }
读者可以尝试替换注释 3 和注释 4 两行代码的位置,看看会有什么变化,并结合上面的讨论思考一下出现这个变化的原因。