【譯】WWDC2019之SwiftUI

一線搬磚工人發表於2019-06-04

SwiftUI基礎教程

SwiftUI只支援Xcode 11、iOS 13版本及以上。

官方文件連結:developer.apple.com/tutorials/s…

建立組合檢視

本篇文章將通過一個構建應用(Landmarks,一個可以發現、分享你喜歡地點的App)示例,來引導大家進行SwiftUI開發。我們將使用SwiftUI框架來構建Landmark詳情介面。

Landmarks利用stacks將圖片和文字組合起來來進行檢視佈局。你需要引用MapKit框架標頭檔案來建立一個地圖檢視。 你可以通過Xcode新的實時反饋功能,來優化你的檢視佈局

1.下載Demo工程。
2.下載Xcode11 Beta。

建立工程

利用SwiftUI應用模版來建立工程,然後探索瞭解下SwiftUI的畫布。

為了能夠體驗Xcode 11的view實時預覽和互動功能,一定要確保你的mac系統版本是macOS 10.15 beta

第一步

開啟 Xcode->Create a new Xcode project,或者通過File > New > Project 來建立工程。

【譯】WWDC2019之SwiftUI

第二步

在模版選擇區域,選擇 iOS->Single View App->Next

【譯】WWDC2019之SwiftUI

第三步

輸入專案名稱 Landmarks->勾選Use SwiftUI->Next 儲存。

【譯】WWDC2019之SwiftUI

第四步

在Xcode導航欄,建立ContentView.swift。通常SwiftUI會宣告兩個結構體。第一個結構體繼承自View,並且在這兒進行View的佈局。第二個結構體宣告瞭一個ContentView 的preview,繼承自PreviewProvider

感謝@SoolyChristina基友的友情提示。這兒並非是繼承的概念,原文的描述如下: The first structure conforms to the View protocol and describes the view’s content and layout. The second structure declares a preview for that view.

所以這兒宣告的兩個結構體,更像是遵循了View和PreviewProvider協議。


import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼
【譯】WWDC2019之SwiftUI

第五步

在SwiftUI畫布中點選 Resume 進行檢視預覽。

【譯】WWDC2019之SwiftUI

Tip:如果畫布沒有展示出來,可以通過 Editor > Editor and Canvas 顯示出來。

第六步

把Hello World更改為Hello SwiftUI!

當你修改文案後,SwiftUI會自動更新檢視。(這他麼不就是熱過載嘛 Hot-Reload

自定義Text View

你有兩種方式來自定義TextView。第一種方式是直接修改view程式碼,第二種方式是通過inspector檢查器來幫助你進行程式碼編寫。

當你構建Landmarks的時候,你可以運用任何一個編輯器來進行編碼工作:直接修改原始碼、通過畫布、通過inspector view檢查器。程式碼並不會關心你用什麼工具,它始終能夠保持最新狀態。

接下來,你將通過inspector來自定義Text View

第一步

在preview畫布上,按住Command鍵+點按Text文字框,這時候inspector就會被喚起。

inspector彈出框所展示的屬性也會因為不同的UI控制元件而有所不同。

【譯】WWDC2019之SwiftUI

第二步

通過inspector檢查器修改Text文字框的屬性。

【譯】WWDC2019之SwiftUI

第三步

修改文字框字型。

修改文字框字型是利用的系統的字型。

【譯】WWDC2019之SwiftUI

第四步

手動修改程式碼,即新增.color(.green) 把文字修改成綠色。
要自定義SwiftUI檢視,你可以呼叫modifiers方法。Modifiers可以修改檢視的屬性,並且modifier返回一個新的檢視,所以通常會將多個modifiers像鏈一樣垂直堆疊在一起。( 說白了就是鏈式程式設計,每呼叫一個方法就返回自身 )。


import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Turtle Rock")
            .font(.title)
            .color(.green)
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼

你編寫的程式碼肯定和view是一一對應的。當你通過inspector修改了view屬性之後,Xcode會自動更新你的程式碼。

第五步

這時候,開啟inspector,然後把文字Color屬性修改為Inherited。

【譯】WWDC2019之SwiftUI

第六步

注意一點的就是,Xcode會根據inspector修改自動更新你的程式碼。

利用Stacks組合檢視

我們建立了一個文字框用來顯示landmark的詳情資訊,並且把這個文字控制元件放到頭部。
當我們建立SwiftUI檢視控制元件的時候,我們會把控制元件的內容、佈局還有一些行為放在body屬性中;然而body屬性只返回了一個view。你可以利用stacks嵌入多個view,它可以垂直嵌入、水平嵌入等。

在這個篇幅,我們將使用垂直stack來顯示park詳情資訊。

【譯】WWDC2019之SwiftUI

第一步

Command+點按text初始化方法區域。選擇 Embed in VStack

【譯】WWDC2019之SwiftUI

第二步

接下來,我們將拖拽一個text view到stack中。

點選+號,開啟Library皮膚。拖拽一個text view到 “Turtle Rock”後面

【譯】WWDC2019之SwiftUI

第三步

修改text view文案為 Joshua Tree National Park

第四步

設定text view的字型。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Turtle Rock")
                .font(.title)
            Text("Joshua Tree National Park")
                .font(.subheadline)
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼

第五步

修改VStack對齊方式。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            Text("Joshua Tree National Park")
                .font(.subheadline)
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼

如果不設定對齊方式,VStack預設是內容垂直居中。

第六步

在皮膚中,Command+點按 Joshua Tree National Park 喚起inspector,選擇 Embed in HStack

【譯】WWDC2019之SwiftUI

第七步

在location後面新增一個新的文字框,修改文字框文案並設定字型。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Text("California")
                    .font(.subheadline)
            }
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼

