通過(guò)這個(gè)項(xiàng)目我們將討論一些深層次的問(wèn)題曙咽,完成本項(xiàng)目,你會(huì)對(duì)SwiftUI有透徹的理解埠戳。
1.SwiftUI的view為什么是struct?
UIkit時(shí)使用類(lèi)而不是結(jié)構(gòu)體構(gòu)造視圖饱亿,而SwiftUI使用struct赴蝇。
1.性能因素,結(jié)構(gòu)體比類(lèi)更簡(jiǎn)單,更快迎罗。
UIkit時(shí)睬愤,UIView是類(lèi),他有很多屬性方法纹安,子類(lèi)繼承了它的所有尤辱。
在SwiftUI中,使用結(jié)構(gòu)體創(chuàng)建view的速度很快厢岂,它的大小取決于你給它多少屬性光督。
2.除了性能外,類(lèi)可以自由更改屬性值塔粒,而結(jié)構(gòu)體使我們以一種干凈的方式隔絕狀態(tài)结借,SwiftUI知道什么時(shí)候它會(huì)更改來(lái)更新UI。
SwiftUI讓我們以更具功能性的方式來(lái)設(shè)計(jì)卒茬,視圖變得簡(jiǎn)單船老,以惰性的方式數(shù)據(jù)轉(zhuǎn)換為UI咖熟,而不是無(wú)法控制的智能方式。
2.main SwiftUI view的后面是什么?
當(dāng)我們創(chuàng)建項(xiàng)目后柳畔,會(huì)得到一串代碼Hello world的Text馍管,我們給它加一個(gè)背景。
var body: some View {
Text("Hello World")
.background(Color.red)
}
它是這樣的薪韩,
我們發(fā)現(xiàn)文本后有一大片的空白确沸,在以前的UIKit時(shí)我們可以更改它,但在SwiftUI中你不可以這樣做躬存。
你要有另一種觀念张惹,view后沒(méi)有任何東西,你所看到的岭洲,就是你擁有的宛逗。不要嘗試通過(guò)SwiftUI意外的方式來(lái)操作。
所以我們?nèi)绾螌ext的背景充滿(mǎn)這個(gè)屏幕盾剩。
Text("Hello World")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
我們通過(guò)修改Text的frame
雷激,將maxWidth
和maxHeight
設(shè)為infinity
,它會(huì)將frame
變?yōu)?code>infinity(可能的最大值)告私。但你會(huì)發(fā)現(xiàn)還在安全區(qū)域內(nèi)屎暇,你可以使用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)
}
我們通過(guò)type(of:)
打印出Button的類(lèi)型根悼,ModifiedContent<ModifiedContent<Button<Text>, _BackgroundModifier<Color>>, _FrameLayout>
你會(huì)發(fā)現(xiàn):
- 每次修改view,SwiftUI會(huì)使用泛型
ModifiedContent<OurThing, OurModifier>
來(lái)修改。 - 使用多次會(huì)疊加起來(lái)蜀撑。
在每個(gè)ModifiedContent
類(lèi)型里挤巡,都有一個(gè)要轉(zhuǎn)化的視圖和實(shí)際修改,而不是直接修改改視圖酷麦。
所以修飾符順序很重要矿卑。
修改背景不用修改frame,并不會(huì)去重繪沃饶。
使用修飾符的一個(gè)問(wèn)題是母廷,多次使用相同效果,每個(gè)修飾符會(huì)簡(jiǎn)單的添加的以前的內(nèi)容中糊肤。
例如琴昆,SwiftUI為我們提供了padding()修飾符。
Text("Hello World")
.padding()
.background(Color.red)
.padding()
.background(Color.blue)
.padding()
.background(Color.green)
.padding()
.background(Color.yellow)
這樣會(huì)創(chuàng)建一個(gè)多個(gè)邊框的背景轩褐。
4.為什么用some View類(lèi)型椎咧?
5.條件修飾符
滿(mǎn)足特定條件下才適用的修飾符,在SwiftUI中最簡(jiǎn)單的方法是三元運(yùn)算符。
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)境修飾符
如果想將修改器應(yīng)用于多個(gè)視圖,例如在VStack
中有四個(gè)文本視圖脚牍,將修飾符應(yīng)用于VStack向臀,這被稱(chēng)為環(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)
像這個(gè)不會(huì)起作用诸狭,它是常規(guī)修飾符券膀。
哪些是環(huán)境修飾符,需要你自己去試驗(yàn)一下驯遇。
7.視圖作為屬性
視圖作為屬性可以避免重復(fù)芹彬。
struct ContentView: View {
let button = Button(action: {
}, label: {
Text("Button")
})
var body: some View {
VStack {
button
.background(Color.red)
}
}
}
Swift不允許創(chuàng)建引用其它存儲(chǔ)屬性的存儲(chǔ)屬性,但是可以創(chuàng)建計(jì)算屬性叉庐。
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())
}
}
}
視圖中有兩個(gè)相同的文本視圖舒帮,然后將他們包裝在新的自定義視圖中。
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.自定義修飾符
通過(guò)modifier()
修飾符來(lái)使用陡叠,我們創(chuàng)建一個(gè)自定義ViewModifier
結(jié)構(gòu)玩郊,
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的擴(kuò)展上枉阵,是個(gè)不錯(cuò)的主意译红。
extension View {
func titleStyle() -> some View {
self.modifier(Title())
}
}
Text("Hello World")
.titleStyle()
ViewModifier還可以根據(jù)需要?jiǎng)?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.自定義容器
自定義一個(gè)可填充網(wǎng)格類(lèi)型的堆棧視圖拙徽。
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的一個(gè)更高級(jí)的功能假夺,稱(chēng)為泛型(generics),在這種情況下斋攀,這意味著“您可以提供所需的任何種類(lèi)的內(nèi)容,但是無(wú)論它是什么梧田,都必須符合View協(xié)議淳蔼。”同時(shí) 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ā)送多個(gè)視圖并將其形成隱式堆棧。
多數(shù)情況下穿稳,只是將參數(shù)直接復(fù)制到結(jié)構(gòu)的屬性中存皂,但請(qǐng)注意該@ViewBuilder屬性在那里。您還將看到該@escaping屬性,該屬性使我們可以存儲(chǔ)閉包旦袋,以備后用骤菠。
這樣GridStack內(nèi)加不加stack都可以了,隨你疤孕。