*SwiftUI is a modern way to declare user interfaces for any Apple platform. Create beautiful, dynamic apps faster than ever before.
一種牛逼的更加快速,更加漂亮的用戶界面的方法

創(chuàng)建和組合視圖
- 本篇文章將通過一個(gè)構(gòu)建應(yīng)用(
Landmarks,一個(gè)可以發(fā)現(xiàn)、分享你喜歡地點(diǎn)的App)示例,來引導(dǎo)大家進(jìn)行SwiftUI開發(fā)。我們將使用SwiftUI框架來構(gòu)建Landmark詳情界面。 -
Landmarks利用stacks將圖片和文本組合起來來進(jìn)行視圖布局。你需要引用MapKit框架頭文件來創(chuàng)建一個(gè)地圖視圖。 你可以通過Xcode新的實(shí)時(shí)反饋功能,來優(yōu)化你的視圖布局 。
創(chuàng)建一個(gè)使用SwiftUI的新Xcode項(xiàng)目。瀏覽畫布、預(yù)覽和SwiftUI模板代碼。
要在
Xcode中預(yù)覽畫布上的視圖并與之交互,請(qǐng)確保您的Mac運(yùn)行的是macOS 10.15 beta版。
macOS 10.15 beta版下載地址
Xcode 11下載地址

第一步
- 打開 Xcode->Create a new Xcode project,或者通過File > New > Project 來創(chuàng)建工程。

第二步
- 在模版選擇區(qū)域,選擇 iOS->Single View App->Next 。
第三步
- 輸入項(xiàng)目名稱 LGSwiftUIDemo->勾選Use SwiftUI->Next 保存。
記得一定要選擇語言:Swift 然后勾選 Use SwiftUI

點(diǎn)擊之后你就會(huì)感覺發(fā)現(xiàn)了新東西咯:

- 左邊沒有了ViewController 多了sceneDelegate和ContentView
- 中間代碼樣式不一樣了
- 右邊多出一塊預(yù)顯示欄,很牛逼
默認(rèn)情況下,SwiftUI視圖文件聲明兩個(gè)結(jié)構(gòu)。
- 第一個(gè)結(jié)構(gòu)符合視圖協(xié)議,描述了視圖的內(nèi)容和布局。
- 第二個(gè)結(jié)構(gòu)聲明了該視圖的預(yù)覽。
現(xiàn)在我們來玩玩預(yù)覽:
如果畫布沒有展示出來,可以通過 Editor > Editor and Canvas 顯示出來。
第四步
把Hello World更改為Hello SwiftUI!
當(dāng)你修改文案后,SwiftUI會(huì)自動(dòng)更新視圖。

自定義Text View
你有兩種方式來自定義TextView。
- 第一種方式是直接修改
view代碼 - 第二種方式是通過
inspector檢查器來幫助你進(jìn)行代碼編寫。
當(dāng)你構(gòu)建Landmarks的時(shí)候,你可以運(yùn)用任何一個(gè)編輯器來進(jìn)行編碼工作:直接修改源代碼、通過畫布、通過inspector view檢查器。代碼并不會(huì)關(guān)心你用什么工具,它始終能夠保持最新狀態(tài)
接下來,你將通過inspector來自定義Text View
第一步
- 在
preview畫布上,按住Command鍵+點(diǎn)按Text文本框,這時(shí)候inspector就會(huì)被喚起。
inspector彈出框所展示的屬性也會(huì)因?yàn)椴煌腢I控件而有所不同。

第二步
通過inspector檢查器修改Text文本框的屬性。

第三步
- 修改文本框字體。
- 修改文本框字體是利用的系統(tǒng)的字體。

第四步
- 手動(dòng)修改代碼,即添加
.color(.green)把文本修改成綠色。 - 要自定義
SwiftUI視圖,你可以調(diào)用modifiers方法。Modifiers可以修改視圖的屬性,并且modifier返回一個(gè)新的視圖,所以通常會(huì)將多個(gè)modifiers像鏈一樣垂直堆疊在一起。( 鏈?zhǔn)骄幊?/strong>)。
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是一一對(duì)應(yīng)的。當(dāng)你通過inspector修改了view屬性之后,Xcode會(huì)自動(dòng)更新你的代碼。

第五步
這時(shí)候,打開inspector,然后把文本Color屬性修改為Inherited。
第六步
注意一點(diǎn)的就是,Xcode會(huì)根據(jù)inspector修改自動(dòng)更新你的代碼。
利用Stacks組合視圖
我們創(chuàng)建了一個(gè)文本框用來顯示landmark的詳情信息,并且把這個(gè)文本控件放到頭部。
當(dāng)我們創(chuàng)建SwiftUI視圖控件的時(shí)候,我們會(huì)把控件的內(nèi)容、布局還有一些行為放在body屬性中;然而body屬性只返回了一個(gè)view。你可以利用stacks嵌入多個(gè)view,它可以垂直嵌入、水平嵌入等。
在這里,我們將使用垂直stack來顯示park詳情信息。

