當前位置:
首頁 > 最新 > iOS 實現一個容器視圖控制器

iOS 實現一個容器視圖控制器

一直以來想寫一個抽屜效果,看了一些文章後發現並不是那麼簡單,網上的一些抽屜效果不是很嚴謹。看了下MMDrawerController的源碼,等於定製了一個Container View Controller。(類似於系統的UINavigationController以及UITabbarController);

比如下面幾個方法就是MMDrawerController實現的:

WX20171225-105701.png

容器視圖控制器是將來自多個視圖控制器的內容合併到單個用戶界面中的一種方法。容器視圖控制器通常用於促進導航,並基於現有內容創建新的用戶界面類型。UIKit中的容器視圖控制器的例子包括UINavigationController,UITabBarController和UISplitViewController,所有這些都方便您的用戶界面的不同部分之間的導航。

設計自定義容器視圖控制器

幾乎在任何情況下,容器視圖控制器都像其他任何內容視圖控制器一樣管理根視圖和一些內容。區別在於容器視圖控制器從其他視圖控制器獲取其內容的一部分。它獲取的內容僅限於其他視圖控制器的視圖,它嵌入在其自己的視圖層次結構中。容器視圖控制器設置任何嵌入視圖的大小和位置,但原始視圖控制器仍然管理這些視圖內的內容。

在設計自己的容器視圖控制器時,請始終了解容器和包含的視圖控制器之間的關係。視圖控制器的關係可以幫助告知他們的內容應該如何顯示在屏幕上,以及你的容器如何在內部管理它們。在設計過程中,問自己以下問題:

1 .容器的作用是什麼?它的孩子扮演什麼樣的角色?

2.多少個孩子同時顯示?

3.兄弟視圖控制器之間有什麼關係(如果有的話)?

4.子視圖控制器如何添加到容器或從容器中移除?

5.孩子的大小或位置能改變嗎?這些變化在什麼情況下發生?

6.容器是否提供任何裝飾或導航相關的視圖?

7.集裝箱和子公司之間需要什麼樣的溝通?容器是否需要向小孩報告特定事件,而不是由UIViewController班級定義的標準事件?

8.容器的外觀是否可以用不同的方式配置?如果是這樣,怎麼樣?

在定義了各種對象的角色之後,容器視圖控制器的實現相對簡單。UIKit唯一的要求就是在容器視圖控制器和任何子視圖控制器之間建立正式的父子關係。親子關係確保孩子們收到任何相關的系統消息。除此之外,大部分實際工作都發生在包含視圖的布局和管理過程中,對於每個容器來說都是不同的。您可以將視圖放置在容器的內容區域的任何位置,然後根據需要調整視圖的大小。您還可以將自定義視圖添加到視圖層次結構中,以提供修飾或輔助導航。

示例:導航控制器

UINavigationController對象通過分層數據集支持導航。導航界面一次顯示一個子視圖控制器。界面頂部的導航欄顯示數據層次結構中的當前位置,並顯示後退按鈕以向後移動一個級別。向下導航到數據層次結構留給子視圖控制器,可以涉及使用表或按鈕。

視圖控制器之間的導航由導航控制器及其子節點聯合管理。當用戶與子視圖控制器的按鈕或表格行交互時,孩子要求導航控制器將新的視圖控制器推入視圖。孩子處理新的視圖控制器的內容的配置,但導航控制器管理過渡動畫。導航控制器還管理導航欄,該導航欄顯示關閉最上面的視圖控制器的後退按鈕。

圖5-1顯示了導航控制器及其視圖的結構。大多數內容區域由最頂層的子視圖控制器填充,只有一小部分被導航欄佔用。

VCPG_structure-of-navigation-interface_5-1_2x.png

在緊湊和常規的環境中,導航控制器一次只顯示一個子視圖控制器。導航控制器調整其子以適應可用空間。

示例:分割視圖控制器

一個UISplitViewController對象以主 - 細節布局顯示兩個視圖控制器的內容。在這種安排中,一個視圖控制器(主視圖)的內容決定了其他視圖控制器顯示的細節。兩個視圖控制器的可見性是可配置的,但也受當前環境的支配。在規則的水平環境中,分割視圖控制器可以同時顯示兩個子視圖控制器,也可以隱藏主控並根據需要顯示。在緊湊的環境中,分割視圖控制器一次只顯示一個視圖控制器。

圖5-2顯示了在一個常規的水平環境中的分割視圖界面及其視圖的結構。分割視圖控制器本身只有默認的容器視圖。在這個例子中,兩個子視圖是並排顯示的。子視圖的大小是可配置的,主視圖的可見性也是可配置的。

VCPG-split-view-inerface_5-2_2x.png

在界面構建器中配置容器

