笔记
本篇文章记录一下100 Days of SwiftUI
第23-25天的笔记内容
为什么 SwiftUI 使用结构体作为视图?
// 性能因素:结构比类更简单、更快
// 迫使我们考虑以一种干净的方式隔离状态,因为类可以自由更改其值,这可能会导致代码更加混乱
// 如果在视图中使用类,可能会发现的代码无法编译或在运行时崩溃,所以使用结构
SwiftUI 主视图背后是什么?
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
.background(.red)
}
}
// 这样并不能实现整个屏幕为红色,因为Text背后没有任何东西,想要整个屏幕为红色应该让Text占据整个屏幕的空间
Text("Hello, world!")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.red)
为什么修饰符顺序很重要?
Button("Hello, world!") {
// do nothing
}
.background(.red)
.frame(width: 200, height: 200)
// 以上的代码显示效果并不会看到带有“Hello, world!”的200x200红色按钮,而是看到一个200x200的空方块,上面写着“Hello, world!” 位于中间,并在“Hello, world!”周围有一个红色矩形
// 这是因为:每个修饰符都会应用该修饰符创建一个新结构,而不是仅仅在视图上设置属性
print(type(of: self.body))
// 打印ModifiedContent<ModifiedContent<Button<Text>, _BackgroundStyleModifier<Color>>, _FrameLayout>
// 1.每次我们修改视图时,SwiftUI 都会通过使用泛型来应用该修饰符:ModifiedContent<OurThing, OurModifier>。
// 2.当我们应用多个修饰符时,它们只会叠加:ModifiedContent<ModifiedContent<…
// 3.要读取类型是什么,请从最里面的类型开始并逐步解决,每个类型都需要一个要转换的视图以及要进行的实际更改,而不是直接修改视图,所以首先按钮有一些应用了背景颜色的文本,然后给它一个更大的框架
为什么 SwiftUI 使用“some View”作为其视图类型?
// 1.对性能很重要,SwiftUI 需要能够查看我们显示的视图并了解它们如何变化,以便它可以正确更新用户界面
// 2.因为 SwiftUI 使用ModifiedContent,该View协议有一个关联的类型,所以View它本身没有任何意义,我们需要确切地说出它是什么类型的视图
// VStack是如何工作的?
// 使用VStack时SwiftUI会创建一个TupleView,包含VStack中的视图,TupleView只能处理10个视图,也就是为什么SwiftUI 不允许父级内部有超过 10 个视图的原因
// body中创建视图会做什么处理?
// 与VStack原理相同也是创建一个TupleView包含这些视图,并设置给body的@ViewBuilder这个特殊属性
条件修饰符
// 通常希望修饰符仅在满足特定条件时应用,而在 SwiftUI 中,最简单的方法是使用三元条件运算符
struct ContentView: View {
@State private var useRedText = false
var body: some View {
Button("Hello World") {
// flip the Boolean between true and false
useRedText.toggle()
}
.foregroundColor(useRedText ? .red : .blue)
}
}
环境修饰符
// 许多修饰符可以应用于容器,这允许我们同时将相同的修饰符应用于多个视图
VStack {
Text("Gryffindor")
.font(.largeTitle) // 子视图中的修饰符任会替换相同的环境修饰符,Gryffindor会具有大标题
Text("Hufflepuff")
Text("Ravenclaw")
Text("Slytherin")
}
.font(.title)
// 由上述特性可以知道font()是一个环境修饰符
VStack {
Text("Gryffindor")
.blur(radius: 0)
Text("Hufflepuff")
Text("Ravenclaw")
Text("Slytherin")
}
.blur(radius: 5)
// 而blur()是一个常规修改器,因此应用于子视图的任何模糊都会添加到VStack模糊中,而不是替换它
视图作为属性
// 有很多方法可以让您更轻松地在 SwiftUI 中使用复杂的视图层次结构,其中一种选择是使用属性
// 创建一个视图作为您自己视图的属性,然后在布局中使用该属性
struct ContentView: View {
let motto1 = Text("Draco dormiens")
let motto2 = Text("nunquam titillandus")
var body: some View {
VStack {
motto1
.foregroundColor(.red)
motto2
.foregroundColor(.blue)
}
}
}
// Swift 不允许我们创建一个引用其他存储属性的存储属性,因为这会在创建对象时引起问题
// 这意味着尝试创建TextField与本地属性的绑定将会导致问题
// 但是可以创建计算属性
var motto1: some View {
Text("Draco dormiens")
}
// 但是与body不用,Swift 不会自动@ViewBuilder在此处应用该属性,因此如果想返回多个视图还需要调整,提供一下3种方式
// 1.将视图放在堆栈中
var spells: some View {
VStack {
Text("Lumos")
Text("Obliviate")
}
}
// 2.返回一个Group
var spells: some View {
Group {
Text("Lumos")
Text("Obliviate")
}
}
// 3.自己添加@ViewBuilder属性
@ViewBuilder var spells: some View {
Text("Lumos")
Text("Obliviate")
}
视图组成
// SwiftUI 允许我们将复杂的视图分解为更小的视图,而不会产生太大的性能影响
struct ContentView: View {
var body: some View {
VStack(spacing: 10) {
Text("First")
.font(.largeTitle)
.padding()
.foregroundColor(.white)
.background(.blue)
.clipShape(Capsule())
Text("Second")
.font(.largeTitle)
.padding()
.foregroundColor(.white)
.background(.blue)
.clipShape(Capsule())
}
}
}
// 可以修改为:
struct CapsuleText: View {
var text: String
var body: some View {
Text(text)
.font(.largeTitle)
.padding()
.foregroundColor(.white)
.background(.blue)
.clipShape(Capsule())
}
}
struct ContentView: View {
var body: some View {
VStack(spacing: 10) {
CapsuleText(text: "First")
CapsuleText(text: "Second")
}
}
}
自定义修饰符
// SwiftUI允许创建执行特定操作的自定义修饰符
// 创建一个符合ViewModifier协议的新结构,且必须调用body方法接受要使用的内容,并且必须返回some View
struct Title: ViewModifier {
func body(content: Content) -> some View {
content
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.background(.blue)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
}
Text("Hello World")
.modifier(Title())
// 使用自定义修饰符时,j建议创建View扩展使用
extension View {
func titleStyle() -> some View {
modifier(Title())
}
}
Text("Hello World")
.titleStyle()
// 自定义修饰符还可以根据需要创建新的视图结构
struct Watermark: ViewModifier {
var text: String
func body(content: Content) -> some View {
ZStack(alignment: .bottomTrailing) {
content
Text(text)
.font(.caption)
.foregroundColor(.white)
.padding(5)
.background(.black)
}
}
}
extension View {
func watermarked(with text: String) -> some View {
modifier(Watermark(text: text))
}
}
Color.blue
.frame(width: 300, height: 200)
.watermarked(with: "Hacking with Swift")