第八步

可以在兩個水平的文字框之間新增Space來適應寬度。
Space把父檢視在水平或者垂直方向上全部充滿。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Spacer()
                Text("California")
                    .font(.subheadline)
            }
        }
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼
【譯】WWDC2019之SwiftUI

第九步

最後,利用padding()來設定邊距。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Turtle Rock")
                .font(.title)
            HStack {
                Text("Joshua Tree National Park")
                    .font(.subheadline)
                Spacer()
                Text("California")
                    .font(.subheadline)
            }
        }
        .padding()
    }
}

struct ContentView_Preview: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
複製程式碼

建立一個自定義的圖片檢視

我們已經把park名稱和位置的檢視做好了,接下來我們將給park新增個圖片。

你不需要新增很多程式碼,就可以新增一個帶mask、border、shadow的圖片。

第一步

新增一張圖片到asset catalog中。

在Resource資料夾中找到turtlerock.png圖片,然後把它拖拽到asset catalog中。

【譯】WWDC2019之SwiftUI

第二步

選擇 File > New > File 開啟模版選擇皮膚。在 User Interface 區域,選擇 SwiftUI View->Next ,命名為CircleImage.swift。

【譯】WWDC2019之SwiftUI

第三步

把Text構建方法替換成Image。

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
複製程式碼

第四步

呼叫.clipShape(Circle())方法,建立圓形檢視。

【譯】WWDC2019之SwiftUI

第五步

再建立一個圓圈,用灰色進行填充。並將它作為image的border。


import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
            .overlay(
                Circle().stroke(Color.gray, lineWidth: 4))
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
複製程式碼

第六步

新增陰影。

第七步

將邊框顏色更改為白色。

import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("turtlerock")
            .clipShape(Circle())
            .overlay(
                Circle().stroke(Color.white, lineWidth: 4))
            .shadow(radius: 10)
    }
}

struct CircleImage_Preview: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
複製程式碼

UIKit和SwiftUI混合使用

現在我們需要建立一個地圖檢視。你可以MapKit中的MKMapView類來展示渲染地圖介面。

在SwiftUI中要使用UIView或者其子類,你需要讓你的view遵循UIViewRepresentable協議。SwiftUI在WatchKit和AppKit同樣宣告瞭類似的協議。

【譯】WWDC2019之SwiftUI

第一步

建立新的SwiftUI View來展示MKMapView。 File > New > File ,然後建立MapView.swift。

【譯】WWDC2019之SwiftUI

第二步

引入MapKit標頭檔案,並且讓MapView遵循UIViewRepresentable協議。

第三步

UIViewRepresentable協議有兩個協議方法需要實現。第一是UIView(context:)來建立MKMapView。第二個updateUIView(_:context:)來更新view。

把body屬性幹掉,然後UIView(context:)協議方法來建立MKMapView。


import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }
}

struct MapView_Preview: PreviewProvider {
    static var previews: some View {
        MapView()
    }
}
複製程式碼

第四步

實現updateUIView(_:context:)協議方法,來更新view(設定地圖經緯度等)。

func updateUIView(_ view: MKMapView, context: Context) {
        let coordinate = CLLocationCoordinate2D(
            latitude: 34.011286, longitude: -116.166868)
        let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)
}
複製程式碼

第五步

當在靜態模式下進行預覽的時候,Xcode只能渲染SwiftUI檢視控制元件。因為MKMapView是UIView子類,所以你需要把模式切換成live模式才能正常預覽。

點選 Live Preview 切換預覽模式。

【譯】WWDC2019之SwiftUI

把上面的子控制元件組合成一個完成的詳情介面

現在我們已經把所有子控制元件定義實現好了。
利用我們現有的工具,我們可以把這些子控制元件組合起來,形成完整的landmarks詳情介面。

【譯】WWDC2019之SwiftUI

第一步

在工程導航區,選擇ContentView.swift檔案。

第二步

在這三個text view控制元件外面,再嵌入一個VStack檢視。

struct ContentView: View {
    var body: some View {
        VStack {
            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}
複製程式碼

第三步

將你自定義的MapView放在stack的上面。設定MapView的frame。
如果你只設定了Mapview的高度,那麼MapView會自動設定其寬度來適應父檢視。所以MapView會充滿寬度區域。


struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}
複製程式碼
【譯】WWDC2019之SwiftUI

第四步

點選 Live Preview 來預覽效果。
預覽狀態下,你可以繼續編寫view的程式碼,Live Preview會實時更新檢視。

第五步

將CircleImage新增到stack上面。

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .frame(height: 300)

            CircleImage()

            VStack(alignment: .leading) {
                Text("Turtle Rock")
                    .font(.title)
                HStack(alignment: .top) {
                    Text("Joshua Tree National Park")
                        .font(.subheadline)
                    Spacer()
                    Text("California")
                        .font(.subheadline)
                }
            }
            .padding()
        }
    }
}
複製程式碼

第六步

調整一下Image的偏移。

第七步

在VStack的底部新增spacer佔位。

第八步

最後設定下 edgesIgnoringSafeArea(.top) 。

【譯】WWDC2019之SwiftUI

相關文章