要在設計時創建父子容器關係,請將容器視圖對象添加到故事板場景中,如圖5-3所示。容器視圖對象是代表子視圖控制器內容的佔位符對象。使用該視圖來調整和定位與容器中其他視圖相關的子視圖。

container_view_embed_2x.png

當您使用一個或多個容器視圖載入視圖控制器時,Interface Builder還會載入與這些視圖關聯的子視圖控制器。孩子必須與父母同時實例化,以便建立適當的親子關係。

如果您不使用Interface Builder設置父 - 子容器關係,則必須通過將每個子項添加到容器視圖控制器來以編程方式創建這些關係,如將子視圖控制器添加到您的內容中所述。

實現自定義容器視圖控制器

要實現一個容器視圖控制器,你必須建立你的視圖控制器和它的子視圖控制器之間的關係。在嘗試管理任何子視圖控制器的視圖之前,建立這些父子關係是必需的。這樣做讓UIKit知道你的視圖控制器正在管理孩子的大小和位置。您可以在Interface Builder中創建這些關係,或以編程方式創建它們。以編程方式創建父子關係時,您明確地添加和刪除子視圖控制器作為視圖控制器設置的一部分。

將子視圖控制器添加到您的內容

要以編程方式將子視圖控制器合併到內容中,請執行以下操作,在相關的視圖控制器之間創建父子關係:

調用addChildViewController:你的容器視圖控制器的方法。

這個方法告訴UIKit你的容器視圖控制器現在正在管理子視圖控制器的視圖。

將孩子的根視圖添加到容器的視圖層次結構中。

一定要記住設置孩子框架的大小和位置,作為這個過程的一部分。

添加任何約束來管理子視圖的大小和位置。

調用didMoveToParentViewController:子視圖控制器的方法。

清單5-1展示了一個容器如何在其容器中嵌入一個子視圖控制器。建立父子關係後,容器設置其子的框架,並將子視圖添加到自己的視圖層次結構中。設置子視圖的框架大小很重要,並確保視圖在容器中正確顯示。在添加視圖後,容器調用didMoveToParentViewController:子視圖的方法,使子視圖控制器有機會響應視圖所有權的更改。

清單5-1將一個子視圖控制器添加到一個容器

- (void) displayContentController: (UIViewController*) content {

[self addChildViewController:content];

[self.view addSubview:self.currentClientView];

[content didMoveToParentViewController:self];

}

在前面的例子中,注意你只調用didMoveToParentViewController:子方法。那是因為該方法為你addChildViewController:調用孩子的willMoveToParentViewController:方法。您必須didMoveToParentViewController:自己調用方法的原因是,只有在將子視圖嵌入到容器的視圖層次結構中之後,方法才能被調用。

使用自動布局時,在將子對象添加到容器的視圖層次結構後,在容器和子對象之間設置約束。你的約束應該隻影響孩子的根視圖的大小和位置。請勿更改子視圖層次結構中的根視圖或任何其他視圖的內容。

刪除子視圖控制器

要從內容中刪除子視圖控制器,請通過執行以下操作來刪除視圖控制器之間的父子關係:

willMoveToParentViewController:用值 調用孩子的方法nil。

刪除您使用該子視圖的配置的任何約束。

從容器的視圖層次結構中移除孩子的根視圖。

調用孩子的removeFromParentViewController方法來完成親子關係的結束。

刪除子視圖控制器會永久切斷父級和子級之間的關係。只有當您不再需要引用子視圖控制器時,才能移除子視圖控制器。例如,當新導航控制器被推入導航堆棧時,導航控制器不會移除其當前的子視圖控制器。只有當它們從堆棧中彈出時才會被刪除。

清單5-2顯示了如何從容器中刪除子視圖控制器。willMoveToParentViewController:使用該值調用該方法nil使子視圖控制器有機會為更改做準備。該removeFromParentViewController方法還調用孩子的didMoveToParentViewController:方法,傳遞該方法的值nil。設置父視圖控制器以nil完成從容器中刪除子視圖。

清單5-2從容器中刪除一個子視圖控制器

- (void) hideContentController: (UIViewController*) content {

[content willMoveToParentViewController:nil];

[content.view removeFromSuperview];

[content removeFromParentViewController];

}

子視圖控制器之間的過渡

當您想要用另一個子視圖控制器替換動畫時,將子視圖控制器的添加和刪除合併到轉換動畫過程中。在動畫之前,確保兩個子視圖控制器都是你的內容的一部分,但讓當前的孩子知道它即將消失。在您的動畫中,將新的孩子的視圖移動到位並移除舊的孩子的視圖。完成動畫後,完成子視圖控制器的移除。

