100 Days of SwiftUI - Day 23 項目3 Part1

通過這個項目我們將討論一些深層次的問題,完成本項目,你會對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,將maxWidthmaxHeight設為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都可以了,隨你。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容