Swift 學(xué)習(xí)筆記5 - Segue & View Controller的生命周期

前言

這是斯坦福大學(xué)的課程-Developing iOS 9 APPs with Swift误甚,第6節(jié)課的內(nèi)容罗侯。主要是講:組合多個(gè) MVC (Multiple MVCs) 和 View Controller 的生命周期。在 Swift 學(xué)習(xí)筆記3中我也提到過一些 Multiple MVCs 的基礎(chǔ)知識(shí)涵叮,這篇文章算是應(yīng)用進(jìn)階版铅辞。

經(jīng)過這段時(shí)間的學(xué)習(xí)葵礼,個(gè)人總結(jié)了3個(gè)非常好的學(xué)習(xí) swift 基礎(chǔ)的途徑資料(列在下方)。其它的資料比如各路大神的書籍和博客我作為輔助學(xué)習(xí)迁沫,比如喵神芦瘾,YYKit 的作者 ibireme等等集畅。進(jìn)階的資料近弟,我覺得學(xué)習(xí) Github 中的各種開源項(xiàng)目是個(gè)非常好的選擇,比如喵神的 Kingfisher 等挺智。當(dāng)然如果大家能訂閱我的博客祷愉,我會(huì)非常開心噠。:)

下面是我用的最多的3個(gè) Swift 基礎(chǔ)學(xué)習(xí)資料:

  • 斯坦福的這個(gè)課程,Developing iOS 9 Apps with Swift.
  • 蘋果官方文檔二鳄,里面也有實(shí)例教程赴涵。Learn by doing 永遠(yuǎn)是最好的學(xué)習(xí)方法。
  • 按住 option 點(diǎn)擊代碼中的任一單詞订讼。這個(gè)大家應(yīng)該都知道髓窜,但我是真心越來越覺得這個(gè)太好用了。

Demo

又是這張萌蠢的臉躯嫉,但這次它不僅換了發(fā)型纱烘,還添加了按鈕。感興趣的童鞋可以去我的 Github 查看源碼祈餐。

Segue

首先介紹一個(gè)名詞 - segue擂啥。簡短的翻譯就是:轉(zhuǎn)換。

我們創(chuàng)建了很多 Controller 的 Controller帆阳,我們需要用一個(gè) MVC 觸發(fā)另一個(gè) MVC哺壶,這種 MVC 之間的轉(zhuǎn)換就是 segue,一般不用 transition蜒谤,而用 segue山宾,之后會(huì)經(jīng)常遇到。

主要有4種 segue:

  • Show Segue(比如在 Navigation Controller 中鳍徽,一個(gè) MVC 出現(xiàn)在另一個(gè) MVC 里资锰。)
  • Show Detail Segue(比如我們這次的 Demo,一個(gè) master阶祭,一個(gè) detail绷杜,那么就需要用到這個(gè) show detail。Navigation Controller 中也可以使用 show detail segue濒募,和 show segue 功能一樣鞭盟。)
  • Modal Segue(占據(jù)整個(gè)屏幕)
  • Popover Segue(出現(xiàn)一個(gè)小彈窗)

Segue 會(huì)創(chuàng)建一個(gè)新的實(shí)例(后文也會(huì)繼續(xù)提到)。就是說每次點(diǎn)擊同樣的按鈕瑰剃,它返回的不是之前的那個(gè)界面齿诉,雖然長的也許一樣,但其實(shí)是一個(gè)全新的界面晌姚,是重新創(chuàng)建的粤剧。

必須要給新建的 segue 一個(gè) Identifier(在 Attributes inspector 中設(shè)置)。每個(gè) segue 都要有自己獨(dú)特的 id挥唠,因?yàn)槲覀円獙λM(jìn)行操作抵恋。比如我們可以使用 UIViewController 的方法,func performSegueWithIdentifier(identifier: String, sender: Anyobject?) 啟用 segue猛遍,但我們幾乎不用這種方法馋记,一般都在storyboard 中直接用 ctrl 拉線号坡。segue 的 id 更重要的一個(gè)用途是:preparing for a segue。用代碼來說就是:

