对OS开发人员来讲i, 根据需求修改状态栏的背景的很常见的事情
在 iOS 13之前,修改状态栏的背景颜色的方法很简单
+ (void)setStatusBarBackgroundColor:(UIColor *)color {
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
statusBar.backgroundColor = color;
}
}
但是系统升级到iOS 13 之后,私有KVC iOS不允许valueForKey、setValue: forKey获取和设置私有属性
坑一:这个时候修改状态栏(电池栏的)在用之前的方法会闪退
解决办法:适配iOS 13 增加代码
+ (void)setStatusBarBackgroundColor:(UIColor *)color {
if(@available(iOS13.0, *)) {
UIView* statusBar = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
[[UIApplication sharedApplication].keyWindow addSubview:statusBar];
statusBar.backgroundColor= color;
} else {
statusBar.backgroundColor= color;
}
}else{
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
if([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
statusBar.backgroundColor= color;
}
}
}
坑2:如果整个项目的状态栏(电池栏)都是有颜色的,并且都是不透明的,上面这个方法完全可以应付
但是:如果有状态是有透明或者半透明的效果,上面这个方法还是不能胜任,越透明越明显;经过一番查找,终于发现问题:
问题: iOS 13之前,可以通过valueForKey 获取UIApplication的statusBar,因为UIApplication是单例,因此,在iOS 12,通过: [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"]拿到的statusBar永远是同一个对象。不行可以打印出内存地址看下就很清楚了。iOS 13之后,因为苹果不允许使用KVC的valueForKey访问私有属性。通过上面的代码可以多看点,每次进来都调用 alloc:init的方法,重新生成一个statusBar;然后添加到UIApplication的keyWindow上,再设置背景颜色。因此这个方法多次调用就会创建多份statusBar,造成内存开销不说,如果设置为透明,根部不能起开效果。
解决办法:既然定位到问题所在,办法就是保证iOS 13 之后,每次也都能拿到有去只有一个对象。方法有很多,我的方法代码如下:使用 static 配合 gcd
+ (void)setStatusBarBackgroundColor:(UIColor *)color {
if(@available(iOS13.0, *)) {
staticUIView*statusBar =nil;
if(!statusBar) {
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
statusBar = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
[[UIApplication sharedApplication].keyWindow addSubview:statusBar];
statusBar.backgroundColor= color;
});
}else{
statusBar.backgroundColor= color;
}
}else{
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
if([statusBarrespondsToSelector:@selector(setBackgroundColor:)]) {
statusBar.backgroundColor= color;
}
}
}