第一步
Command+點(diǎn)按text初始化方法區(qū)域。選擇Embed in VStack。

第二步
- 接下來,我們將拖拽一個(gè)
text view到stack中。 - 點(diǎn)擊+號(hào),打開
Library面板。拖拽一個(gè)text view到 “Turtle Rock”后面 。

第三步
修改text view文案為Joshua Tree National Park。
第四步
設(shè)置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對(duì)齊方式。
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()
}
}
如果不設(shè)置對(duì)齊方式,VStack默認(rèn)是內(nèi)容垂直居中。
第六步
在面板中,Command+點(diǎn)按 Joshua Tree National Park喚起inspector,選擇Embed in HStack。

第七步
在location后面添加一個(gè)新的文本框,修改文本框文案并設(shè)置字體
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()
}
}
第八步
- 可以在兩個(gè)水平的文本框之間添加
Space來適應(yīng)寬度。 -
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()
}
}

第九步
最后,利用padding()來設(shè)置邊距。
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()
}
}
創(chuàng)建一個(gè)自定義的圖片視圖
我們已經(jīng)把park名稱和位置的視圖做好了,接下來我們將給park添加個(gè)圖片。
你不需要添加很多代碼,就可以添加一個(gè)帶mask、border、shadow的圖片。
第一步
添加一張圖片到asset catalog中。
在Resource文件夾中找到turtlerock.png圖片,然后把它拖拽到asset catalog中。
第二步
選擇File > New > File打開模版選擇面板。在 User Interface區(qū)域,選擇 SwiftUI View->Next ,命名為CircleImage.swift。
第三步
把Text構(gòu)建方法替換成Image。
import SwiftUI
struct CircleImage: View {
var body: some View {
Image("turtlerock")
}
}
struct CircleImage_Preview: PreviewProvider {
static var previews: some View {
CircleImage()
}
}
第四步
調(diào)用.clipShape(Circle())方法,創(chuàng)建圓形視圖。

第五步
再創(chuàng)建一個(gè)圓圈,用灰色進(jìn)行填充。并將它作為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混合使用
現(xiàn)在我們需要?jiǎng)?chuàng)建一個(gè)地圖視圖。你可以MapKit中的MKMapView類來展示渲染地圖界面。
在SwiftUI中要使用UIView或者其子類,你需要讓你的view遵循UIViewRepresentable協(xié)議。SwiftUI在WatchKit和AppKit同樣聲明了類似的協(xié)議

第一步
創(chuàng)建新的SwiftUI View來展示MKMapView。 File > New > File,然后創(chuàng)建MapView.swift
第二步
引入MapKit頭文件,并且讓MapView遵循UIViewRepresentable協(xié)議。
第三步
UIViewRepresentable協(xié)議有兩個(gè)協(xié)議方法需要實(shí)現(xiàn)。第一是UIView(context:)來創(chuàng)建MKMapView。第二個(gè)updateUIView(_:context:)來更新view。
把body屬性干掉,然后UIView(context:)協(xié)議方法來創(chuàng)建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()
}
}
第四步
實(shí)現(xiàn)updateUIView(_:context:)協(xié)議方法,來更新view(設(shè)置地圖經(jīng)緯度等)。
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)
}
第五步
當(dāng)在靜態(tài)模式下進(jìn)行預(yù)覽的時(shí)候,Xcode只能渲染SwiftUI視圖控件。因?yàn)?code>MKMapView是UIView子類,所以你需要把模式切換成live模式才能正常預(yù)覽。
點(diǎn)擊Live Preview切換預(yù)覽模式。

把上面的子控件組合成一個(gè)完成的詳情界面
現(xiàn)在我們已經(jīng)把所有子控件定義實(shí)現(xiàn)好了。
利用我們現(xiàn)有的工具,我們可以把這些子控件組合起來,形成完整的landmarks詳情界面。

第一步
在工程導(dǎo)航區(qū),選擇ContentView.swift文件。
第二步
在這三個(gè)text view控件外面,再嵌入一個(gè)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的上面。設(shè)置MapView的frame。
如果你只設(shè)置了Mapview的高度,那么MapView會(huì)自動(dòng)設(shè)置其寬度來適應(yīng)父視圖。所以MapView會(huì)充滿寬度區(qū)域。
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()
}
}
}

第四步
點(diǎn)擊Live Preview來預(yù)覽效果。
預(yù)覽狀態(tài)下,你可以繼續(xù)編寫view的代碼,Live Preview會(huì)實(shí)時(shí)更新視圖。
第五步
將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()
}
}
}
第六步
調(diào)整一下Image的偏移。
第七步
在VStack的底部添加spacer占位。
第八步
最后設(shè)置下 edgesIgnoringSafeArea(.top) 。

整體寫下來,就是感覺很簡(jiǎn)單,很舒服.更加快速的面向開發(fā),此時(shí)此刻還有誰!
Swift 寫天寫地寫世界,千秋萬載,一統(tǒng)江湖