我面临一个奇怪的错误,只在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};
}
这是一个错误吗?我能解决这个问题吗?这非常令人沮丧。
答案 0 :(得分:22)
以下是根据iOS 10 SDK Release Notes更改的内容:
在iOS 10中,UIKit为UINavigationBar,UITabBar和UIToolbar更新并统一了后台管理。特别是,更改这些视图的背景属性(例如背景或阴影图像,或设置栏样式)可能会启动栏的布局传递以解析新的背景外观。
特别是,这意味着尝试更改这些条形图的背景外观 - [UIView layoutSubviews], - [UIView updateConstraints], - [UIViewController willLayoutSubviews], - [UIViewController didLayoutSubviews], - [UIViewController updateViewConstraints]或任何其他响应布局而调用的方法可能会导致布局循环。
所以问题似乎是viewWillAppear
触发了上面提到的布局循环,因为它是由于布局更改而调用的:
我的快速解决方法是覆盖popViewControllerAnimated
和pushViewController
并更新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)
}
我的猜测是它有效,因为操作系统不会因为布局更改而调用popViewControllerAnimated
和pushViewController
,而是触摸事件。因此,如果您想要找到另一个地方来更新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