通過這個項目我們將討論一些深層次的問題,完成本項目,你會對SwiftUI有透徹的理解。
1.SwiftUI的view為什么是struct?
UIkit時使用類而不是結構體構造視圖,而SwiftUI使用struct。
1.性能因素,結構體比類更簡單,更快。
UIkit時,UIView是類,他有很多屬性方法,子類繼承了它的所有。
在SwiftUI中,使用結構體創(chuàng)建view的速度很快,它的大小取決于你給它多少屬性。
2.除了性能外,類可以自由更改屬性值,而結構體使我們以一種干凈的方式隔絕狀態(tài),SwiftUI知道什么時候它會更改來更新UI。
SwiftUI讓我們以更具功能性的方式來設計,視圖變得簡單,以惰性的方式數據轉換為UI,而不是無法控制的智能方式。
2.main SwiftUI view的后面是什么?
當我們創(chuàng)建項目后,會得到一串代碼Hello world的Text,我們給它加一個背景。
var body: some View {
Text("Hello World")
.background(Color.red)
}
它是這樣的,

我們發(fā)現(xiàn)文本后有一大片的空白,在以前的UIKit時我們可以更改它,但在SwiftUI中你不可以這樣做。
你要有另一種觀念,view后沒有任何東西,你所看到的,就是你擁有的。不要嘗試通過SwiftUI意外的方式來操作。
所以我們如何將Text的背景充滿這個屏幕。
Text("Hello World")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
我們通過修改Text的frame,將maxWidth和maxHeight設為infinity,它會將frame變?yōu)?code>infinity(可能的最大值)。但你會發(fā)現(xiàn)還在安全區(qū)域內,你可以使用edgesIgnoringSafeArea。
Text("Hello World")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
.edgesIgnoringSafeArea(.all)
3.修飾符順序很重要!
var body: some View {
Button("Hello World") {
print(type(of: self.body))
}
.background(Color.red)
.frame(width: 200, height: 200)
}
我們通過type(of:)打印出Button的類型,ModifiedContent<ModifiedContent<Button<Text>, _BackgroundModifier<Color>>, _FrameLayout>
你會發(fā)現(xiàn):
- 每次修改view,SwiftUI會使用泛型
ModifiedContent<OurThing, OurModifier>來修改。 - 使用多次會疊加起來。
在每個ModifiedContent類型里,都有一個要轉化的視圖和實際修改,而不是直接修改改視圖。
所以修飾符順序很重要。
修改背景不用修改frame,并不會去重繪。
使用修飾符的一個問題是,多次使用相同效果,每個修飾符會簡單的添加的以前的內容中。
例如,SwiftUI為我們提供了padding()修飾符。
Text("Hello World")
.padding()
.background(Color.red)
.padding()
.background(Color.blue)
.padding()
.background(Color.green)
.padding()
.background(Color.yellow)
這樣會創(chuàng)建一個多個邊框的背景。
4.為什么用some View類型?
5.條件修飾符
滿足特定條件下才適用的修飾符,在SwiftUI中最簡單的方法是三元運算符。
struct ContentView: View {
@State private var useRedText = false
var body: some View {
Button("Hello World") {
// flip the Boolean between true and false
self.useRedText.toggle()
}
.foregroundColor(useRedText ? .red : .blue)
}
}
在某些情況下,也可以使用if else。
6.環(huán)境修飾符
如果想將修改器應用于多個視圖,例如在VStack中有四個文本視圖,將修飾符應用于VStack,這被稱為環(huán)境修改器。
VStack {
Text("Gryffindor")
Text("Hufflepuff")
Text("Ravenclaw")
Text("Slytherin")
}
.font(.title)
VStack {
Text("Gryffindor")
.blur(radius: 0)
Text("Hufflepuff")
Text("Ravenclaw")
Text("Slytherin")
}
.blur(radius: 5)
像這個不會起作用,它是常規(guī)修飾符。
哪些是環(huán)境修飾符,需要你自己去試驗一下。
7.視圖作為屬性
視圖作為屬性可以避免重復。
struct ContentView: View {
let button = Button(action: {
}, label: {
Text("Button")
})
var body: some View {
VStack {
button
.background(Color.red)
}
}
}
Swift不允許創(chuàng)建引用其它存儲屬性的存儲屬性,但是可以創(chuàng)建計算屬性。
var motto1: some View { Text("Draco dormiens") }
8.視圖分解
struct ContentView: View {
var body: some View {
VStack(spacing: 10) {
Text("First")
.font(.largeTitle)
.padding()
.foregroundColor(.white)
.background(Color.blue)
.clipShape(Capsule())
Text("Second")
.font(.largeTitle)
.padding()
.foregroundColor(.white)
.background(Color.blue)
.clipShape(Capsule())
}
}
}
視圖中有兩個相同的文本視圖,然后將他們包裝在新的自定義視圖中。
struct CapsuleText: View {
var text: String
var body: some View {
Text(text)
.font(.largeTitle)
.padding()
.foregroundColor(.white)
.background(Color.blue)
.clipShape(Capsule())
}
}
你可以在原始視圖中使用
struct ContentView: View {
var body: some View {
VStack(spacing: 10) {
CapsuleText(text: "First")
CapsuleText(text: "Second")
}
}
}
9.自定義修飾符
通過modifier()修飾符來使用,我們創(chuàng)建一個自定義ViewModifier結構,
struct Title: ViewModifier {
func body(content: Content) -> some View {
content
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.background(Color.blue)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
}
Text("Hello World")
.modifier(Title())
將自定義修飾符,放在View的擴展上,是個不錯的主意。
extension View {
func titleStyle() -> some View {
self.modifier(Title())
}
}
Text("Hello World")
.titleStyle()
ViewModifier還可以根據需要創(chuàng)建新的視圖,我們可以添加視圖,然后組合成新視圖返回。
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(Color.black)
}
}
}
extension View {
func watermarked(with text: String) -> some View {
self.modifier(Watermark(text: text))
}
}
我們可以在任何地方添加水印。
Color.blue
.frame(width: 300, height: 200)
.watermarked(with: "Hacking with Swift")
10.自定義容器
自定義一個可填充網格類型的堆棧視圖。
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
init(rows: Int, columns: Int, @ViewBuilder content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
var body: some View {
VStack {
ForEach(0..<rows, id: \.self) { row in
HStack {
ForEach(0..<self.columns, id: \.self) { column in
self.content(row, column)
}
}
}
}
}
}
第一行–struct GridStack<Content: View>: View使用Swift的一個更高級的功能,稱為泛型(generics),在這種情況下,這意味著“您可以提供所需的任何種類的內容,但是無論它是什么,都必須符合View協(xié)議?!蓖瑫r GridStack本身也符合View協(xié)議。
var body: some View {
GridStack(rows: 2, columns: 2, content: { (row, col) in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
})
}
SwiftUI的一種功能(ViewBuilder),該功能允許我們發(fā)送多個視圖并將其形成隱式堆棧。
多數情況下,只是將參數直接復制到結構的屬性中,但請注意該@ViewBuilder屬性在那里。您還將看到該@escaping屬性,該屬性使我們可以存儲閉包,以備后用。
這樣GridStack內加不加stack都可以了,隨你。