首页 > IOS导航栏的问题

IOS导航栏的问题

ios 两个状态栏 背景图片不一样 push过去正常显示
返回(pop)回来,当前的背景图变成了push过去的那个导航栏的背景图片.
用图片演示:
1.这是控制器A:

2.设置push过去的控制器B:

3.点击返回后的控制器A:

为什么控制器A的导航栏的背景图片变成了控制器B的?如何修改?

代码奉上:
自定义控制器的所有代码:

#import "LMSNavigationController.h"

@interface LMSNavigationController ()

@end

@implementation LMSNavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.navigationBar setTitleTextAttributes:
  @{NSFontAttributeName:[UIFont systemFontOfSize:16]}];
}


/**
 * 在这个方法中拦截所有push进来的控制器
 */
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (self.childViewControllers.count > 0) {
        // 如果push进来的不是第一个控制器
        UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [leftBtn setImage:[UIImage imageNamed:@"nav-back.png"] forState:UIControlStateNormal];
        [leftBtn setImage:[UIImage imageNamed:@"nav-back-hi.png"] forState:UIControlStateHighlighted];
        leftBtn.frame = CGRectMake(0, 0, 30, 30);
        [leftBtn addTarget:self action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
        viewController.navigationItem.leftBarButtonItem  = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];
        // 隐藏tabbar
        viewController.hidesBottomBarWhenPushed = YES;
    }
    
    //把导航栏底部的横线去掉
    [self.navigationBar setShadowImage:[UIImage new]];
    
    // 这句super的push要放在后面, 让viewController可以覆盖上面设置的leftBarButtonItem
    // 意思是,我们任然可以重新在push控制器的viewDidLoad方法中设置导航栏的leftBarButtonItem,如果设置了就会覆盖在push方法中设置的“返回”按钮,因为 [super push....]会加载push的控制器执行viewDidLoad方法。
    [super pushViewController:viewController animated:animated];
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

控制器A关键代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    //导航栏透明背景(nav-clearcolor-image是一张透明的图片)
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"nav-clearcolor-image"] forBarMetrics:UIBarMetricsDefault];
    //把导航栏底部的横线去掉
    [self.navigationController.navigationBar setShadowImage:[UIImage new]];
    //不显示文字
    self.navigationItem.title = nil;
}

控制器B关键代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    UILabel *titleLabel = [[UILabel alloc]init];
    titleLabel.text = @"push过来的控制器";
    titleLabel.textColor = TitleTextColor;
    [titleLabel sizeToFit];
    self.navigationItem.titleView = titleLabel;
}

nav 下的所有 VC 是通用同一个 navBar 的
你在进入 B 的时候修改了 navBar
但是在退出 B 的时候没有改回来


你肯定在B中修改了导航条的色或者图了吧


这是个关于 UINavigationController(假设你们的 LMSNavigationController 是它的子类)以及 UIViewController (后文简称 VC )生命周期的问题。。。


简单版本的答案

VC AviewDidLoad 改成 viewWillAppear
(下面还有个感觉上更优的办法。)

复杂版本(也杂不到哪去。。。)

先看看文档对 viewDidLoad 是怎么描述的:

Called after the controller'€™s view is loaded into memory.

在 VC 的视图加载进内存的时候,这个方法会被调用。VC B pop 出去的时候不符合调用 VC AviewDidLoad 的条件,解释如下:

UINavigationController 维护着一个 VC 栈,你 pushpop 那些 VC 时都是在操作这个栈。

push 的时候它会创建一个新的 VC 压入栈中,就是你的 VC B,这个过程中这个新的 VC B 绑定的 view 经历了被加入内存的过程, viewDidLoad 被调用。 VC B 处于栈顶,成为 topViewControllerVC A 不再处于栈顶,但仍处于栈中,与它关联的 view 仍在内存中。再 pop VC B 出去的时候,VC A 关联的 view 的 viewDidLoad 不再被调用。

所以 push pop 具体调用了什么?

上图

Figure 1-5 Messages sent during stack changes

图片来源:Navigation Controller 文档的 Monitoring Changes to the Navigation Stack 部分

Monitoring Changes to the Navigation Stack
监控导航栈的变化

push VC B 出去为例:

  1. VC B 作为当前的 Topmost view controller ,其 viewWillDispear 最先被调用

  2. VC AviewWillAppear 被调用

  3. UINavigationControllerDelegatenavigationController:willShowViewController:animated: 被调用

  4. 转场动画(完成?不清楚)

  5. VC BviewDidDispear 被调用

  6. VC AviewDidAppear 被调用

  7. UINavigationControllerDelegatenavigationController:didShowViewController:animated: 被调用

综上, VC AviewDidLoad 改成 viewWillAppear 即可。

然而个人感觉更好的办法是把两个 viewDidLoad 都拆了,逻辑重写在 UINavigationControllerDelegatenavigationController:willShowViewController:animated: 中。即可在一个位置解决改背景图这个需求。

完。


更新,上面说的用 UINavigationControllerDelegate 的办法:

    func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
        if self.childViewControllers.count > 1 {
            navigationController.navigationBar.setBackgroundImage(UIImage(named: "a"), forBarMetrics: .Default)
        } else {
            navigationController.navigationBar.setBackgroundImage(UIImage(named: "b"), forBarMetrics: .Default)
        }
    }

viewWillAppear 也试了,跟这个一模一样,没出现黑框。就这个问题而言,这两个地方就是符合你要求的。怎么选看自己喜欢就好。

系统自带的导航控制器在动画开始前已经做完了该做的事,不会出现动画到一半图片才载入完。最差也就是动画开始前卡住几秒,载入完才开始。

你们的导航控制器是自己定制的,检查定制的部分哪里有问题导致的黑框。

(另。。还是没看到背景图在哪改了。不是第一个控制器那里改的是按钮图片啊)


在A的viewWillAppear方法里面再设置一遍vc的背景图片试试


你有2个导航栏控制器 (pop)回去的导航栏是跟当前的导航栏一样的,应该搞个代理就重新赋值就可以了。(不过比较麻烦)

【热门文章】
【热门文章】