ViewWillAppear中的导航栏着色在iOS 10中发生得太晚了

时间:2016-09-15 12:30:29

标签: ios10 viewwillappear

我面临一个奇怪的错误,只在iOS 10上发生。

我有一个带有多个屏幕的应用程序,每个屏幕都会为navigationBar中的viewWillAppear着色。因此,当您进入下一个屏幕时,它将被正确着色。

但是,在iOS 10上进行测试时,我会在返回上一个屏幕时突然看到以下行为: 当出现上一个屏幕时,navigationBar仍然具有上一个屏幕的颜色,然后闪烁到正确的颜色。 它看起来几乎像viewWillAppear一样表现为viewDidAppear

相关代码:

的ViewController:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [ViewControllerPainter paint:self withBackground:[UIColor whiteColor] andForeground:[UIColor blackColor] andIsLight:true];

}

画家:

+ (void)paint:(UIViewController *)controller withBackground:(UIColor *)backgroundColor andForeground:(UIColor *)foregroundColor andIsLight:(bool)isLight
{
    controller.navigationController.navigationBar.opaque = true;
    controller.navigationController.navigationBar.translucent = false;
    controller.navigationController.navigationBar.tintColor = foregroundColor;
    controller.navigationController.navigationBar.barTintColor = backgroundColor;
    controller.navigationController.navigationBar.backgroundColor = backgroundColor;
    controller.navigationController.navigationBar.barStyle = isLight ? UIBarStyleDefault : UIBarStyleBlack;
    controller.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName: foregroundColor};
}

这是一个错误吗?我能解决这个问题吗?这非常令人沮丧。

4 个答案:

答案 0 :(得分:22)

以下是根据iOS 10 SDK Release Notes更改的内容:

  

在iOS 10中,UIKit为UINavigationBar,UITabBar和UIToolbar更新并统一了后台管理。特别是,更改这些视图的背景属性(例如背景或阴影图像,或设置栏样式)可能会启动栏的布局传递以解析新的背景外观。
  特别是,这意味着尝试更改这些条形图的背景外观 - [UIView layoutSubviews], - [UIView updateConstraints], - [UIViewController willLayoutSubviews], - [UIViewController didLayoutSubviews], - [UIViewController updateViewConstraints]或任何其他响应布局而调用的方法可能会导致布局循环。

所以问题似乎是viewWillAppear触发了上面提到的布局循环,因为它是由于布局更改而调用的: viewWillAppear stack trace

我的快速解决方法是覆盖popViewControllerAnimatedpushViewController并更新navigationBar子类的UINavigationController背景。这是它的样子:

override func popViewControllerAnimated(animated: Bool) -> UIViewController? {
    let poppedViewController = super.popViewControllerAnimated(animated)

    // Updates the navigation bar appearance
    updateAppearanceForViewController(nextViewController)

    return poppedViewController
}

override func pushViewController(viewController: UIViewController, animated: Bool) {
    super.pushViewController(viewController, animated: animated)

    // Updates the navigation bar appearance
    updateAppearanceForViewController(viewController)
}

我的猜测是它有效,因为操作系统不会因为布局更改而调用popViewControllerAnimatedpushViewController,而是触摸事件。因此,如果您想要找到另一个地方来更新navigationBar背景,请记住这一点。

答案 1 :(得分:15)

我必须解决这个问题:

self.navigationController.navigationBarHidden = YES;
self.navigationController.navigationBarHidden = NO;

这样您就不必重写popviewcontroller或pushviewcontroller。它基本上是触发导航栏重绘。

它们仍然很烦人,他们怎么能推出一个新版本的操作系统来破坏这个重要的东西。

答案 2 :(得分:7)

尝试使用willMoveToParentViewController,提供与覆盖UINavigationController方法相同的效果,但没有麻烦。

答案 3 :(得分:-1)

我发布了Objective-C(UINavigationController的子类)的解决方案:

#import "FUINavigationController.h"

@interface FUINavigationController ()

@end

@implementation FUINavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"current: %@",[self.topViewController class]);

    if ([NSStringFromClass([self.topViewController class]) isEqualToString:@"LoginVC"]) {
        [self setNavBarHidden];
    }

    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
-(UIViewController*)popViewControllerAnimated:(BOOL)animated {

    UIViewController *popedVC = [super popViewControllerAnimated:animated];

    if ([NSStringFromClass([popedVC class]) isEqualToString:@"SignUpVC"] && [NSStringFromClass([[super topViewController] class]) isEqualToString:@"LoginVC"]) {
        [self setNavBarHidden];
    }

    return popedVC;
}

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {

    [super pushViewController:viewController animated:animated];
    [self setNavBarVisible];
}

-(void)setNavBarHidden {

    [self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
    [self.navigationBar setShadowImage:[UIImage new]];
    [self.navigationBar setTranslucent:YES];
    [self.navigationBar setBackgroundColor:[UIColor clearColor]];

}

-(void)setNavBarVisible {

    [self.navigationBar setBackgroundColor:[UIColor grayColor]];
    [self.navigationBar setBarTintColor:[UIColor grayColor]];
    [self.navigationBar setTintColor:[UIColor whiteColor]];
    [self.navigationBar setTranslucent:NO];
    [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName :[UIColor whiteColor]}];
    [self.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIColor whiteColor], NSForegroundColorAttributeName, [UIFont fontWithName:@"Roboto-Reqular" size:18], NSFontAttributeName,nil]];
    [self.navigationBar.topItem setBackBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]];

}