func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // sender 一般是 button梯醒,但可以是任何東西
  if let identifier = segue.identifier { // 檢查是否 nil
    switch identifier {
      case "Show Graph":
        if let vc = segue.destinationViewController as? GraphController { // 如果非 nil宽堆,則作為 GraphController
          vc.property1 = ...
          vc.CallMethodToSetItUp(...)
        }
      default: break
    }
  }
}

在上面的這段代碼中,有個(gè) switch case 語句茸习,而我們在 Demo 中畜隶,充分發(fā)揮了 Swift 的特性,使用了字典号胚,使得我們的代碼更加優(yōu)雅(這也是我為什么喜歡 Swift 的原因籽慢,相比于 CPP)。

private let emotionalFaces: Dictionary<String,FacialExpression> = [

注意:這個(gè)準(zhǔn)備(preparation)的過程是在設(shè)置好 outlet 之前完成的猫胁,其實(shí)也挺符合實(shí)際箱亿,因?yàn)橄劝言摐?zhǔn)備的東西準(zhǔn)備好了,才能去設(shè)置再去呈現(xiàn)弃秆。但在實(shí)際開發(fā)過程中很容易出現(xiàn) bug届惋,不自覺的就會(huì)去使用未設(shè)置好的 optional 變量。文末會(huì)介紹這樣的 bug 以及相應(yīng)的處理對策菠赚。

我們也可以阻止 segue脑豹,只要在 UIViewController 中實(shí)現(xiàn)下面這個(gè)方法,返回 false衡查。

func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool

組合多個(gè) MVC

有3種方式可以將多個(gè) MVC 聯(lián)合起來瘩欺,

  1. Tab Bar Controller:
    每個(gè)界面應(yīng)該是相互獨(dú)立的,因?yàn)槿绻麅蓚€(gè)界面相互關(guān)聯(lián)拌牲,如上面提到的我們這次的 Demo俱饿,我們希望用戶點(diǎn)擊了某個(gè)相應(yīng)的事件按鈕才能看到下個(gè)界面,而不是直接點(diǎn)了和事件無關(guān)的 Tab Bar 就能看到下個(gè)界面们拙。所以我們的 Demo 不能用 Tab Bar Controller稍途。
  2. Split Controller:
    如我在 Swift 學(xué)習(xí)筆記3 - Gesture中有提到的阁吝, “對于一個(gè)split view來說砚婆,[0]是主體部分, [1]是細(xì)節(jié)部分”突勇。對于我們這次的 Demo装盯,帶有 “Angry”, "Worried" 等按鈕的界面可以是主體部分 (master),而那個(gè)呆萌的臉可以作為呈現(xiàn)細(xì)節(jié)的另一個(gè)view (detail)甲馋。 別忘記 Split Controller 是用于 ipad 的(以及6 plus)埂奈,那是因?yàn)?6 plus 以前的 iphone 屏幕不夠大,沒辦法 split定躏。要想在 6 里用 split账磺,我們還是需要借助下面的這個(gè)哥們芹敌,Navigation Controller.
  3. Navigation Controller:
    選中主體部分(master),然后 Editor -> Embed in -> Navigation Controller垮抗。Boom氏捞!It worked! 其實(shí)它就是多了個(gè) Back 按鈕冒版,這也是 Navigation Controller 精髓所在液茎。在之前的學(xué)習(xí)筆記中也說過,Navigation Controller 是通過 stack 的方式存放這些 view 的辞嗡,Back 就代表將現(xiàn)在的這個(gè) view 拋棄掉(是徹底拋棄哦捆等,重新點(diǎn)擊按鈕,它創(chuàng)建的就是一個(gè)新的界面续室,和之前的那個(gè)毛線關(guān)系沒有啦)栋烤,然后返回上一個(gè) view。但如果是 Tab Bar挺狰,它就是一直存在的班缎,也就是說點(diǎn)擊一個(gè) tab A,再點(diǎn) B她渴,再點(diǎn) A达址,它出現(xiàn)的還是原來的 A。用教授的話說就是 “tab bar 不是 segue趁耗,navigation 是 segue”沉唠。
  • 有一點(diǎn)需要注意的是,如果在一個(gè) Navigation Controller 中放有另一個(gè) Navigation Controller苛败,iOS 會(huì)自動(dòng)忽視里面的那個(gè) Navigation Controller满葛。
  • 當(dāng)我們想在 detail 的那個(gè)界面加個(gè)標(biāo)題,我們還需要在那個(gè) detail 上 embed 一個(gè) Navigation Controller罢屈,但是這樣的話在 ipad 上運(yùn)行那些按鈕就沒用了嘀韧,因?yàn)?prepare 的是 UINavigationController, 而我們想要 prepare 的是 UINavigationController 里面的內(nèi)容,也就是我們之前做好的 detail view 里的東西缠捌。所以我們還需要加上這段代碼锄贷,也就是 prepare UINavigationController 里面的內(nèi)容的。
