第七節(jié) 正向傳值
在之前我們已經(jīng)完成了跳轉(zhuǎn)的部分,但是只是跳轉(zhuǎn)到一個Text View,現(xiàn)在我們新建一個SwiftUI View:SceneryView.swift 文件,用來顯示跳轉(zhuǎn)后的頁面

在新的頁面,我們先創(chuàng)建好所需的視圖,這里我們采用 ZStack 深淺的布局,使得圖片和文字疊加
struct SceneryView: View {
var body: some View {
ZStack {
Image("埃及-金字塔-黃昏")
Text("埃及")
}
}
}

為了能夠在這個頁面接收上一個頁面?zhèn)鬟M來的值,我們定義一個屬性 “scenery”
這個時候在下面的 SceneryView_Previews 預覽代碼就會提示我們,我們定義了一個值,但是沒有進行傳值操作
我們點擊“Fix”自動修復,填入我們之前定義的sceneries數(shù)組

struct SceneryView_Previews: PreviewProvider {
static var previews: some View {
SceneryView(scenery: sceneries[0])
}
}
我們可以發(fā)現(xiàn),在每一個SwiftUI View下方都有一個Previews,它的作用就是讓我們可以調(diào)試數(shù)據(jù),然后預覽效果。它只會在debug狀態(tài)下有效,在運行后這部分的代碼的不會有效的。
我們將代碼修改一下如圖所示,可以看到在Previews中可以測試我們需要顯示的內(nèi)容

然后回到第一個頁面,將跳轉(zhuǎn)的destination目標改為我們新建的SceneryView,并且參照剛才Presviews的代碼,我們寫下如下代碼

這時候點擊右邊畫布的,就可以發(fā)現(xiàn)我們已經(jīng)實現(xiàn)了正向傳值了。
最后我們完善一下SceneryView的布局設置
struct SceneryView: View {
let scenery: Scenery
var body: some View {
// 對齊方式:底部右對齊
ZStack(alignment: .bottomTrailing) {
Image(scenery.imageName)
// 讓圖片不超出父控件范圍
.resizable()
// 自適應比例模式
.aspectRatio(contentMode: .fit)
// 設置最小、最大寬高
.frame(minWidth: 0, idealWidth: 0, maxWidth: .infinity, minHeight: 0, idealHeight: 0, maxHeight: .infinity, alignment: .center)
Text(scenery.location)
// 字體
.font(.largeTitle)
// 四周邊距
.padding()
// 顏色
.foregroundColor(.secondary)
}
// 導航欄顯示樣式為inline
.navigationBarTitle(Text(scenery.name), displayMode: .inline)
}
}
這里是.infinity是指無窮大,結(jié)合.resizble和.fit的限制,圖片就會在當前區(qū)域自動尋找到最合適的高度,這個應該比較好理解
根據(jù)截圖可以看出Image和Text的區(qū)域



第八節(jié) 狀態(tài)綁定和縮放動畫
為了達到點擊圖片出現(xiàn)縮放的效果,我們需要給圖片添加一個點擊手勢事件,并且需要用一個變量來記錄點擊的狀態(tài),這個我們就需要使用到狀態(tài)綁定這個知識點。
關(guān)于@State的說明:加了@State注解的變量,視圖通過監(jiān)視和讀取該變量來重新渲染UI。其狀態(tài)是由SwiftUI來存儲管理的,作為視圖渲染的單一可信來源。
意思也很簡單,就是一旦使用了@State修飾變量,一旦變量的值發(fā)生改變,SwiftUI就會自動刷新頁面,不需要我們手動去刷新。
我們在View中定義一個用來記錄縮放的變量zoomed,然后根據(jù)zoomed的狀態(tài)來改變圖片的顯示模式,代碼如下
struct SceneryView: View {
let scenery: Scenery
@State var zoomed = false
var body: some View {
// 對齊方式:底部右對齊
ZStack(alignment: .bottomTrailing) {
Image(scenery.imageName)
// 讓圖片不超出父控件范圍
.resizable()
// 根據(jù)zoomed狀態(tài)進行調(diào)整
.aspectRatio(contentMode: zoomed ? .fill : .fit)
// 設置最小、最大寬高
.frame(minWidth: 0, idealWidth: 0, maxWidth: .infinity, minHeight: 0, idealHeight: 0, maxHeight: .infinity, alignment: .center)
.onTapGesture {
// toggle相當于對值取反
zoomed.toggle()
}
Text(scenery.location)
// 字體
.font(.largeTitle)
// 四周邊距
.padding()
// 顏色
.foregroundColor(.secondary)
}
// 導航欄顯示樣式為inline
.navigationBarTitle(Text(scenery.name), displayMode: .inline)
}
}

到這里我們就實現(xiàn)了圖片點擊縮放的效果,也是超級簡單。得力于@State狀態(tài)綁定的特性,我們可以很快捷的得到我們想看到的預覽效果。
接下來我們再給縮放添加一個簡單動畫效果:withAnimation
.onTapGesture {
// toggle相當于對值取反
withAnimation {
zoomed.toggle()
}
}
完成了圖片的處理,緊接著我們開始對Text View的動畫進行處理
首先我們完成消失/顯示的處理
if !zoomed {
Text(scenery.location)
.font(.largeTitle)
.foregroundColor(.secondary)
.padding()
}
這里就是表示當zoomed為false時,顯示Text View,為true時隱藏Text View。
另外還有一個向右邊移動的動畫,這個時候我們需要使用到 transition
在SwiftUI中,transition決定了某個View如何插入到視圖棧中,或者如何在視圖棧中移除。transition自身并沒有任何效果, 需要配合動畫一起使用
if !zoomed {
Text(scenery.location)
.font(.largeTitle)
.foregroundColor(.secondary)
.padding()
// 向右邊邊距移動
.transition(.move(edge: .trailing))
}

到這里我們所有的代碼都寫完了,可以說總共不到一百行的代碼,卻實現(xiàn)了很多內(nèi)容。不得不說SwiftUI是真的簡潔好用,已經(jīng)不想寫OC的代碼了,哈哈哈。