【问题标题】:Navigationbar coloring in ViewWillAppear happens too late in iOS 10ViewWillAppear 中的导航栏着色在 iOS 10 中发生得太晚了
【发布时间】:2016-09-15 12:30:29
【问题描述】:

我遇到了一个奇怪的错误,它只发生在 iOS 10 上。

我有一个包含多个屏幕的应用程序,每个屏幕都为viewWillAppear 中的navigationBar 着色。因此,当您转到下一个屏幕时,它将正确着色。

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

相关代码:

视图控制器:

- (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};
}

这是一个错误吗?我能做些什么来解决这个问题吗?这很令人沮丧。

【问题讨论】:

  • 同样的问题!

标签: ios10 viewwillappear


【解决方案1】:

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

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

所以问题似乎是viewWillAppear 正在触发上述布局循环,因为它是由于布局更改而被调用的:

对我来说,快速解决方法是覆盖 popViewControllerAnimatedpushViewController 并更新我的 UINavigationController 子类的 navigationBar 背景。下面是它的样子:

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 背景,请记住这一点。

【讨论】:

  • 有趣。将对此进行调查。在新的 iOS 版本中更改类似的内容仍然很混乱。我会认为这是一个错误,但显然它是一个功能。
  • 这确实可以正常工作。仍会为此行为提交错误报告,因为对于您希望状态栏与第二个视图控制器状态栏颜色不同的情况,这当然不是真正的解决方案。
  • 你对目标 c 有什么建议?
  • 请更新目标 c 的解决方案...如何为带有导航控制器的标签栏控制器完成此操作?
【解决方案2】:

我必须解决这个问题:

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

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

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

【讨论】:

  • 谢谢你!我把它放在 poppedViewController 的 viewWillDisappear 中,效果很好。非常感谢。
  • 有效。非常感谢。顺便说一句,在 iOS 11 中,对 UiNavigationBar 的更改更加荒谬。
【解决方案3】:

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

【讨论】:

  • 这是唯一不会直接进入疯狂小镇的可行解决方案!
  • 这只是被推送和弹出的视图控制器调用:推送时很好,但是弹出时,您希望访问将要显示的视图控制器,而不是如果每个控制器的颜色会发生变化,则将被删除。
【解决方案4】:

我正在发布 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

【讨论】:

  • [self setNavBarVisible] 方法里面是什么?
  • -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated 方法没有在我的视图控制器中被调用...
  • 你继承 UINavigationController 了吗?
  • 你的意思是视图控制器是 UINavigationController 的子类吗?
  • 没有。找到你 UINavigationController 并使用它的子类(例如“FUINavigationController.h”就像答案)
猜你喜欢
  • 2012-05-17
  • 2010-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多