View Controller 容器

在 iOS 5 之前涕滋,view controller 容器是 Apple 的特權(quán)睬辐。實際上,在 view controller 編程指南中還有一段申明宾肺,指出你不應該使用它們溯饵。Apple 對 view controllers 的總的建議曾經(jīng)是“一個 view controller 管理一個全屏幕的內(nèi)容”。這個建議后來被改為“一個 view controller 管理一個自包含的內(nèi)容單元”锨用。為什么 Apple 不想讓我們構(gòu)建自己的 tab bar controllers 和 navigation controllers丰刊?或者更確切地說,這段代碼有什么問題:

[viewControllerA.view addSubView:viewControllerB.view]

UIWindow 作為一個應用程序的根視圖(root view)增拥,是旋轉(zhuǎn)和初始布局消息等事件產(chǎn)生的來源啄巧。在上圖中,child view controller 的 view 插入到 root view controller 的視圖層級中掌栅,被排除在這些事件之外了秩仆。View 事件方法諸如viewWillAppear:將不會被調(diào)用喘鸟。

在 iOS 5 之前構(gòu)建自定義的 view controller 容器時任内,要保存一個 child view controller 的引用赂蠢,還要手動在 parent view controller 中轉(zhuǎn)發(fā)所有 view 事件方法的調(diào)用手趣,要做好非常困難导街。

一個例子

當你還是個孩子漓库,在沙灘上玩時吧黄,你父母是否告訴過你柬帕,如果不停地用鏟子挖枚钓,最后會到達美國铅搓?我父母就說過,我就做了個叫做Tunnel的 demo 程序來驗證這個說法搀捷。你可以 clone 這個Github 代碼庫并運行這個程序星掰,它有助于讓你更容易理解示例代碼多望。(劇透:從丹麥西部開始,挖穿地球氢烘,你會到達南太平洋的某個地方)

為了尋找對跖點怀偷,也稱作相反的坐標,將拿著鏟子的小孩四處移動播玖,地圖會告訴你對應的出口位置在哪里椎工。點擊雷達按鈕,地圖會翻轉(zhuǎn)過來顯示位置的名稱蜀踏。

屏幕上有兩個 map view controllers维蒙。每個都需要控制地圖的拖動,標注和更新果覆。翻過來會顯示兩個新的 view controllers颅痊,用來檢索地理位置。所有的 view controllers 都包含于一個 parent view controller 中局待,它持有它們的 views斑响,并保證正確的布局和旋轉(zhuǎn)行為。

Root view controller 有兩個 container views燎猛。添加它們是為了讓布局恋捆,以及 child view controllers 的 views 的動畫做起來更容易,我們馬上就可以看到重绷。

