一:編寫第一個SwiftUI程序
1,創(chuàng)立SwiftUI工程

-左面是代碼區(qū)
-右側是Canvas
編寫代碼時,右側的Canvas可以實時顯示出代碼的UI預覽效果

2,編寫UI布局代碼
經過拖拽添加UI控件

VStack:SwiftUI常用的一種布局元素,可以用來垂直地疊加視圖
HStack:水平疊加視圖

HStack {
VStack {
Text("Rooms")
Text("20 people")
}
}
在文字左面添加一個圖片
HStack {
// `photo`是體系自帶資源庫中的圖片
Image(systemName: "photo")
VStack {
Text("Rooms")
Text("20 people")
}
}
在Canvas中將VStack修正為左對齊

設置Text的字號

HStack {
Image(systemName: "photo")
VStack(alignment: .leading) {
Text("Rooms")
Text("20 people")
.font(.subheadline)
}
}
咱們稱.font(.subheadline)為潤飾器(modifier),用來自界說視圖的外觀或行為
設置Text的色彩為secondary
HStack {
Image(systemName: "photo")
VStack(alignment: .leading) {
Text("Rooms")
Text("20 people")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
將HStack替換為List


3,設置數(shù)據(jù)源
添加Room模型
在SwiftUI中,需要讓模型遵照Identifiable協(xié)議,完結id特征

在ContentView中運用Room數(shù)據(jù)來展現(xiàn)UI

4,豐富UI內容
設置圖片圓角
經過拖拽Modifier庫來完結

設置Navigation、NavigationTitle以及給每個單元格設置跳轉
NavigationView {
List(rooms) { room in
NavigationLink(destination: Text(room.name)) {
Image(systemName: "photo")
.cornerRadius(8.0)
VStack(alignment: .leading) {
Text(room.name)
Text("(room.capacity) people")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
.navigationTitle(Text("Rooms"))
}
進入實時方法,查看效果

將子視圖提成一個單獨的視圖

創(chuàng)立新的頁面:RoomDetail
struct RoomDetail: View {
let room: Room
var body: some View {
Image(room.imageName)
.resizable()
.aspectRatio(contentMode: .fit)
}
}
struct RoomDetail_Previews: PreviewProvider {
static var previews: some View {
RoomDetail(room: testData[0])
}
}
設置navigationBarTitle
Image(room.imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.navigationBarTitle(Text(room.name))
在預覽中添加NavigationView上下文
struct RoomDetail_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
RoomDetail(room: testData[0])
}
}
}
調整navigationBarTitle的展現(xiàn)方法

將RoomCell的跳轉修正為前往RoomDetail頁面
struct RoomCell: View {
let room: Room
var body: some View {
NavigationLink(destination: RoomDetail(room: room)) {
//
}
}
}
二:Swift UI的工作方法
2.1 View
A View Defines a Piece of UI
在SwiftUI中,視圖是一種遵守View協(xié)議的結構,而不是繼承自根底類的類
A View Defines its Dependencies
2.2 狀態(tài)特征
@State
當SwiftUI看到一個帶@State狀態(tài)變量的視圖時,它會以視圖的名義為那個變量分配存儲空間。

綠色部分是APP的內存
紫色是SwiftUI所處理的內存
SwiftUI可以觀察到@State變量合時被讀寫,一同SwiftUI知道zoom是從body中讀取的,SwiftUI會在@State變量產生更改時,使用新的狀態(tài)值,改寫烘托。
例:完結在RoomDetail中,點擊圖片修正填充方法
struct RoomDetail: View {
let room: Room
@State private var zoomed = false
var body: some View {
Image(room.imageName)
.resizable()
.aspectRatio(contentMode: zoomed ? .fit : .fill) // 根據(jù)zoomed值修正填充方法
.navigationBarTitle(Text(room.name), displayMode: .inline)
.onTapGesture {
self.zoomed.toggle() // 點擊修正zoomed的值
}
}
}
2.3 實際來歷
在SwiftUI中,UI或許因不同的數(shù)據(jù),處于不同的狀態(tài),咱們將這些用來繪制UI的數(shù)據(jù)稱為“實際來歷”,“實際來歷“由狀態(tài)變量和模型一同組成。
特征可以簡略地分為:實際來歷(Source of Truth)和衍生值(Derived Value)

zoomed變量是一個實際來歷,contentMode衍生自它,當體系觀察到zoomed變量產生變化時,SwiftUI結構會請求新的body,改寫烘托,從頭生成一個新的寬高比視圖,接下來覆蓋contentMode。
數(shù)據(jù)流原語(Data Flow Primitives)

SwiftUI是數(shù)據(jù)驅動,而不是事情驅動
三:完善Rooms APP
添加動畫
.onTapGesture {
withAnimation {
self.zoomed.toggle()
}
}
添加一個ZStack
ZStack(alignment: .topLeading) {
Image(room.imageName)
.resizable()
.aspectRatio(contentMode: zoomed ? .fit : .fill)
.navigationBarTitle(Text(room.name), displayMode: .inline)
.onTapGesture {
withAnimation {
self.zoomed.toggle()
}
}
Image(systemName: "video.fill")
.font(.title)
.padding(.all)
}

固定圖標的方位
Image(room.imageName)
.frame(minWidth:0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)

一同預覽多個View
struct RoomDetail_Previews: PreviewProvider {
static var previews: some View {
Group {
NavigationView {
RoomDetail(room: testData[0])
}
NavigationView {
RoomDetail(room: testData[1])
}
}
}
}
添加動效
給視頻圖標添加過渡動效
if room.hasVideo && !zoomed {
Image(systemName: "video.fill")
.font(.title)
.padding(.all)
.transition(.move(edge: .leading))
}

給圖片的動效延伸時刻
.onTapGesture {
withAnimation(.easeInOut(duration: 2)) {
self.zoomed.toggle()
}
}
支撐動態(tài)添加
監(jiān)測數(shù)據(jù)模型的改動,實時更新UI
創(chuàng)立RoomStore儲存Room模型
import SwiftUI
class RoomStore {
var rooms: [Room]
init(rooms: [Room] = []) {
self.rooms = rooms
}
}
遵守ObservableObject協(xié)議
class RoomStore: ObservableObject {
@Published var rooms: [Room]
//
}
聲明EnvironmentObject類型變量
@EnvironmentObject var store: RoomStore
傳入EnvironmentObject類型變量
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(RoomStore(rooms: testData))
}
}
列表中添加一個按鈕
List {
Button(action:{
}) {
Text("Add Room")
}
// ForEach為它的每個集結項都創(chuàng)立一個視圖
ForEach(store.rooms) { room in
RoomCell(room: room)
}
}

Button(action:addRoom) {
Text("Add Room")
}
func addRoom() {
store.rooms.append(Room(name: "Hall 2", capacity: 2000))
}

修正List的樣式
修正listStyle
NavigationView {
List {
//
}
.navigationBarTitle(Text("Rooms"))
.listStyle(GroupedListStyle())
}
設置分組
List {
Section {
Button(action:addRoom) {
Text("Add Room")
}
}
Section {
ForEach(store.rooms) { room in
RoomCell(room: room)
}
}
}
支撐動態(tài)刪去
func delete(at offsets: IndexSet) {
store.rooms.remove(atOffsets: offsets)
ForEach(store.rooms) { room in
RoomCell(room: room)
}
.onDelete(perform: delete)

設置NavigationBarItem
NavigationView {
List {
}
.navigationBarItems(trailing: EditButton())
}
支撐列表從頭排序
func move(from source: IndexSet, to destination: Int) {
store.rooms.move(fromOffsets: source, toOffset: destination)
}
ForEach(store.rooms) { room in
RoomCell(room: room)
}
.onDelete(perform: delete)
.onMove(perform: move)

設置預覽環(huán)境
Group {
ContentView()
.environmentObject(RoomStore(rooms: testData))
// 大字號環(huán)境
ContentView()
.environmentObject(RoomStore(rooms: testData))
.environment(.sizeCategory, .extraExtraLarge)
// 深色方法
ContentView()
.environmentObject(RoomStore(rooms: testData))
.environment(.colorScheme, .dark)
// 布局方向
ContentView()
.environmentObject(RoomStore(rooms: testData))
.environment(.layoutDirection, .rightToLeft)
.environment(.locale, Locale(identifier: "ar"))
}
總結
SwiftUI四個首要規(guī)劃準則:
- Declarative
- Compositional
- Automatics
- Consistent