9.%d Xcode Study— Segue & Passing Data
目錄
⦿ Segue(轉場)
⦿ 頁間傳值,正向傳值
⦿ Segue Review
⦿ Unwind Segue
⦿ 反向傳值
Segue(轉場)
在 Xcode 開發中,我們所熟知的 Segue 可藉由幾種方式達成,例如在 storyboard 裡,可以從一個 VC
的控件(UIElement,如 Button
)來 show 另一個 VC
。
當上述的 Segue 完成後,在 AVC
中,按下 Button 便會跳轉到 BVC
。
初學者在練習時常會遇到一個問題,若頁面跳轉
的流程中沒有加入 Navigation Controller 的情況下,A 轉到 B 後,不論你的 transition style 是哪一種,在 B 可沒有能夠回到 A 的方式。
若你從 B 的 Button 再拉一條線 show A,這時的頁面堆疊是 A / B / A,若再按下 A 的 Button,則會變成 A / B / A / B,也就是頁面會不停地堆疊下去,這時,你可以使用 Navigation Controller,又或者透過 Button 的 IBAction,如下:
如果不使用 Navigation Controller,在 IBAction 裡卻使用 popViewController 的話便會像左圖一樣沒有任何變化;所以我們改用 dismiss 順利地回到上一頁。
過程為,原先是 A,畫面堆疊為 A,接著是 A => B,畫面堆疊為 A / B,最後是 A => B => A,畫面堆疊為 A。
繼續閱讀|回目錄
頁間傳值,正向傳值
Passing Data(傳值),在 iOS 開發中是一個重要的觀念,既然懂得頁面的跳轉,那如何將一個頁面的值傳遞到另一個頁面呢?
若以物件導向的真實世界描述為,我這邊做了某件事,通知你狀態已經改變,那麼你就必須反應在身上。
所以在 A 頁面,我們這樣寫:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let controller = segue.destination as! BViewController
controller.name = aTextField.text!
}
prepare 是一個本來就有的方法,但本來的方法並沒有做任何事
,修改原方法就要 override
,而由於 Segue 可包含各種不同的 destination(destination 的型別為 UIViewController),比方說 A 頁面透過 B Button 跳轉到 B 頁面,透過 C Button 跳轉到 C 頁面,此時 B 和 C 就是不同的 destination。
如下:
所以不同的 destination 經過不同的判斷,可以做不同的事,所以透過 segue 去取到的 destination 轉型(降轉)為 BViewController,就可以再去取到 B 裡的 property,name 就是其中一個 property。如下:
我們再將 name 的值設定為 A 中的 TextField 的 text。
而 controller.name = aTextField.text!
這一段程式碼會不會有問題呢?在 Swift 中,若經常使用 !
,程式將常常閃退,所以我們希望看到比較多的 ?
,不過 textField 的 text 在沒有輸入的時候就是空字串,即是 ""
,所以是不會有問題的。
而 let controller = segue.destination as! BViewController
這一段程式碼中,as! 可能會有問題,例如我並沒有這個 B VC,又或者我並沒有能夠跳 B VC 的 Segue。
所以我們可將程式碼改成如下:
if let controller = segue.destination as? BViewController {
controller.name = aTextField.text!
}
最後結果如下:
這個從 A 到 B,並將 A 的數值傳遞到 B 的過程,我們叫做正向傳值。
繼續閱讀|回目錄
Segue Review
至此,我們先回頭看看 Segue 的生成方式。
如果要從 A 頁面按下按鈕跳轉到 B 頁面,只要從按鈕拉線到 B 選 show 即可,但有我們會這麼做,如下:
從 VC,即左上方那個點拉線到 B 選 show,不過這時按下 A 頁面的 Button 是不會起任何作用的。
我們必須先幫這個 Segue 取個 ID 像是 fromAtoB
,在 A 中,就會這樣操作:
@IBAction func aBtnPressed(_ sender: Any) {
self.performSegue(withIdentifier: "fromAtoB", sender: self)
}
performSegue,是不是跟 instantiate ViewController 很像呢?沒錯!兩者都得透過 Identifier(ID)去產生,ID 我們則是在 Storyboard 設定。
這邊故意用了不同的轉場效果來呈現,當然傳值
仍然正常運作。
在正向轉場、傳值
中,用 Button 拉線 show VC 完成的 Segue,跟從 VC 拉線 show VC 的 performSegue,差異在於前者是直接產生 Button action,後者必須要加入 performSegue 這個方法。
那麼,正向轉場叫做 Segue,反向轉場呢?
下一段,我們就來看看 Unwind Segue。
繼續閱讀|回目錄
Unwind Segue
在文章前段,由 B 返回 A 可由 Navigation Controller 的 Left Bar Button Item 去回前頁,也可透過 Button 的 action 去 pop 或 dismiss,但在 Storyboard 中還有另外一種做法 —— Unwind Segue。
早先在數位媒介尚未普及時,影片儲存媒介有個東西叫做錄影帶,錄影帶順著播放看完後,需要將帶子捲回去,這個捲回去的動作叫做倒帶,倒帶的英文是 Rewind,而 Unwind 的意思有點像是解開束縛、鬆開,解開原先的 Segue,就是回上一頁的意思了。
首先,在 AVC 中加入這段程式碼:
@IBAction func bBackToA(_ segue: UIStoryboardSegue) {
}
雖然它是 IBAction,但我們並不需要跟任何控件
相連,回到 Storyboard,在第二頁面的右上角找到 Exit
,這是 ViewController 的出口,我們從控件拉線到這個出口,此時就能找到剛寫好的 IBAction。
操作如下:
同樣地,為了區分不同的 Segue,這邊又用了不同的轉場效果。我們再看看 Unwind Segue 的 function,如下:
是不是前方的圓圈仍然保持空心呢?
繼續閱讀|回目錄
反向傳值
那麼,我們繼續在bBackToA裡面寫程式碼,如下:
@IBAction func bBackToA(_ segue: UIStoryboardSegue) {
if let vc = segue.source as? BViewController,
let text = vc.bTextField.text {
aLabel.text = text
}
}
由於跳回前頁面,是調用 Unwind Segue 的 function,而 function 寫在前頁面,所以這個 Segue 的 Source 也是第二頁面,我們找到這個 VC
,並取用它 textField 的 text
,用它來設定第一頁面的 Label。
而一般為了不使 APP 閃退,我們在取用控件的值時,可以 guard let value = textField.text else { return };同樣地,在不確定能否取用別的頁面(通常是下一頁)的控件的值時,我們可以將值先傳遞到別的頁面,用別的頁面的 property 存起來。
在跳轉到別的頁面時,再用這個 property 去設定別的頁面的控件
,即是不建議
用控件來設定控件
。
最後再複習一下,正向轉場是 Segue 時,傳值使用 prepare
;反向轉場用 Unwind Segue 時,傳值使用 Unwind Segue 的 function。
除此之外,正向傳值也能使用 IBSegueAction
來編寫,最後再來看看成果吧!
這下兩邊能夠互相傳值了。
我們已經擺脫用全域變數來儲存數值並傳遞的方式,既不安全又難以追蹤,是吧?
這次就分享到這,感謝您的閱讀。
繼續閱讀|回目錄
附上 GitHub: