navigation 简单了解下使用
常用方法
添加导航栏
1 | TestViewController * mainVC = [[TestViewController alloc] init]; |
push
首先是最常用的方法:
1 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; |
使用 setViewControllers
一次性依次压入多个控制器,最后显示最后的控制器:
1 | UINavigationController *nav = [[UINavigationController alloc] init]; |
pop
常用方法:
1 | - (UIViewController *)popViewControllerAnimated:(BOOL)animated; |
一层层返回不方便,可以直接返回到某一个控制器:
1 | // 返回到某一个控制器 |
那么如何获取要 pop 到的控制器呢?
1 | /// 当前管理的所有的控制器 |
注意,topViewController与visibleViewController大部分情况一样,也有可能不同
导航条
基本属性
NaviagationItem
来决定大部分的显示与控制。首先看看有哪些属性:
1 | // 中间的标题文字 |
设置默认返回按钮
设置返回箭头
导航栏默认有一个返回的按钮,我们可以自定义它的箭头:
1 | // 设置颜色时,imageWithRenderingMode 设置 UIImage 渲染为原来的颜色 |
通用的隐藏返回文字
返回按钮旁的标题默认是上一级页面的 title,可以如下设置隐藏 title:
1 | [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-100, 0) forBarMetrics:UIBarMetricsDefault]; |
特殊页面设置返回按钮
把自定义的 barbutton 设置到 backBarButtonItem
即可
1 | let backbtn = UIBarButtonItem(title: "取消", style: UIBarButtonItemStyle.Plain, target:self, action: nil) |
注意,这段代码要写在上一个控制器中。因为 back 要返回的是上一个控制器。
自定义左侧按钮
按钮使用的是 UIBarButtonItem
这个类,通过 initWithCustomView:
方法初始化。
1 | - (void)viewDidLoad { |
自定义右侧按钮和左侧按钮方法类似。
注意,设置了 leftBarButtonItem 就替代了原来的的返回按钮。可以通过设置
self.navigationItem.leftItemsSupplementBackButton = YES;
保留返回按钮和 leftItems
自定义中间视图
自定义中间视图比较简单,直接设置 navigationItem
的 titleView
。
给系统的 leftBarButtonItem 添加 Badge
上面提到了如何自定义左侧按钮,我们可以在自定义的 Button 上添加 Badge 实现效果。如果是系统的呢?由于 leftBarButtonItem 是 UIBarButtonItem
的实例,而 UIBarButtonItem
并非继承于 UIView
,可以推测出,UIBarButtonItem
上显示的 image 和 label 是隐藏的属性。
我们需要使用 runtime 查找 UIBarButtonItem
中视图的属性名,并且通过 KVC,拿到这个视图属性:
1 | // UIBarButtonItem+Badge.m |
经过比对,发现视图属性名为 _view
,拿到这个属性后,对其内部视图进行遍历,找到 UIImageView 的对象,我们就可以在 UIImageView 上添加 Badge 了。
同样的,我们还可以通过这种方法,自定义 UITabBarItem 的 Badge
1 |
|
参考自:iOS: 教你给 UI 控件添加 Badge(消息提醒小圆点)
自定义右上角按钮或多个按钮
1 | @property(nullable, nonatomic,strong) UIBarButtonItem *rightBarButtonItem; |
设置 navigationItem 字体格式
可以通过 [UINavigationBar appearance]
方法 设置:
1 | [[UINavigationBar appearance]setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor],NSFontAttributeName:[UIFont systemFontOfSize:18]}]; |
这个设置和是否隐藏导航栏一样,都是全局的,无法孤立地设置某一个 ViewController。如果有一个地方需要特殊设置,那么需要在其它地方再设置回去。
操作 navigationItem
上面我们看到操作 navigationBar 都是通过 self.navigationController.navigationBar
,操作 navigationItem 都是通过 self.navigationItem
。
事实上,UINavigationController 并没有navigationItem
这样一个直接的属性,由于 UINavigationController 继承于 UIViewController ,而 UIViewController 是有navigationItem
这个属性的是,所以对 navigationController 使用点语法获取 navigationItem
是编译得过的,但是这样操作是没有效果的。
NavigationItem 是一个 NSObject 对象,里面保存了导航栏上的各个 view;
NavigationBar 是一个 UIView 对象,NavigationItem 中的各个 view 都被添加到其上
UINavigationController 返回手势失效
系统为 UINavigationController 提供了一个 interactivePopGestureRecognizer
用于右滑返回(pop),但是,如果自定了 left button 或者隐藏了 navigationBar ,该手势就失效了。我们需要自己实现一下 delegate 方法;
不过一般我们最好还是使用默认的 backbutton 返回为好。一般自定义了 left button,并且隐藏了 backbutton 的情况,都应该是不让用户能够直接返回的情况。
新建一个 BaseNavigationController
实现 delegate:
1 | - (void)viewDidLoad { |
需要实现的代理方法:
1 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { |
考虑到在 push 动画发生的时候,要禁止滑动手势,所以继续在 BaseNavigationController
中实现代理方法,禁止滑动手势:
1 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { |
然后在新 push 出来的 ViewController 中设置启用滑动手势:
1 | - (void)viewDidAppear:(BOOL)animated { |
这种是开启了全局的右滑返回,对于特定页面的禁用右滑手势可以通过:
1 | - (void)viewDidAppear:(BOOL)animated { |
导航栏
隐藏导航栏
1 | - (void)viewWillAppear:(BOOL)animated{ |
这个方法设置之后,后面的页面的导航栏也都隐藏了。所以要注意一个原则,在当前页面
viewWillAppear
方法中做的修改,也要在当前页面的viewWillDisappear
中还原回来。
修改导航栏背景色
错误示范
1 | - (void)viewDidLoad { |
这个方法无法达到理想效果,上面会盖上白色的一层 UIImageView
,必须直接设置这个 UIImageView
才能达到理想的效果。
正确示范
1 | [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"xxx"] forBarMetrics:UIBarMetricsDefault]; |
就像上面说的,必须要直接设置 UIImageView
,可以自己写个纯色的 UIImage
也可以直接放一张图片。
设置导航栏透明度
设置导航栏透明度需要自己创建一个带有透明度的 UIImage:
1 | // 背景色 |
这里只创建了一个像素的图片,但是还是能达到平铺整个导航栏的效果。
创建的 UIImage 的大小只有一个像素的好处是节约 CPU 资源。
隐藏导航栏底部的分割线
就是设置一个透明的图片,和设置导航栏背景色一个道理。
1 | UINavigationBar *navigationBar = self.navigationController.navigationBar; |
导航栏的几个属性
这些属性一般用处不大,但是有点印象就行了。以下为默认值
1 | self.navigationController.navigationBar.translucent = YES; |
- 导航半透明的时候
- 设置
edgesForExtendedLayout
为UIRectEdgeAll
,视图会延伸到导航栏下。默认为 YES- 此时默认
automaticallyAdjustScrollViewInsets
为 YES。ScrollView 会自动设置 contentInset
- 此时默认
- 设置
edgesForExtendedLayout
为UIRectEdgeNone
,视图不会延伸。
- 设置
- 导航栏不透明的时候
- 设置
edgesForExtendedLayout
无效。 - 设置
extendedLayoutIncludesOpaqueBars
为 YES,可以延伸到不透明的导航栏下 - 设置
extendedLayoutIncludesOpaqueBars
为 NO,不会延伸到透明的导航栏系下,默认为 NO
- 设置
更改顶部状态栏颜色
在工程的Info.plist文件中添加一行UIViewControllerBasedStatusBarAppearance,选择Boolean类型,并设置为YES,Xcode会自动把名称变为View controller-based status bar appearance。
在你的ViewController中添加下面的方法
1
2
3
4-(UIStatusBarStyle)preferredStatusBarStyle{
// return UIStatusBarStyleDefault; 黑色
return UIStatusBarStyleLightContent; // 白色
}调用 UIViewController 的
setNeedsStatusBarAppearanceUpdate
方法,通知状态栏颜色改变了。
全屏滑动返回
实现全屏滑动返回仅需在导航栏给导航栏添加UIGestureRecognizerDelegate
协议,并在ViewDidLoad中设置。关键在于调用系统返回处理方法 hyandleNavigationTransition
1 | // 获取系统自带滑动手势的target对象 |
导航栏过渡
导航栏过渡是一个需要好好设计的功能。如果做得不好,push 和 pop 时候会非常僵硬。
比较好的方式是通过 Method Swizzling 获取系统方法 _updateInteractiveTransition
拿到当前的进度。
具体可参考如下两篇文章:
超简单!!! iOS设置状态栏、导航栏按钮、标题、颜色、透明度,偏移等
如果要求不高,对于特殊的页面,直接在 viewWillAppear
和 viewWillDisapper
中通过 [self.navigationController setNavigationBarHidden:YES animated:YES]
显示和隐藏即可。