NavigationController

实际开发中都是多控制器的;用一个控制器(父)管理多个控制器(子)

ios提供2个特殊的(父)控制器

UINavigationControler
  • 简介
    导航控制器,可以轻松完成多个控制器之间的切换,其结构包含导航条(y=20)、栈顶控制器的view、导航控制器的view。导航控制器需要设置一个根控制器,一般是UIViewControler。
  • 基本使用
QQ.png

最开始的时候,栈顶控制器的view就是导航控制器的根控制器的view。

1.先去掉Info.plist的倒数第三行的main,以采用代码方式创建

QQ.png

2.在AppDelegate.m中:


QQ.png

其中,先创建窗口,再创建导航控制器(同时创建了一个UIviewControler的控制器,作为其根控制器),然后把该窗口的根控制器设为所创建的导航控制器,最后显示窗口。
PS:一般地,导航控制器的根控制器来自新建一个UIViewControler类,然后import进来。另外,只要一个控制器是导航控制器的子控制器,那么导航控制器就会成为这个控制器的一个属性(可以拿到 )

  • 控制器之间的跳转:
QQ.png

根控制器的导航控制器调用pushViewControler方法,参数为跳转后的控制器。跳转后,显示的是后来这个ViewControler的view,也成了栈顶控制器的view,同时从window上移除了前一个控制器的view,但仍保存于导航控制器的子控制器数组中。
PS:导航控制器是其根控制器的一个属性。

  • 导航控制器的子控制器
    一般保存在数组(以栈的形式)(先进后出)中
QQ.png

PS:使用setViewControllers方法可以一次压入多个控制器,但是只会显示最后压入的那个控制器的view(栈顶控制器)
[nav setViewControllers:@[vc1,vc2,vc3] animated:YES];
获取栈顶控制器:navController.topViewController
获取当前可见视图的控制器:.visibleViewController

  • 控制器的返回
    是跳转操作的逆向。执行返回操作后,先从window中移除当前控制器的view,同时把该控制器(在栈顶)从栈中移除。此时在栈顶的控制器的view会添加到window,显示出来,从而实现返回。


    QQ.png