if let navcon = destinationvc as? UINavigationController {
            destinationvc = navcon.visibleViewController ?? destinationvc
        }

View Controller 生命周期

生命周期大概是這樣的:

  • Creation. 一般在 storyboard 中創(chuàng)建實(shí)例曼月。
  • Preparation if being segued to.
    如之前所說谊却,這個(gè)準(zhǔn)備過程在 Outlet Setting 之前完成。
  • Outlet Setting.
  • Appearing and disappearing.
  • Geometry changes.
  • Low-memory situation. 一般在現(xiàn)在的 iphone 中很少發(fā)生哑芹。但要是發(fā)生了炎辨,可以釋放好久不用的占用很大空間的事件,比如最長未使用原則(LRU)聪姿。

在 storyboard 中創(chuàng)建實(shí)例碴萧,設(shè)置了 outlet 之后乙嘀,就是調(diào)用 viewDidLoad 方法了。viewDidLoad, viewWillLoad, viewWillAppear, viewWillDisappear, viewDidAppear, viewDidDisappear 等等這么多方法破喻,我會(huì)在下面逐條整理乒躺。

  • viewDidLoad

    view 只會(huì) load 一次,一般在這里在這里進(jìn)行一些初始化的工作低缩,我們一般不用 init 方法嘉冒,因?yàn)榇藭r(shí) outlet 已經(jīng)設(shè)置完畢了。我們一般也將 update UI 的工作放在這個(gè)方法里咆繁。但是 view 的 geometry 不在這里設(shè)置讳推,因?yàn)檫€不知道使用的設(shè)備是什么。這里的 geometry 的意思是 view 的大小尺寸玩般,橫向還是縱向之類的银觅。
override func viewDidLoad() {
 super.viewDidLoad() 
 // 進(jìn)行一些 MVC 初始化的工作
}
  • viewWillAppear

    這是 view 即將呈現(xiàn)出來之前的方法,一般把需要大量運(yùn)算的程序放在這里(比如多線程的操作)坏为,view 的 geometry 也是在這里設(shè)置的究驴,但是如果要做旋轉(zhuǎn)之類的操作,其它地方會(huì)響應(yīng)這些操作匀伏。
override func viewDidLoad() {
 super.viewDidLoad() 
 // 進(jìn)行一些 MVC 初始化的工作
}
  • viewDidAppear

    這是在 view 呈現(xiàn)出來之后的方法洒忧,一些動(dòng)畫在這里進(jìn)行。
func viewDidAppear(animated: Bool)
  • viewWillDisappear

    這是 view 即將消失之前的方法够颠,主要做一些簡單的清理工作熙侍,但一些非常耗時(shí)的工作不是在這里進(jìn)行的,這里可以關(guān)閉動(dòng)畫履磨。
override func viewWillDisappear(animated: Bool) {
 super.viewWillDisappear(animated) // 在所有的 viewWill/Did 方法中蛉抓,調(diào)用 super。
 // 進(jìn)行一些 MVC 初始化的工作
}
  • viewDidDisappear

    這是在 view 消失之后的方法剃诅,釋放一些在 willappear 階段從網(wǎng)絡(luò)上拿來的數(shù)據(jù)巷送。
func viewDidDisappear(animated: Bool)
  • func viewWillLayoutSubviews()func viewDidLayoutSubviews(),這兩個(gè)方法用于幾何變換的時(shí)候(geometry)矛辕,而我們在 storyboard 中相對應(yīng)于那些藍(lán)線設(shè)置的 constraints笑跛,是在這兩個(gè)方法之間發(fā)生的。這里其實(shí)我們不需要做什么特殊操作如筛,因?yàn)槎际亲詣?dòng)完成的堡牡。
  • viewWillTransitionToSize

    在做旋轉(zhuǎn)變換的時(shí)候抒抬,就是將手機(jī)或者 pad 屏幕橫過來的時(shí)候杨刨,我們可以在這里設(shè)置一些動(dòng)畫屬性。
