iOS--UINavigationController學習筆記
阿新 • • 發佈:2019-01-07
1.簡介
UINavigationController:是iOS常見的一種容器型Controller。官方文件上給出的註釋如下
UINavigationController manages a stack of view controllers and a navigation bar.It
performs horizontal view transitions for pushed and popped views while keeping the navigation bar in sync.Most clients will not need to subclass
UINavigationController. If a navigation controller is nested in a tabbar controller, it uses the title and toolbar attributes of the bottom view
controller on the stack.UINavigationController is rotatable if its top view controller is rotatable.Navigation
between controllers with non-uniform rotatability is currently not supported.
意思是說,UINavigationController管理著一個viewController的棧和一個navigation bar(navigation bar後面會說)。管理著出於同一層次的viewController的view,包括壓棧和出棧(viewController顯示滑進和隱藏滑出界)的轉場動畫,同時還同步管理著頂部navigation bar的狀態。大部分的app都不需要實現一個它的子類(對於一般的功能,都具有支援)。如果一個UINavigationController的例項巢狀在一個UITabbarController的例項中,它將使用棧底的viewController的標題和toolbar的屬性。UINavigationController的可旋轉是和棧頂的viewController保持一致的。現在還不支援同一層次的viewController旋轉型別不一致的壓棧出棧。
2.常見屬性和方法
- (instancetype)initWithRootViewController:(UIViewController
*)rootViewController;
以一個viewController為棧底,例項化一個navigation viewcontroller
- (void)pushViewController:(UIViewController
*)viewController animated:(BOOL)animated;
展示一個viewcontroller,壓棧操作(可以有動畫效果)。
- (nullableUIViewController
*)popViewControllerAnimated:(BOOL)animated;
結束並隱藏一個viewcontroller(dealloc),出棧操作(可以有動畫效果),返回出棧的viewcontroller
- (nullableNSArray<__kindofUIViewController
*> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;
結束並隱藏 在棧中位於一個特殊的viewcontroller之前所有的viewcontroller,並返回
- (nullableNSArray<__kindofUIViewController
*> *)popToRootViewControllerAnimated:(BOOL)animated;
出棧到根viewcontroller
@property(nullable,nonatomic,readonly,strong)
UIViewController *topViewController;
棧頂viewcontroller
@property(nullable,nonatomic,readonly,strong)
UIViewController *visibleViewController;
如果模態viewcontroller存在,返回模態viewcontroller,否則棧頂viewcontroller
@property(nonatomic,copy)
NSArray<__kindofUIViewController *> *viewControllers;
當前棧中的元素
- (void)setViewControllers:(NSArray<UIViewController
*> *)viewControllers animated:(BOOL)animated NS_AVAILABLE_IOS(3_0);
設定棧中的元素,animated=yes,根據當前的棧頂viewcontroller在不在這個viewcontroller陣列中,模擬一次push和pop的動畫。這就相當於對當前的棧進行了一次整體的重新整理
下面的bar屬性主要是用來顯示頂部的導航欄以及底部的工具欄
@property(nonatomic,getter=isNavigationBarHidden)BOOLnavigationBarHidden;
- (void)setNavigationBarHidden:(BOOL)hidden
animated:(BOOL)animated;
@property(nonatomic,readonly)
UINavigationBar *navigationBar;
@property(nonatomic,getter=isToolbarHidden)BOOLtoolbarHidden
NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;
- (void)setToolbarHidden:(BOOL)hidden
animated:(BOOL)animated NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;
@property(null_resettable,nonatomic,readonly)
UIToolbar *toolbar NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;//
delegate,可以在壓棧出棧操作中,設定一些動畫,以及一些額外的操作
@property(nullable,nonatomic,weak)id<UINavigationControllerDelegate>
delegate;
這裡需要說一下visibleViewController和topViewController:topViewController永遠代表著棧頂元素;visibleViewController代表著當前顯示的那個vc,這個vc可能是top vc,也有可能是top vc 展示出來的vc。
3.UINavigationBar
3.1. 導航欄屬性設定:
通常我們都會在APPDelegate為整個APP的導航欄做全域性性的設定,使用[UINavigationBarappearance]這個方法獲得當前的導航欄例項;以及在viewController中對當前頁面的導航欄做特殊的設定,使用self.navigationController.navigationBar獲得當前的導航欄例項。記住,在viewController中對導航欄的設定,要在這個viewController小時之前還原,否則會覆蓋全域性的設定,從而影響其他頁面的顯示。
barTintColor:設定導航欄的整體背景顏色,包括狀態列的背景顏色。如下:
[[UINavigationBarappearance]setBarTintColor:[UIColoryellowColor]];執行的效果如下
tintColor:設定導航欄的按鈕的圖示和文字顏色(系統提供的),自定義的不在此範圍;
[[UINavigationBarappearance]setTintColor:[UIColorredColor]];執行的效果如下
barStyle:設定導航欄的整體風格,有UIBarStyleDefault和UIBarStyleBlack兩種選擇。對應以下兩種執行結果,可以看到UIBarStyleBlack會影響狀態列的顏色以及導航欄的標題顏色。(但是已經對標題的字型顏色都已經賦值的,就不受影響了)
titleTextAttributes:設定導航欄的標題的顯示樣式,包括UITextAttributeFont - 字型 UITextAttributeTextColor - 文字顏色 UITextAttributeTextShadowColor
- 文字陰影顏色 UITextAttributeTextShadowOffset - 偏移用於文字陰影
[[UINavigationBarappearance]setTitleTextAttributes:[NSDictionarydictionaryWithObjectsAndKeys:
[UIColorredColor],NSForegroundColorAttributeName,
[UIFontsystemFontOfSize:18.f],NSFontAttributeName,
nil]];
backgroundImage/shadowImage:設定導航欄的背景和陰影圖片
[[UINavigationBarappearance]setBackgroundImage:[UIImageimageNamed:xxx]
forBarMetrics:UIBarMetricsDefault];
[[UINavigationBarappearance]setShadowImage:[UIImageimageNamed:xxx]];
translucent:BOOL,半透明效果。
補充點:前面說到也可以在viewController中對UINavigationBar進行屬性設定,基本上都會生效。但是,有些情況下會發現,viewController的(UIStatusBarStyle)preferredStatusBarStyle是不起作用的,原因是因為當前的viewController是巢狀在一個UINavigationController的例項中,最多隻會呼叫UINavigationController的preferredStatusBarStyle,除非將導航欄隱藏,才會呼叫viewController的preferredStatusBarStyle方法。解決辦法:在iOS9之前,App的info.plist檔案中,新增View
controller-based status bar appearance的值為NO,(意識是說讓Application的設定優先於viewController的設定),通過[[UIApplication sharedApplication] setStatusBarStyle:]這個方法來進行設定修改statusBar;但是在iOS9之後,該方法會提示過時⚠️,解決辦法是將 View
controller-based status bar appearance的值設定YES,然後通過navigationBar.barStyle來設定。然而,現在很多應用都是在iOS7做適配的時候,將View
controller-based status bar appearance設為NO,(iOS之前,預設值為NO,iOS之後為YES),iOS7和8對此也都處於相容狀態。9之後會出現警告。
3.2. UINavigationItem以及其與UINavigationBar的關係:
在前面的簡介之中,提到UINavigationController管理著一個UIViewController的棧和一個UINavigationBar。其實,一個UINavigationController的例項對應一個UINavigationBar的例項,而一個UINavigationBar的例項同樣管理著一個棧,這個棧中的元素就是UINavigationItem。所以,既然一個navigationController對應一個navigationBar,可以推斷到UINavigationController的棧和UINavigationBar的棧也是對應的,而兩個棧裡元素也是一一對應,也就是一個UIViewController的例項也對應著一個UINavigationItem的例項(如果不對應,系統會報異常情況)。在navigationController進行push和pop的同時,navigationBar也在同時做相應的push和pop
。在官方的API中,在UINavigationController.h檔案中會有一個UIViewController的一個category,裡面定義著一個UINavigationItem的變數。
3.3. 定製導航欄內容
定製導航欄一般都是在viewController中進行自我定製navigationItem。
定製title,使用viewController.title和viewController.navigationItem.title都可以實現導航欄title的定製。補充:設定viewController.title會覆蓋viewController.navigationItem.title,設定viewController.navigationItem.title不會覆蓋viewController.title。
有的時候需要將導航欄標題修改為一個我們自己想要的UIView,只要將我們需要的UIView賦值給viewController.navigationItem.titleView。比如一個button,一張圖片,一個搜尋框等等
定製left/rightBarButtonItem:在導航欄中,左右兩邊通常都會有一些button或者需要互動的view。這時候就需要給viewController.navigationItem設定left/rightBarButtonItem,可定製成任意樣式。
在導航欄中,如果本身沒有對viewController.navigationItem.leftBarButtonItem進行定製,會優先展示上一個頁面的backBarButtonItem;如果上一個頁面也沒有設定backBarButtonItem,就會展示一個系統的回退按鈕和上一個頁面的title。