Erlo

NavigationController

2019-01-14 17:02:48 发布   449 浏览  
页面报错/反馈
收藏 点赞

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

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

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

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

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

 

QQ.png


2.在AppDelegate.m中:


其中,先创建窗口,再创建导航控制器(同时创建了一个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中的内容

    • 位于中间的标题会根据下方顺序选择展示的内容:
      如果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;
    }

作者:Arthur凌
链接:https://www.jianshu.com/p/ef6594eeefd6
來源:简书

 

登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认