清單5-3顯示了如何使用過渡動畫將一個子視圖控制器交換為另一個子視圖控制器的示例。在這個例子中,新的視圖控制器被動畫為現有的子視圖控制器當前佔用的矩形,該控制器被移出屏幕。動畫完成後,完成塊從容器中刪除子視圖控制器。在這個例子中,該transitionFromViewController:toViewController:duration:options:animations:completion:方法自動更新容器的視圖層次,所以你不需要自己添加和刪除視圖。

清單5-3兩個子視圖控制器之間的轉換

- (void)cycleFromViewController: (UIViewController*) oldVC

toViewController: (UIViewController*) newVC {

// Prepare the two view controllers for the change.

[oldVC willMoveToParentViewController:nil];

[self addChildViewController:newVC];

// Get the start frame of the new view controller and the end frame

// for the old view controller. Both rectangles are offscreen.

newVC.view.frame = [self newViewStartFrame];

CGRect endFrame = [self oldViewEndFrame];

// Queue up the transition animation.

[self transitionFromViewController: oldVC toViewController: newVC

duration: 0.25 options:0

animations:^{

// Animate the views to their final positions.

newVC.view.frame = oldVC.view.frame;

oldVC.view.frame = endFrame;

}

completion:^(BOOL finished) {

// Remove the old view controller and send the final

// notification to the new view controller.

[oldVC removeFromParentViewController];

[newVC didMoveToParentViewController:self];

}];

}

管理兒童的外觀更新

在將容器添加到容器後,容器會自動將外觀相關的消息轉發給子容器。這通常是您想要的行為,因為它確保所有事件都能正確發送。但是,有時默認行為可能會以對您的容器無意義的順序發送這些事件。例如,如果多個孩子同時改變其視圖狀態,則可能需要合併這些更改,以使外觀回調都以更合理的順序同時發生。

為了接管外觀回調的責任,重寫shouldAutomaticallyForwardAppearanceMethods容器視圖控制器中的方法並返回NO,如清單5-4所示。返回NO讓UIKit知道你的容器視圖控制器通知其子的外觀變化。

清單5-4禁用自動外觀轉發

- (BOOL) shouldAutomaticallyForwardAppearanceMethods {

return NO;

}

當出現外觀轉換時,根據需要調用子對象beginAppearanceTransition:animated:或endAppearanceTransition方法。例如,如果您的容器有一個由child屬性引用的單個子項,那麼您的容器會將這些消息轉發給子項,如清單5-5所示。

清單5-5當容器出現或消失時轉發外觀消息

-(void) viewWillAppear:(BOOL)animated {

[self.child beginAppearanceTransition: YES animated: animated];

}

-(void) viewDidAppear:(BOOL)animated {

[self.child endAppearanceTransition];

}

-(void) viewWillDisappear:(BOOL)animated {

[self.child beginAppearanceTransition: NO animated: animated];

}

-(void) viewDidDisappear:(BOOL)animated {

[self.child endAppearanceTransition];

}

建立一個容器視圖控制器的建議

設計,開發和測試新的容器視圖控制器需要時間。雖然個人行為是直截了當的,但整個控制者可能相當複雜。在實現自己的容器類時,請考慮以下提示:

只訪問子視圖控制器的根視圖。一個容器只能訪問每個孩子的根視圖,也就是孩子view屬性返回的視圖。它不應該訪問任何孩子的其他意見。

子視圖控制器應該對其容器有最少的了解。子視圖控制器應該關注自己的內容。如果容器允許其行為受到孩子的影響,則應該使用委託設計模式來管理這些交互。

首先使用常規視圖設計您的容器。使用常規視圖(而不是來自子視圖控制器的視圖)使您有機會在簡化的環境中測試布局約束和動畫過渡。當常規視圖按預期工作時,將它們交換為您的子視圖控制器的視圖。

將控制委派給子視圖控制器

容器視圖控制器可以將其自身外觀的某些方面委託給其一個或多個子級。您可以通過以下方式委託控制:

讓一個子視圖控制器確定狀態欄的樣式。要委派狀態欄外觀小孩,覆蓋的一個或兩個childViewControllerForStatusBarStyle,並childViewControllerForStatusBarHidden在你的容器視圖控制器的方法。

讓孩子指定自己喜歡的尺寸。具有靈活布局的容器可以使用孩子自己的preferredContentSize財產來幫助確定孩子的大小。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Cocoa開發者社區 的精彩文章:

仿QQ錄音以及振幅動畫實現
iOS感測器:使用陀螺儀完成一個小球撞壁的小遊戲
西二旗程序員殺人事件
iMac Pro 就是一台「怪獸」,但不適合所有人
iOS圖片載入策略的簡單實現

TAG:Cocoa開發者社區 |