提到自定义导航栏,大家首先想到的就是自己写个自定义导航控制器,然后设置自己的导航控制器的主题。再把包装着自己控制器的导航控制器的class填上自己写的自定义nav如果遇到个别控制器的导航栏想与众不同,就再写个自定义nav然后再弄个新的导航控制器包裹自己。
可是,如果一个项目中用到了 父子控制器,上面的这种做法就会没有效果。原因就是取不到导航栏。
比如我做的大概架构是一个collectionView的循环引用,让一个个tableview都是包装在我的collectionViewcell里面的,然后里面的的tableview的cell点击之后会push一个新的界面。这时候不管你把这个push的新界面怎么设置导航栏样式 在程序中都是看不见的,因为只能看到最上层,就是collectionViewController的导航栏。
(这里暂时就没截图了,因为好多种结果,截太麻烦了,相信大家能理解文字的意思)
而且就算设置导航栏的主题大家也都知道,这段代码也都是写在首次调用本类时方法里
+ (void)initialize
{
// 设置导航栏的主题
UINavigationBar *navBar = [UINavigationBar appearance];
[navBar setBarTintColor:[UIColor redColor]];
}
之后再想在动态修改,导航栏的背景色或是背景图片都是改不了了,只有在一加载时就写好。我曾试过在push出新的控制器和pop弹栈之后发出通知在这里更改样式,但是都是无效的,改不了。
如图,这个里面控制器是两个scrollView,一个是标题栏scrollView,一个是下面的内容也是一个scrollView。 (之所以用scrollview是为了每个页面懒加载,性能更好),然后在scrollView中嵌套了一个个tableViewController。 是把tableViewController的view加到 scrollView的subViews里。
scrollView是主控制器view的subView 那这个tableViewController的view就是主控制器的 subView的subView。
这里要说一下关于父子控制器的注意点就是:如果把A的view加到了B的view的subViews里 那这个A的控制器也必须加到这个B的控制器的childControllers里。
不然view过去了控制器却没有 他不就是没人管了?
上面这个效果 主要用到了一个方法就是导航栏的隐藏方法
当然在设置界面的时候,上面的箭头和跟帖都是要自己自定义的一个普通的view不是导航栏,可以把他当作导航栏来使用。点击返回会把栈顶控制器弹栈,点击跟帖会再push一个评论控制器。
然后再这个详情控制器将要显示时把导航栏隐藏,再在这个详情页将要弹栈时设置导航栏显示就好。
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
- (IBAction)backBtnClick:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
这里还有个注意点就是 要把导航栏隐藏写在合适的方法里,如果写在将要跳转的方法prepareForSegue里就会有抽搐的bug
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController isKindOfClass:[SXDetailController class]]) {
NSInteger x = self.tableView.indexPathForSelectedRow.row;
SXDetailController *dc = segue.destinationViewController;
dc.newsModel = self.arrayList[x];
[self.navigationController setNavigationBarHidden:YES animated:YES];
}else{
NSInteger x = self.tableView.indexPathForSelectedRow.row;
SXPhotoSetController *pc = segue.destinationViewController;
pc.newsModel = self.arrayList[x];
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
}
如图这个是反面教材,仔细看导航栏有个抽搐的bug
有的人可能会尝试干脆直接不用导航控制器了 自己设置按钮控制push和pop
但是这样系统是不允许的 会报错,报错的大概意思就是 你不能给一个没有导航控制器的控制器添加push操作。
我上面用的方法是虽然把导航控制器隐藏了,但他的功能还在。
隐藏导航栏非常好用,可是会遇到一个问题,就是隐藏了导航栏之后,push出的界面无法向左滑动返回。
这个问题类似于: 在导航栏的左上角自定义了一个返回按钮把原本的返回按钮给覆盖了。导致无法实现滑动返回
解决方案是这样,再将控制器push的代码之后 或者在prepareForSegue 方法里面加上这行代码
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}
因为你覆盖了系统的返回按钮,系统将会通过代理禁用这个滑动返回功能,你提前把代理给踢了,这样滑动返回功能就能报住了。
为了安全起见 最好把主控制器的即将显示加上这行代码
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
如果不加这行代码 可能你一push再拉回来导航栏还是隐藏着的。因为刚才我们只在返回按钮触发时设置了导航栏显示对吧。
最终效果可以达到这样如图
哪里觉得不对劲或者哪里觉得看不懂 欢迎与我交流 欢迎关注我