- (void)viewDidLoad{? ? [superviewDidLoad];//Setup controllers_startMapViewController = [RGMapViewController new];? ? [_startMapViewController setAnnotationImagePath:@"man"];? ? [selfaddChildViewController:_startMapViewController];//? 1[topContainer addSubview:_startMapViewController.view];//? 2[_startMapViewController didMoveToParentViewController:self];//? 3[_startMapViewController addObserver:selfforKeyPath:@"currentLocation"options:NSKeyValueObservingOptionNewcontext:NULL];? ? _startGeoViewController = [RGGeoInfoViewController new];//? 4}

我們實例化了_startMapViewController沸停,用來顯示起始位置,并設(shè)置了用于標注的圖像昭卓。

_startMapViewcontroller被添加成 root view controller 的一個 child愤钾。這會自動在 child 上調(diào)用willMoveToParentViewController:方法。

child 的 view 被添加成 container view 的 subview候醒。

child 被通知到它現(xiàn)在有一個 parent view controller能颁。

用來顯示地理位置的 child view controller 被實例化了,但是還沒有被插入到任何 view 或 controller 層級中倒淫。

布局

Root view controller 定義了兩個 container views伙菊,它決定了 child view controller 的大小。Child view controllers 不知道會被添加到哪個容器中敌土,因此必須適應大小镜硕。

- (void) loadView{? ? mapView = [MKMapViewnew];? ? mapView.autoresizingMask =UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;? ? [mapView setDelegate:self];? ? [mapView setMapType:MKMapTypeHybrid];self.view = mapView;}

現(xiàn)在,它們就會用 super view 的 bounds 來進行布局返干。這樣增加了 child view controller 的可復用性兴枯;如果我們把它 push 到 navigation controller 的棧中,它仍然會正確地布局矩欠。

過場動畫

Apple 已經(jīng)針對 view controller 容器做了細致的 API财剖,我們可以構(gòu)造我們能想到的任何容器場景的動畫悠夯。Apple 還提供了一個基于 block 的便利方法,來切換屏幕上的兩個 controller views躺坟。方法transitionFromViewController:toViewController:(...)已經(jīng)為我們考慮了很多細節(jié)沦补。

- (void) flipFromViewController:(UIViewController*) fromController? ? ? ? ? ? ? toViewController:(UIViewController*) toController? ? ? ? ? ? ? ? ? withDirection:(UIViewAnimationOptions) direction{? ? toController.view.frame = fromController.view.bounds;//? 1[selfaddChildViewController:toController];//[fromController willMoveToParentViewController:nil];//[selftransitionFromViewController:fromController? ? ? ? ? ? ? ? ? ? ? toViewController:toController? ? ? ? ? ? ? ? ? ? ? ? ? ? ? duration:0.2options:direction |UIViewAnimationOptionCurveEaseInanimations:nilcompletion:^(BOOLfinished) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [toController didMoveToParentViewController:self];//? 2[fromController removeFromParentViewController];//? 3}];}

在開始動畫之前,我們把toController作為一個 child 進行添加咪橙,并通知fromController它將被移除策彤。如果fromController的 view 是容器 view 層級的一部分,它的viewWillDisappear:方法就會被調(diào)用匣摘。

toController被告知它有一個新的 parent,并且適當?shù)?view 事件方法將被調(diào)用裹刮。

fromController被移除了音榜。

這個為 view controller 過場動畫而準備的便捷方法會自動把老的 view controller 換成新的 view controller。然而捧弃,如果你想實現(xiàn)自己的過場動畫赠叼,并且希望一次只顯示一個 view,你需要在老的 view 上調(diào)用removeFromSuperview违霞,并為新的 view 調(diào)用addSubview:嘴办。錯誤的調(diào)用次序通常會導致UIViewControllerHierarchyInconsistency警告。例如:在添加 view 之前調(diào)用didMoveToParentViewController:就觸發(fā)這個警告买鸽。

為了能使用UIViewAnimationOptionTransitionFlipFromTop動畫涧郊,我們必須把 children's view 添加到我們的 view containers 里面,而不是 root view controller 的 view眼五。否則動畫將導致整個 root view 都翻轉(zhuǎn)妆艘。

通信

View controllers 應該是可復用的、自包含的實體看幼。Child view controllers 也不能違背這個經(jīng)驗法則批旺。為了達到目的,parent view controller 應該只關(guān)心兩個任務(wù):布局 child view controller 的 root view诵姜,以及與 child view controller 暴露出來的 API 通信汽煮。它絕不應該去直接修改 child view tree 或其他內(nèi)部狀態(tài)。

Child view controller 應該包含管理它們自己的 view 樹的必要邏輯棚唆,而不是把它們看作單純呆板的 views暇赤。這樣,就有了更清晰的關(guān)注點分離和更好的可復用性瑟俭。

在示例程序 Tunnel 中翎卓,parent view controller 觀察了 map view controllers 上的一個叫currentLocation的屬性。

[_startMapViewController addObserver:selfforKeyPath:@"currentLocation"options:NSKeyValueObservingOptionNewcontext:NULL];

當這個屬性跟著拿著鏟子的小孩的移動而改變時摆寄,parent view controller 將新坐標的對跖點傳遞給另一個地圖:

[oppositeController updateAnnotationLocation:[newLocation antipode]];

類似地失暴,當你點擊雷達按鈕坯门,parent view controller 給新的 child view controllers 設(shè)置待檢索的坐標。

[_startGeoViewControllersetLocation:_startMapViewController.currentLocation];[_targetGeoViewControllersetLocation:_targetMapViewController.currentLocation];

我們想要達到的目標和你選擇的手段無關(guān)逗扒,從 child 到 parent view controller 消息傳遞的技術(shù)古戴,不論是采用 KVO,通知矩肩,或者是委托模式现恼,child view controller 都應該獨立和可復用。在我們的例子中黍檩,我們可以將某個 child view controller 推入到一個 navigation 棧中叉袍,它仍然能夠通過相同的 API 進行通信。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刽酱,一起剝皮案震驚了整個濱河市喳逛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棵里,老刑警劉巖润文,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異殿怜,居然都是意外死亡典蝌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門头谜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骏掀,“玉大人,你說我怎么就攤上這事乔夯∽┲” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵末荐,是天一觀的道長侧纯。 經(jīng)常有香客問我,道長甲脏,這世上最難降的妖魔是什么眶熬? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮块请,結(jié)果婚禮上娜氏,老公的妹妹穿的比我還像新娘。我一直安慰自己墩新,他們只是感情好贸弥,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著海渊,像睡著了一般绵疲。 火紅的嫁衣襯著肌膚如雪哲鸳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天盔憨,我揣著相機與錄音徙菠,去河邊找鬼。 笑死郁岩,一個胖子當著我的面吹牛婿奔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播问慎,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼萍摊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了如叼?” 一聲冷哼從身側(cè)響起记餐,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薇正,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體囚衔,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡挖腰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了练湿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猴仑。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肥哎,靈堂內(nèi)的尸體忽然破棺而出辽俗,到底是詐尸還是另有隱情,我是刑警寧澤篡诽,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布崖飘,位于F島的核電站,受9級特大地震影響杈女,放射性物質(zhì)發(fā)生泄漏朱浴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一达椰、第九天 我趴在偏房一處隱蔽的房頂上張望翰蠢。 院中可真熱鬧,春花似錦啰劲、人聲如沸梁沧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽廷支。三九已至频鉴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酥泞,已是汗流浹背砚殿。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芝囤,地道東北人似炎。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像悯姊,于是被迫代替她去往敵國和親羡藐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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

  • 介紹 objc.io objc.io 是關(guān)于 Objective-C 最佳實踐和先進技術(shù)的期刊悯许,歡迎來到第一期仆嗦! ...
    評評分分閱讀 1,685評論 5 24
  • /* UIViewController is a generic controller base class th...
    DanDanC閱讀 1,793評論 0 2
  • ViewsBecause view objects are the main way your applicati...
    梁光飛閱讀 593評論 0 0
  • 大家好,又和大家見面了先壕,之前做的幾個動畫Demo的文章被好多人轉(zhuǎn)載和關(guān)注瘩扼,感謝大家對我鼓勵,說實話垃僚,以前看別人的文...
    Neo_joke閱讀 5,213評論 4 31
  • 一個人住在新宿舍里 自己除了床墊什么也沒有 還好有小雨留下的床品 老大的枕頭 讓我可以安心的躺下 好溫暖
    Echoeer閱讀 219評論 0 0