UIViewController

什么是 UIViewController

UIViewController: 使用數(shù)據(jù)( Model )來構(gòu)造視圖( View )的基本控制單元( Controller )

UIViewController間的關(guān)系

  • 并列關(guān)系
  • 容器包含關(guān)系
    • 系統(tǒng)自帶容器控制器
      • UINavigationController — 導(dǎo)航控制器
      • UITabBarController — 標簽控制器
    • 自定義容器控制器

容器控制器

// 獲取 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)變化圖

狀態(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)欄文字顏色的方法 :
    1. 全局統(tǒng)一設(shè)置
// 已廢棄
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

同時在info.plist 中添加 View controller-based status bar 設(shè)置值為 NO

  1. 通過 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];

參考

Model-View-Controller
Customizing UINavigationBar

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市芹助,隨后出現(xiàn)的幾起案子堂湖,更是在濱河造成了極大的恐慌,老刑警劉巖状土,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件无蜂,死亡現(xiàn)場離奇詭異,居然都是意外死亡蒙谓,警方通過查閱死者的電腦和手機斥季,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彼乌,“玉大人泻肯,你說我怎么就攤上這事∥空眨” “怎么了灶挟?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毒租。 經(jīng)常有香客問我稚铣,道長箱叁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任惕医,我火速辦了婚禮耕漱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抬伺。我一直安慰自己螟够,他們只是感情好,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布峡钓。 她就那樣靜靜地躺著妓笙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪能岩。 梳的紋絲不亂的頭發(fā)上寞宫,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機與錄音拉鹃,去河邊找鬼辈赋。 笑死,一個胖子當著我的面吹牛膏燕,可吹牛的內(nèi)容都是我干的钥屈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼煌寇,長吁一口氣:“原來是場噩夢啊……” “哼焕蹄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起阀溶,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸦泳,沒想到半個月后银锻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡做鹰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年击纬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钾麸。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡更振,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饭尝,到底是詐尸還是另有隱情肯腕,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布钥平,位于F島的核電站实撒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜知态,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一捷兰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧负敏,春花似錦贡茅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至庶柿,卻和暖如春村怪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浮庐。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工甚负, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人审残。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓梭域,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搅轿。 傳聞我的和親對象是個殘疾皇子病涨,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

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