什么是 UIViewController
UIViewController: 使用數(shù)據(jù)( Model )來構(gòu)造視圖( View )的基本控制單元( Controller )
UIViewController間的關(guān)系
- 并列關(guān)系
- 容器包含關(guān)系
- 系統(tǒng)自帶容器控制器
- UINavigationController — 導(dǎo)航控制器
- UITabBarController — 標簽控制器
- 自定義容器控制器
- 系統(tǒng)自帶容器控制器
容器控制器
// 獲取 ViewController 的父控制器
.parentViewController
// 判斷 ViewController 的視圖是否加載
.viewLoaded
自定義容器控制器
操作 | 通知系統(tǒng)包含關(guān)系變更 | 調(diào)整視圖關(guān)系 | 更改ViewController包含關(guān)系 | 調(diào)整視圖關(guān)系 | 通知系統(tǒng)包含關(guān)系變更 |
---|---|---|---|---|---|
ViewController加入一個容器 | |- addChildViewController | - addSubview | - didMoveToParentViewController | ||
ViewController移除一個容器 | - willMoveToParentViewController | - removeFromSuperview | - removeFromParentViewController | \ |
- (void)addChildVc:(UIViewController*)vc view:(UIView *)view
{
//
BOOL needAddToParent = !vc.parentViewController;
//
if (needAddToParent) [self addChildViewController:vc];
vc.view.frame = view.bounds;
//
[view addSubview:vc.view];
//
if (needAddToParent) [vc didMoveToParentViewController:self];
}
- (void)removeChildVc:(UIViewController*)vc
{
//
[vc willMoveToParentViewController:nil];
//
if (![vc isViewLoaded]) {
//
[vc removeFromParentViewController];
}
else {
//
[vc.view removeFromSuperview];
//
[vc removeFromParentViewController];
}
}
UIViewController 的狀態(tài)和轉(zhuǎn)場
狀態(tài)變化圖:
狀態(tài)和對應(yīng)方法:
狀態(tài) | 方法 |
---|---|
初始化 | - alloc init |
啟動界面初始化 | — loadView |
加載中 | — viewDidLoad |
啟動即將完畢 | — viewWillAppear:animated: |
啟動完畢 | — viewDidAppear:animated: |
- loadView —- 初始化UIViewController View
UIViewController缺省持有屬于自身的view
self.view 的設(shè)置采用懶加載的方式,即調(diào)用 self.view 時才會調(diào)用 loadView 方法
// self.view的設(shè)置
- (void)loadView
{
//po self->_view 輸出為nil
NSLog(@"Before Load View");
[super loadView];
//po self->_view 輸出不為nil
NSLog(@"After Load View");
}
- viewDidLoad — view 加載完成
處理和view相關(guān)的功能 - viewWillAppear & viewDidAppear
- viewWillDisappear & viewDidDisappear
UIViewController 轉(zhuǎn)場
轉(zhuǎn)場 —— UIViewController 間的展示切換
系統(tǒng)默認轉(zhuǎn)場 —— Push / Modal
[self.navigationController pushViewController:animated:];
[self.navigationController popViewController:animated:];
[self presentViewController:animated:completion:];
[self dismissViewControllerAnimated:completion:];
狀態(tài)和轉(zhuǎn)場的關(guān)系
- 有動畫轉(zhuǎn)場磨隘、無動畫轉(zhuǎn)場
有動畫轉(zhuǎn)場 viewWillAppear 和 viewDidAppear 有時間差蝶锋,轉(zhuǎn)場完畢后才 viewDidAppear臭猜。無動畫轉(zhuǎn)場有順序但是中間幾乎沒有時間差 - 手勢交互完成轉(zhuǎn)場割择、手勢交互未完成轉(zhuǎn)場
手勢拖動時 viewWillAppear 穴翩,等到完全完成轉(zhuǎn)場 viewDidAppear探赫,如果手勢未完成則不會調(diào)用 viewDidAppear
UINavigationController
UINavigationController 是系統(tǒng)的 Push / Pop 轉(zhuǎn)場控件厦画,為棧結(jié)構(gòu)( FIFO )
// 棧結(jié)構(gòu)中的所有 viewController
.viewControllers
// 處于棧頂?shù)?viewController
.topViewController
UINavigationController 有 .navigationBar 和 .toolbar (默認不顯示牍陌,顯示:[self setToolbarHidden:NO animated:NO])擎浴。navigationBar 為 UINavigationBar 對象(繼承于 UIView),.items 屬性也是棧結(jié)構(gòu)毒涧,保存各個 viewController 對應(yīng)的 UINavigationItem 對象( .navigationItem )贮预,UINavigationItem 不是一個 view ,而是一個 model
UIViewController 對象在 UINavigationController 中布局的設(shè)置
UIViewController 有 .edgesForExtendedLayout 屬性契讲,類型為 UIRectEdge 枚舉仿吞,枚舉值為:
UIRectEdgeNone / UIRectEdgeTop / UIRectEdgeBottom / UIRectEdgeLeft / UIRectEdgeRight / UIRectEdgeAll
- 全屏布局
UINavigationController 的 view 有多大則 UIViewController 的 view 就有多大(默認, UIRectEdgeAll == UIRectEdgeTop | UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight )
UIViewController 有 .edgesForExtendedLayout 屬性,類型為 UIRectEdge 枚舉捡偏,枚舉值為:
UIRectEdgeNone / UIRectEdgeTop / UIRectEdgeBottom / UIRectEdgeLeft / UIRectEdgeRight / UIRectEdgeAll 唤冈,全屏決定 UIViewController 的 view 的范圍 - 延伸到不透明的bar
UIViewController 有 .extendedLayoutIncludesOpaqueBars 屬性,類型為 BOOL 值(默認為 NO )银伟,決定了 UIViewController 的 view 是否延伸到不透明的 bar
// 全延伸
self.edgesForExtendedLayout = UIRectEdgeAll;
// 不延伸到不透明 bar
self.extendedLayoutIncludesOpaqueBars = NO;
// 設(shè)置為不透明
self.navigationController.navigationBar.translucent = NO;
// 這種情況下即使 self.edgesForExtendedLayout 為 ALL 也不做延伸布局
// 為 YES 則可強制在不透明的情況下也布局上去
//self.extendedLayoutIncludesOpaqueBars = YES;
自定義導(dǎo)航欄樣式
UINavigationBar 自定義樣式屬性
// 背景圖
backgroundImage
// 背景色
barTintColor
// 毛玻璃透明
translucent
// 陰影分割線你虹,改分割線之前要先把背景圖替換掉,不然沒效果
shadowImage
// 設(shè)置切掉邊界則不會顯示 shadowImage
clipToBounds
// 標題樣式
titleTextAttributes
// 標題位置
titleVerticalPosition
// 返回按鈕顏色
tintColor
// backIndicatorImage 和 backIndicatorTransitionMaskImage需要同時設(shè)置
// 返回按鈕圖片
backIndicatorImage
// 返回按鈕圖片(在轉(zhuǎn)場中使用)
backIndicatorTransitionMaskImage
// 隱藏導(dǎo)航欄
navigationBarHidden
// 隱藏導(dǎo)航欄事件
// 滑動隱藏
self.navigationController.hidesBarsOnSwipe = YES;
// 點擊屏幕隱藏
self.navigationController.hidesBarsOnTap = YES;
// 彈出鍵盤隱藏
self.navigationController.hidesBarsWhenKeyboardAppears = YES;
- 狀態(tài)欄顏色和導(dǎo)航欄顏色分不開 —— 當我們設(shè)置背景色時彤避,狀態(tài)欄( status bar )的顏色也一并被修改
更改狀態(tài)欄文字顏色的方法 :- 全局統(tǒng)一設(shè)置
// 已廢棄
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
同時在info.plist 中添加 View controller-based status bar 設(shè)置值為 NO
- 通過 ViewController 設(shè)置
// ViewController和NavigationController 都要設(shè)置
- (UIStatusBarStyle)preferredStatusBarStyle;
- UINavigationBar 的全局設(shè)置
[UINavigationBar appearance] 可獲取 UINavigationBar 實例傅物,對這個實例進行的修改是全局的
UINavigationItem 樣式定義
UINavigationBar 作為 View,需要一個自定義的 Model( UINavigationItem ) 琉预,通過 Model 的配置董饰,展示自定義的界面
UINavigationItem 的屬性有:
// 如果設(shè)置了 titleView ,則 title 不起作用
.title
.titleView
// 類型為 UIBarButtonItem
.backBarButtonItem
.leftBarButtonItem \ .rightBarButtonItem
.leftBarButtonItems \ .rightBarButtonItems
UIBarButtonItem ,自定義 Button 的 Model 卒暂,可以:自定義寬度贮缅、加入自定義視圖、響應(yīng)自定義行為
- initWithImage:style:target:action:
- initWithBarButtonSystemItem:target:action:
- initWithTitle:style:target:action:
- initWithCustomView:
// 調(diào)整返回按鈕文字的位置
- setBackButtonTitlePositionAdjustment:forBarMetrics:
// 占位用介却,固定寬度谴供,可用于排版
// 一般情況下 UIBarButtonItem 在 UINavigationBar 的位置或間距固定,可用一個空白的 UIBarButtonItem 來調(diào)整位置
UIBarButtonItem *negSpaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negSpaceItem.width = -10;
- UIBarButtonItem 的全局設(shè)置
[UIBarButtonItem appearance] 可獲取 UIBarButtonItem 實例齿坷,對這個實例進行的修改是全局的
POP 的手勢的失效
iOS7 以后 UINavigationController 有一個側(cè)滑 POP 的手勢( .interactivePopGestureRecognizer )桂肌,手指在屏幕邊緣滑動,系統(tǒng)會判斷手指拖動出來的大小來決定是否要執(zhí)行控制器的Pop操作永淌。導(dǎo)致這個手勢失效的方式有:
- 自定義返回按鈕
- navigationBarHidden
- navigationItem.hidesBackButton
.interactivePopGestureRecognizer 為 UIGestureRecognizer 類型崎场,UIGestureRecognizer 有屬性 .delegate ,可指向一個實現(xiàn)了 <UIGestureRecognizerDelegate> 協(xié)議的實例
viewController 只需要將 UINavagationController 的 interactivePopGestureRecognizer 指向自己遂蛀,即不會導(dǎo)致 POP 手勢失效
// 在 viewDidAppear 中讓 delegate 指向自己
self.originalDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
// 在 viewWillDisappear 中讓 delegate 指向原實例谭跨,才會不丟失
self.navigationController.interactivePopGestureRecognizer.delegate = self.originalDelegate;
self.originalDelegate = nil;
<UINavigationControllerDelegate>
- navigationController:willShowViewController:animated:
- navigationController:didShowViewController:animated:
\\ 導(dǎo)航統(tǒng)計需求
@interface NavStatistic ()
@property (nonatomic, assign) NSInteger currentCount;
@property (nonatomic, weak) UIViewController *currentPage;
@property (nonatomic, assign) NSTimeInterval currentShowTime;
@end
@implementation NavStatistic
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
BOOL isPush = NO;
if (navigationController.viewControllers.count > self.currentCount) {
isPush = YES;
}
if (isPush) {
if (self.currentPage) {
NSLog(@"首次展示頁面:%@ 來自 %@", NSStringFromClass([viewController class]), NSStringFromClass([self.currentPage class]));
}
else {
NSLog(@"首次展示頁面:%@", NSStringFromClass([viewController class]));
}
}
if (self.currentPage) {
NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
NSTimeInterval duration = currentTime - self.currentShowTime;
NSLog(@"頁面 %@ 展示時長 %f", NSStringFromClass([self.currentPage class]), duration);
}
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
self.currentCount = [navigationController.viewControllers count];
self.currentPage = viewController;
self.currentShowTime = [[NSDate date] timeIntervalSince1970];
}
UITabBarController
.viewControllers
selectedViewController
selectedIndex
UITabBarController 的 viewControllers 數(shù)組大于5個標簽頁面則從第5個開始收起來
UITabBar 自定義樣式屬性
// 背景圖
.backgroundImage
// 背景色
.batTintColor
// 毛玻璃透明
.translucent
// 陰影分割線
.shadowImage
// item寬度和間距只可以在 iPad 上設(shè)置
// item寬度
.itemWidth
// item間距
.itemSpace
// 選中的顏色
.tintColor
// 選中的標識圖片
.selectionIndicatorImage
// Bar的樣式
.barStyle
UITabBarItem 樣式定義
UITabBar 作為 View,需要一個自定義的 Model( UITabBarItem ) 李滴,通過 Model 的配置螃宙,展示自定義的界面
- initWithTitle:image:selectedImage:
.title
.image
.selectedImage
.badgeValue
.titlePositionAdjustment
- setTitleTextAttributes:forState:
自定義 Tab 的行為
UITabBarItem 并沒有提供自定義的 Action ,UITabBarController 有一個 .delegate 所坯,對象需實現(xiàn) <UITabBarControllerDelegate> 協(xié)議
- tabBarController:shouldSelectViewController:
- tabBarController:didSelectViewController:
通過這兩個方法谆扎,我們可以自定義 Tab 的行為
標簽頁是 UINavigationController 情況下 Push 后 TabBar 隱藏
SecondViewController *vc = [[SecondViewController alloc] init];
vc.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:vc animated:YES];