func viewWillTransitionToSize() {
  size: CGSize,
  withTransitionCoordinator: UIViewControllerTransitionCoordinator
}
  • awakeFromNib

    在 preparation 和 outlet set 之前(在 MVC load 之前)擦剑。這個(gè)會(huì)發(fā)給任何對象妖胀,不只是 ViewController芥颈。

概括這個(gè)周期,就是:

  • Instantiated (一般從 storyboard 中創(chuàng)建實(shí)例)
  • awakeFromNib
  • segue preparation
  • outlet set
  • viewDidLoad

    以下這些會(huì)經(jīng)常調(diào)用赚抡,比如每次顯示和關(guān)閉某個(gè) view 的時(shí)候
  • viewWillAppear & viewDidAppear
  • viewWillDisappear & viewDidDisappear

    以下幾何變換的方法有可能在 viewDidLoad 之后的任何時(shí)候被調(diào)用
  • viewWillLayoutSubviews
  • 自動(dòng)部署布局
  • viewDidLayoutSubviews
  • didReceiveMemoryWarning (內(nèi)存不夠的時(shí)候)

Demo 中的 bug 和對策

有個(gè) bug 在 iOS 開發(fā)中應(yīng)該會(huì)經(jīng)常遇到爬坑,就是

fatal error: unexpectedly found nil while unwrapping an Optional value

如 Paul Hegarty 教授所講,

Your outlets are not set at the time you preparing

這是因?yàn)橛袀€(gè) optional 我們沒有處理涂臣,比如在這個(gè) demo 中盾计,faceView 是 optional 的,faceView 是這樣來的:

@IBOutlet weak var faceView: FaceView!

所以在后面 update faceView 的時(shí)候赁遗,需要考慮它 nil 的情況署辉,有兩種措施:

  • 第一種方法,以其中一條語句為例
faceView?.eyeBrowTilt = eyeBrowTilts[expression.eyeBrowns] ?? 0.0

注意等號左邊的問號 faceView?岩四,它可以處理 optional nil 的問題哭尝,如果式子的任何一個(gè)地方為 nil,那么它就會(huì) ignore 整條語句剖煌。但是這樣的語句很多材鹦,我們不能每條都這樣處理。僅管我們是碼農(nóng)程序猿耕姊,我們也要學(xué)會(huì)優(yōu)雅桶唐。:)

  • 另一個(gè)方法就是加上 if faceView != nil { },這樣就避免了 nil 問題了茉兰。

by:諸葛俊偉
歡迎轉(zhuǎn)載莽红,轉(zhuǎn)載請注明出處。訪問我的個(gè)人主頁邦邦,了解更多安吁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市燃辖,隨后出現(xiàn)的幾起案子鬼店,更是在濱河造成了極大的恐慌,老刑警劉巖黔龟,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妇智,死亡現(xiàn)場離奇詭異,居然都是意外死亡氏身,警方通過查閱死者的電腦和手機(jī)巍棱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門捣炬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虽另,“玉大人,你說我怎么就攤上這事厂置∠莼ⅲ” “怎么了到踏?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵杠袱,是天一觀的道長。 經(jīng)常有香客問我窝稿,道長楣富,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任伴榔,我火速辦了婚禮纹蝴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘踪少。我一直安慰自己骗灶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布秉馏。 她就那樣靜靜地躺著耙旦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萝究。 梳的紋絲不亂的頭發(fā)上免都,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音帆竹,去河邊找鬼绕娘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛栽连,可吹牛的內(nèi)容都是我干的险领。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼秒紧,長吁一口氣:“原來是場噩夢啊……” “哼绢陌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熔恢,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤脐湾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后叙淌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秤掌,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年鹰霍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闻鉴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茂洒,死狀恐怖孟岛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤蚀苛,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布在验,位于F島的核電站玷氏,受9級特大地震影響堵未,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盏触,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一渗蟹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赞辩,春花似錦雌芽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至糟需,卻和暖如春屉佳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洲押。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工武花, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杈帐。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓体箕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挑童。 傳聞我的和親對象是個(gè)殘疾皇子累铅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容