注意:
要回到指定的控制器,这个指定的控制器必须得是导航控制器的子控制器。
当一个控制器被pop后,控制器内存就被释放了(会调用deinit/dealloc函数。
控制器被销毁,它的view不一定就销毁(只要有storng指针),但是当一个控制器被销毁,那么它的view的业务逻辑无法处理。

设置导航条的内容

由栈顶控制器的navigationItem属性来决定导航条的内容,在栈顶控制器的.m的viewDidLoad{ }中设置

QQ.png

其中,titleView可以设为各种控件


QQ.png
QQ20170623-094427.png

设置navBar的字体大小,颜色

[nav.navigationBar setTitleTextAttributes:@{
      NSFontAttributeName:[UIFont systemFontOfSize:19],
      NSForegroundColorAttributeName:[UIColor whiteColor]
}];
navBar的隐藏状态:navController.navigationBarHidden = YES;

UINavigationBar通过UINavigationItem堆栈,按照如下方式来决定展示在UINavigationBar中的内容

q

  • 位于中间的标题会根据下方顺序选择展示的内容:
    如果topItem设置了标题视图(titleView属性), 则展示标题视图
    如果topItem设置了标题文字(title属性), 则展示标题文字
    如果以上都未设置, 则展示空白。
  • 位于右侧的按钮会根据下方顺序选择展示的内容:
    如topItem设置了右侧按钮(rightBarButtonItem属性)则展示右侧按钮
    如果以上都未设置, 则展示空白。
  • 位于左侧的按钮会根据下方顺序选择展示的内容:
    如topItem设置了左侧按钮(leftBarButtonItem属性), 则展示左侧按钮
    如backItem设置了返回按钮(backBarButtonItem), 则展示返回按钮
    如backItem设置了标题(title), 则展示用标题文字封装的返回按钮
  • 如果以上都未设置, 则展示利用文字"Back"封装的返回按钮。

设置导航条的外观

UINavigationBar类中提供了大量属性/方法用于设置其外观, 我们可以设置其样式、背景颜色、色彩颜色、文字属性,同样, 我们也可以设置其背景图片、阴影图片。

  • 样式:navigationBar.barStyle
    该属性可为UIBarStyleDefault(白底黑字)、UIBarStyleBlack(黑底白字)
  • 背景色:navigationBar.barTintColor
  • 前景色:navigationBar.tintColor
  • 设置背景图片:[navigationBar setBackgroundImage:。。。];
  • 阴影图片:navigationBar.shadowImage = [UIImage imageNamed:@""];

区分关系:

NavigaitonBar是导航栏,位于屏幕上方,管理整个导航控制器的navigationItem,它类似navigationcontroller一样,提供了一个栈来管理UINavigationItem。在编程时,一般只设置每个控制器的navigationItem属性。
一个导航控制器管理多个视图控制器(多个视图控制器共享一个导航控制器),而一个导航控制器只有一个UINavigationBar,被管理的多个视图控制器共享这一个UINavigationBar,只要一个视图控制器改变了UINavigationBar的属性则影响是全局的。
每个视图控制器都会有属于自己的UINavigationItem,系统会以懒加载的方式创建一个UINavigationItem显示在UINavigationBar中,改变UINavigationItem只会在当前控制器起作用,不会影响其它控制器。

通过storybord跳转控制器

设根控制器为导航控制器(删掉默认的控制器,拖入nav,设之为默认即添加箭头,添加导航控制器的根控制器(viewcontroler),方法是采用拖线的方式,点击rootcontroler;再拖入一个viewcontroler,实现控制器之间的跳转,方法是拖线(从一个按钮拖到下一个控制器,点击show选项)
设置导航条的内容(可以把其他控件如item等拖到导航条;可以设导航条标题;

自定义导航控制器

  • 设置导航条统一的样式
+(void)initialize{
    //获取APP的导航条标识
//    UINavigationBar *bar = [UINavigationBar appearance];  //这种写法有bug,它会代表app中所有的导航条,当app中还有自定义导航条时也会被包含
    UINavigationBar *bar = [UINavigationBar appearanceWhenContainedIn:[PDNavigationViewController class],nil];  //(获取指定某几个类的导航条标识)
    //设置nav bar的背景图片(因无法设置背景颜色)
    [bar setBackgroundImage:[UIImage imageNamed:@"navBar"] forBarMetrics:UIBarMetricsDefault];  
    //标题字体大小、颜色
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[NSFontAttributeName] = [UIFont systemFontOfSize:22.0];    
    dict[NSForegroundColorAttributeName] = [UIColor whiteColor];   
    [bar setTitleTextAttributes:dict];
}

统一样式只需设一次即可。因此,在initialize方法中设置,此方法当当前类第一次初始化的时候才调用,这样就实现只设置一次。当然在加载时viewDidLoad方法也只调用一次,但是一个缺点就是,当要创建多个导航控制器时,会调用(设置)多次。

  • 设置统一的返回按钮
    重写push方法来实现
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    [super pushViewController:viewController animated:animated];
    //判断当前不是在根控制器下,此时设置leftBarButtonItem
    if(self.viewControllers.count > 1)    
        viewController.navigationItem.leftBarButtonItem = 
        [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"nearby"] 
        style:0 target:self action:@selector(back)];
}
-(void)back{
    [self popViewControllerAnimated:YES];    //pop返回上层
}

(此时就系统没有了滑动返回功能)

  • 恢复滑动返回功能
    原理:通过清空代理的方式可以恢复
self.interactivePopGestureRecognizer.delegate = nil;   

但是存在bug,因为清空之后会带来后续影响,改善如下:

@interface PDNavigationViewController ()<UINavigationControllerDelegate>
@property(nonatomic,strong) id popGesture;   //用于暂存
@end
-(void)viewDidLoad {
    [super viewDidLoad];
    self.popGesture = self.interactivePopGestureRecognizer.delegate;   //暂存代理
    self.delegate = self;   //将代理设为自身
}

在导航控制器代理方法中清空代理,此方法当控制器显示完毕时调用,因此能够拿到当前的控制器

-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
//根控制器中不需滑动返回,此时还原代理
    if(self.viewControllers[0] == viewController){   //当前是根控制器时
        self.interactivePopGestureRecognizer.delegate = self.popGesture;   //还原代理
    }else{    //非根控制器时,清空代理( 恢复滑动返回功能 )
        self.interactivePopGestureRecognizer.delegate = nil;   
    }
}
  • 实现全屏滑动返回( 系统默认从左侧滑动才能返回 )
-(void)viewDidLoad {
    [super viewDidLoad];

    //以下代码实现全屏滑动返回
    id target = self.interactivePopGestureRecognizer.delegate;
    self.interactivePopGestureRecognizer.enabled = NO;
    
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
    [self.view addGestureRecognizer:pan];
    
    pan.delegate = self;
}

解决全屏滑动的bug

//当开始滑动就会调用
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    //如果是在非根控制器,才允许滑动
    BOOL open = self.viewControllers.count > 1;   
    return open;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 前言:ios7开始 苹果增加了页面 右滑返回的效果;具体的是以UINavigationController为容器的...
    iPhone阅读 3,744评论 2 3
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,376评论 25 707
  • 侧滑返回NavigationController自从iOS7.0之后, 增加了一个滑动返回功能, 当push入一个...
    面糊阅读 2,662评论 0 50
  • *7月8日上午 N:Block :跟一个函数块差不多,会对里面所有的内容的引用计数+1,想要解决就用__block...
    炙冰阅读 2,468评论 1 14
  • 羽山 羽山位于尧光山之东三百五十里,山下水源丰富,山上雨水很足,却没有花草树木,很多蝮蛇。
    司契阅读 210评论 0 0