iOS導(dǎo)航欄控制的一些總結(jié)
前言
許久不寫UI,對(duì)UI的很多東西都生疏了,最近使用導(dǎo)航欄的各種場(chǎng)景做一些總結(jié)。
1.導(dǎo)航欄的顯示與隱藏
導(dǎo)航欄的顯示與隱藏,分兩種情況:
1.從不顯示導(dǎo)航欄的頁(yè)面push到顯示導(dǎo)航欄的頁(yè)面。
2.從顯示導(dǎo)航欄的頁(yè)面Push到不顯示導(dǎo)航欄的頁(yè)面。
注意:
1.如果導(dǎo)航欄不顯示時(shí),系統(tǒng)的側(cè)滑返回功能無(wú)效。
2.雖然側(cè)滑返回功能無(wú)效,但是導(dǎo)航欄的 .interactivePopGestureRecognizer.delegate還是存在的。
針對(duì)以上兩種情況分別處理,整個(gè)Push過程都假設(shè)是從A頁(yè)面跳轉(zhuǎn)到B頁(yè)面
1.1 從不顯示導(dǎo)航欄的頁(yè)面Push到顯示導(dǎo)航欄的頁(yè)面。
關(guān)于導(dǎo)航欄的顯示,是否順滑,是通過如下兩個(gè)方法來控制。
// 不顯示動(dòng)畫,導(dǎo)航欄顯示就比較突兀 [self.navigationController setNavigationBarHidden:YES]; // 顯示動(dòng)畫,在側(cè)滑時(shí),導(dǎo)航欄顯示就比較順滑 [self.navigationController setNavigationBarHidden:YES animated:YES];
所以,做法是:
A頁(yè)面:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
B頁(yè)面:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
1.2 從顯示導(dǎo)航欄的頁(yè)面跳轉(zhuǎn)到不顯示導(dǎo)航欄的頁(yè)面
這種情況的做法如下:
A頁(yè)面:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
B頁(yè)面:
// 在頁(yè)面將要出現(xiàn)時(shí),記錄原始側(cè)滑手勢(shì)代理對(duì)象,并將手勢(shì)代理設(shè)置為當(dāng)前頁(yè)面
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.interactivePopDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
// 在頁(yè)面消失時(shí),還原側(cè)滑手勢(shì)代理對(duì)象
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.navigationController.interactivePopGestureRecognizer.delegate = self.interactivePopDelegate;
self.interactivePopDelegate = nil;
}
// 實(shí)現(xiàn)手勢(shì)代理,為了防止影響其他手勢(shì),可以判斷一下手勢(shì)類型
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
return YES;
}
...... 其他手勢(shì)的處理
return NO;
}
2.統(tǒng)一重寫導(dǎo)航欄返回按鈕
有時(shí)候,我們可能需要統(tǒng)一工程中的返回按鈕樣式,比如都是 箭頭+返回 或者都是 箭頭。
方案有兩種:
1.創(chuàng)建一個(gè)BaseViewController,然后統(tǒng)一設(shè)置navigationItem.leftBarButtonItem。
2.重寫導(dǎo)航控制器的Push方法,在push之前,設(shè)置navigationItem.backBarButtonItem。
注意:
如果重寫了導(dǎo)航欄的leftBarButtonItem,那么側(cè)滑返回功能也就失效了,需要側(cè)滑返回功能需要自己處理。
第一種方案比較簡(jiǎn)單就不做贅述了,第二種方案是這樣的:
自定義導(dǎo)航控制器,然后重寫如下方法:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStyleDone target:nil action:nil];
viewController.navigationItem.backBarButtonItem = backItem;
[super pushViewController:viewController animated:animated];
}
如果不需要返回這兩個(gè)字,只需要這樣寫就好。
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:nil style:UIBarButtonItemStyleDone target:nil action:nil];
viewController.navigationItem.backBarButtonItem = backItem;
[super pushViewController:viewController animated:animated];
}
3.監(jiān)聽返回按鈕的點(diǎn)擊事件
在有些場(chǎng)景,我們需要監(jiān)聽返回按鈕的事件。比如,當(dāng)頁(yè)面用戶輸入了一些內(nèi)容后,用戶要點(diǎn)擊返回,想要回到上一個(gè)頁(yè)面時(shí),提醒用戶是否要緩存已經(jīng)輸入的內(nèi)容。
如果我們重寫了導(dǎo)航欄的返回按鈕,那么處理這種情況就很Easy,不做贅述了。
但是,如果我們沒有重寫過系統(tǒng)的返回按鈕,想要處理這種情況就比較麻煩,但是也是可以處理的。
處理步驟如下:
1.首先創(chuàng)建一個(gè)UIViewController的類別,頭文件(.h)的內(nèi)容如下:
@protocol BackItemProtocol <NSObject> - (BOOL)navigationShouldPopWhenBackButtonClick; @end @interface UIViewController (BackItem)<BackItemProtocol> @end @interface UINavigationController (BackItem) @end
包含一個(gè)協(xié)議、UIViewController的類別、UINavigationController的類別。
然后,實(shí)現(xiàn)文件(.m)如下:
#import "UIViewController+BackItem.h"
@implementation UIViewController (BackItem)
- (BOOL)navigationShouldPopWhenBackButtonClick
{
return YES;
}
@end
@implementation UINavigationController (BackItem)
// 這個(gè)其實(shí)是導(dǎo)航欄的協(xié)議方法,在這里重寫了
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
if([self.viewControllers count] < [navigationBar.items count]) {
return YES;
}
BOOL shouldPop = YES;
UIViewController *vc = [self topViewController];
if([vc respondsToSelector:@selector(navigationShouldPopWhenBackButtonClick)]) {
shouldPop = [vc navigationShouldPopWhenBackButtonClick];
}
if (shouldPop) {
dispatch_async(dispatch_get_main_queue(), ^{
[self popViewControllerAnimated:YES];
});
} else {
for(UIView *subview in [navigationBar subviews]) {
if(subview.alpha < 1) {
[UIView animateWithDuration:.25 animations:^{
subview.alpha = 1;
}];
}
}
}
return NO;
}
@end
默認(rèn)是,不需要處理返回按鈕的事件,直接使用系統(tǒng)的pop方法。
但是,如果我們需要在用戶點(diǎn)擊返回按鈕時(shí),彈窗提示,那就需要導(dǎo)入這個(gè)類別。
然后,重寫一個(gè)方法:
- (BOOL)navigationShouldPopWhenBackButtonClick
{
BOOL isFlag = 輸入框不為空等等條件
if (isFlag) {
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:@"是否保存修改" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// 這里延時(shí)執(zhí)行是因?yàn)閁IAlertController阻塞UI,可能會(huì)導(dǎo)致動(dòng)畫的不流暢
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
}];
UIAlertAction *saveAction = [UIAlertAction actionWithTitle:@"保存" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
// 這里延時(shí)執(zhí)行是因?yàn)閁IAlertController阻塞UI,可能會(huì)導(dǎo)致動(dòng)畫的不流暢
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self rightClick];
});
}];
[alertVC addAction:cancelAction];
[alertVC addAction:saveAction];
[self presentViewController:alertVC animated:YES completion:nil];
return NO;
}
return YES;
}
4.導(dǎo)航控制器的頁(yè)面跳轉(zhuǎn)方式
安卓中的頁(yè)面跳轉(zhuǎn)有四種方式: standard、singleTop、singleTask、singleInstance。
例如singleTask,在做IM類App,跳轉(zhuǎn)到聊天室的場(chǎng)景,就非常有用,可以保證控制器棧中只有一個(gè)聊天室,避免返回時(shí)層級(jí)太深。
iOS端如果要仿這個(gè)效果的話,可以利用導(dǎo)航控制器的API:
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated
首先,為UINavigationController 創(chuàng)建一個(gè)類別。
比如:
UINavigationController+HLPushAndPop.h UINavigationController+HLPushAndPop.m
然后,新增幾個(gè)方法:
拿兩個(gè)方法來舉例
- (void)hl_pushSingleViewController:(UIViewController *)viewController animated:(BOOL)animated; - (void)hl_pushSingleViewController:(UIViewController *)viewController parentClass:(Class)parentClass animated:(BOOL)animated;
再然后,實(shí)現(xiàn)方法:
實(shí)現(xiàn)步驟:
- 創(chuàng)建新的數(shù)組復(fù)制導(dǎo)航控制器原來的堆棧中的控制器。
- 在原始堆棧數(shù)組中判斷是否存在該類型的控制器,如果存在記錄其索引。
- 在復(fù)制的數(shù)組中將索引及上方所有控制器移除。
- 把將要push出來的控制器添加到復(fù)制的數(shù)組中。
- 將新的控制器數(shù)組設(shè)置為導(dǎo)航控制器的棧數(shù)組,根據(jù)參數(shù)判斷是否要顯示動(dòng)畫。
我這邊做了一些發(fā)散,因?yàn)橐恍╊惪赡軙?huì)有很多子類,那么想要保證父類以及子類的實(shí)例都只有一個(gè),所以將方法做了改進(jìn)。
- (void)hl_pushSingleViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
[self hl_pushSingleViewController:viewController parentClass:viewController.class animated:animated];
}
- (void)hl_pushSingleViewController:(UIViewController *)viewController
parentClass:(Class)parentClass
animated:(BOOL)animated
{
if (!viewController) {
return;
}
// 如果要push的界面不是 parentClass以及其子類的實(shí)例,則按照方法1處理
if (![viewController isKindOfClass:parentClass]) {
[self hl_pushSingleViewController:viewController animated:animated];
return;
}
// 判斷 導(dǎo)航控制器堆棧中是否有parentClass以及其子類的實(shí)例
NSArray *childViewControllers = self.childViewControllers;
NSMutableArray *newChildVCs = [[NSMutableArray alloc] initWithArray:childViewControllers];
BOOL isExit = NO;
NSInteger index = 0;
for (int i = 0; i < childViewControllers.count; i++) {
UIViewController *vc = childViewControllers[i];
if ([vc isKindOfClass:parentClass]) {
isExit = YES;
index = i;
break;
}
}
// 如果不存在,則直接push
if (!isExit) {
[self pushViewController:viewController animated:animated];
return;
}
// 如果存在,則將該實(shí)例及上面的所有界面全部彈出棧,然后將要push的界面放到棧頂。
for (NSInteger i = childViewControllers.count - 1; i >= index; i--) {
[newChildVCs removeObjectAtIndex:i];
}
[newChildVCs addObject:viewController];
viewController.hidesBottomBarWhenPushed = (newChildVCs.count > 1);
[self setViewControllers:newChildVCs animated:animated];
}
當(dāng)然了,除了上面這些場(chǎng)景,還可以擴(kuò)展出一些其他的場(chǎng)景,比如我們期望將要push出來的控制器再某個(gè)棧中控制器的后面或者前面,這樣當(dāng)點(diǎn)擊返回或者側(cè)滑時(shí),就直接回到了指定頁(yè)面了。
或者我們知道將要返回的頁(yè)面的類型,直接pop回指定頁(yè)面。
擴(kuò)展出來的其他方法都在Demo中了,有興趣的可以看一下。
地址是:HLProject (本地下載)
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)我們的支持。
上一篇:iOS實(shí)現(xiàn)文字水平無(wú)間斷滾動(dòng)效果
欄 目:IOS
本文標(biāo)題:iOS導(dǎo)航欄控制的一些總結(jié)
本文地址:http://www.jygsgssxh.com/a1/IOS/11878.html
您可能感興趣的文章
- 01-11iOS常用算法之兩個(gè)有序數(shù)組合并(要求時(shí)間復(fù)雜度為0(n))
- 01-11iOS 彈幕功能的實(shí)現(xiàn)思路圖解
- 01-11iOS調(diào)試Block引用對(duì)象無(wú)法被釋放的小技巧分享
- 01-11iOS動(dòng)態(tài)更換Icon的全過程記錄
- 01-11iOS實(shí)現(xiàn)文本分頁(yè)的方法示例
- 01-11iOS常見宏理解及使用方法
- 01-11iOs遷至WKWebView跨過的一些坑
- 01-11iOS模擬中獎(jiǎng)名單循環(huán)滾動(dòng)效果
- 01-11Python一鍵查找iOS項(xiàng)目中未使用的圖片、音頻、視頻資源
- 01-11iOS中如何獲取某個(gè)視圖的截圖詳析


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-11UILabel顯示定時(shí)器文本跳動(dòng)問題的解決
- 01-11iOS常用算法之兩個(gè)有序數(shù)組合并(要
- 01-11iOS 彈幕功能的實(shí)現(xiàn)思路圖解
- 01-11詳解MacOs免密登錄CentOs操作步驟
- 01-11iOS動(dòng)態(tài)更換Icon的全過程記錄
- 01-11iOS調(diào)試Block引用對(duì)象無(wú)法被釋放的小技
- 01-11iOS常見宏理解及使用方法
- 01-11iOS實(shí)現(xiàn)文本分頁(yè)的方法示例
- 01-11iOs遷至WKWebView跨過的一些坑
- 01-11iOS模擬中獎(jiǎng)名單循環(huán)滾動(dòng)效果
隨機(jī)閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什