@end

或使用方法调配:

#import <objc/runtime.h>
#import "UINavigationController+FadeOutNavigationBar.h"

@implementation UINavigationController (FadeOutNavigationBar)

+(void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        //Swizzling view will appear
        SEL originalSelectorVWA = @selector(viewWillAppear:);
        SEL swizzledSelectorVWA = @selector(swizzled_viewWillAppear:);

        Method originalMethodVWA = class_getInstanceMethod(class, originalSelectorVWA);
        Method swizzledMethodVWA = class_getInstanceMethod(class, swizzledSelectorVWA);

        BOOL didAddMethodVWA =
        class_addMethod(class,
                        originalSelectorVWA,
                        method_getImplementation(swizzledMethodVWA),
                        method_getTypeEncoding(swizzledMethodVWA));

        if (didAddMethodVWA) {
            class_replaceMethod(class,
                                swizzledSelectorVWA,
                                method_getImplementation(originalMethodVWA),
                                method_getTypeEncoding(originalMethodVWA));
        } else {
            method_exchangeImplementations(originalMethodVWA, swizzledMethodVWA);
        }

        //Swizzling popViewControllerAnimated
        SEL originalSelectorPVCA = @selector(popViewControllerAnimated:);
        SEL swizzledSelectorPVCA = @selector(swizzled_popViewControllerAnimated:);

        Method originalMethodPVCA = class_getInstanceMethod(class, originalSelectorPVCA);
        Method swizzledMethodPVCA = class_getInstanceMethod(class, swizzledSelectorPVCA);

        BOOL didAddMethodPVCA =
        class_addMethod(class,
                        originalSelectorPVCA,
                        method_getImplementation(swizzledMethodPVCA),
                        method_getTypeEncoding(swizzledMethodPVCA));

        if (didAddMethodPVCA) {
            class_replaceMethod(class,
                                swizzledSelectorVWA,
                                method_getImplementation(originalMethodPVCA),
                                method_getTypeEncoding(originalMethodPVCA));
        } else {
            method_exchangeImplementations(originalMethodPVCA, swizzledMethodPVCA);
        }


        //Swizzling pushViewController
        SEL originalSelectorPVC = @selector(pushViewController:animated:);
        SEL swizzledSelectorPVC = @selector(swizzled_pushViewController:animated:);

        Method originalMethodPVC = class_getInstanceMethod(class, originalSelectorPVC);
        Method swizzledMethodPVC = class_getInstanceMethod(class, swizzledSelectorPVC);

        BOOL didAddMethodPVC =
        class_addMethod(class,
                        originalSelectorPVC,
                        method_getImplementation(swizzledMethodPVC),
                        method_getTypeEncoding(swizzledMethodPVC));

        if (didAddMethodPVC) {
            class_replaceMethod(class,
                                swizzledSelectorPVC,
                                method_getImplementation(originalMethodPVC),
                                method_getTypeEncoding(originalMethodPVC));
        } else {
            method_exchangeImplementations(originalMethodPVC, swizzledMethodPVC);
        }


    });
}

#pragma mark - Method Swizzling

- (void)swizzled_viewWillAppear:(BOOL)animated {
    [self swizzled_viewWillAppear:animated];

    NSLog(@"current: %@",[self.topViewController class]);

    if ([NSStringFromClass([self.topViewController class]) isEqualToString:@"LoginVC"]) {
        [self setNavBarHidden];
    }

}

-(void)setNavBarHidden {

    [self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
    [self.navigationBar setShadowImage:[UIImage new]];
    [self.navigationBar setTranslucent:YES];
    [self.navigationBar setBackgroundColor:[UIColor clearColor]];

}

-(void)setNavBarVisible {

    [self.navigationBar setBackgroundColor:[UIColor grayColor]];
    [self.navigationBar setBarTintColor:[UIColor grayColor]];
    [self.navigationBar setTintColor:[UIColor whiteColor]];
    [self.navigationBar setTranslucent:NO];
    [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName :[UIColor whiteColor]}];
    [self.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIColor whiteColor], NSForegroundColorAttributeName, [UIFont fontWithName:@"Roboto-Reqular" size:18], NSFontAttributeName,nil]];
    [self.navigationBar.topItem setBackBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]];

}

-(UIViewController*)swizzled_popViewControllerAnimated:(BOOL)animated {

    UIViewController *popedVC = [self swizzled_popViewControllerAnimated:animated];

    if ([NSStringFromClass([popedVC class]) isEqualToString:@"SignUpVC"] && [NSStringFromClass([[self topViewController] class]) isEqualToString:@"LoginVC"]) {
        [self setNavBarHidden];
    }

    return popedVC;
}

-(void)swizzled_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {

    [self swizzled_pushViewController:viewController animated:animated];
    [self setNavBarVisible];
}
@end