最近ios10.0發(fā)布回溺,發(fā)現(xiàn)app在系統(tǒng)10.0下運(yùn)行啤贩,導(dǎo)航欄出現(xiàn)了若干bug,遂著手解決之宏悦。
1.復(fù)現(xiàn):
在ios9及以下的系統(tǒng)中無(wú)問(wèn)題镐确,但是在ios10.0下會(huì)出現(xiàn)如下bug(返回按鈕和titleView都不顯示):
項(xiàng)目中使用了UINavigationBar + Extension擴(kuò)展,通過(guò)對(duì)UINavigationBar添加子視圖的方式設(shè)置背景圖饼煞。
使用的源碼如下:
-(void)ls_setBackgroundColor:(UIColor *)backgroundColor{
if (!self.overlay) {
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
//導(dǎo)航欄下面有一條1像素高的灰色的線(xiàn)源葫,這是一張圖片,可能過(guò)設(shè)置shadowImage修改
self.shadowImage = [UIImage new];
self.overlay = [[UIView alloc] initWithFrame:CGRectMake(0, -20, [UIScreen mainScreen].bounds.size.width, CGRectGetHeight(self.bounds) + 20)];
self.overlay.userInteractionEnabled = NO;
self.overlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self insertSubview:self.overlay atIndex:0];
}
self.overlay.backgroundColor = backgroundColor;
}
2.原因分析:
這個(gè)bug比較奇怪砖瞧,在ios9及以下的系統(tǒng)中顯示是正常的息堂,但是ios10.0視圖卻顯示不出來(lái)。由于創(chuàng)建的視圖未按預(yù)期顯示,
首先荣堰,通過(guò)Debug->View Debugging->Capture View Hierarchy查看視圖層級(jí)關(guān)系床未。發(fā)現(xiàn)返回按鈕和titleView是存在的,但是被overlay視圖擋住了振坚,所以未顯示出來(lái)薇搁。如圖:
然后渡八,打印navigationBar的子視圖啃洋,發(fā)現(xiàn)有2個(gè)成員:
<_UIBarBackground: 0x7fd5fb76c490; frame = (0 -20; 375 64); userInteractionEnabled = NO; layer = 0x60000022a740>
<_UINavigationBarBackIndicatorView: 0x7fd5fb446160; frame = (8 11.5; 13 21); alpha = 0; opaque = NO; userInteractionEnabled = NO; layer =0x608000435e60> - Back
navigationBar高度是44,上方是statusBar屎鳍。很明顯_UIBarBackground決定了導(dǎo)航欄的背景色及顯示樣式宏娄,因此可以考慮在_UIBarBackground上添加子視圖,用于顯示背景色或圖片逮壁。
最后孵坚,修復(fù)了該bug。修改后代碼如下:
3.解決方案:
static char overlayKey;
@implementation UINavigationBar (Extension)
- (UIView *)overlay{
return objc_getAssociatedObject(self, &overlayKey);
}
- (void)setOverlay:(UIView *)overlay{
objc_setAssociatedObject(self, &overlayKey, overlay, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)ls_setBackgroundColor:(UIColor *)backgroundColor{
if (!self.overlay) {
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
//去掉灰色的線(xiàn)窥淆。其實(shí)這個(gè)線(xiàn)也是image控制的卖宠。設(shè)為空即可
self.shadowImage = [UIImage new];
UIView *barBgView = self.subviews.firstObject;
self.overlay = [[UIView alloc] initWithFrame:barBgView.bounds];
self.overlay.userInteractionEnabled = NO;
self.overlay.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
[barBgView addSubview:self.overlay];
}
self.overlay.backgroundColor = backgroundColor;
}
@end
可以解決ios10下的導(dǎo)航欄問(wèn)題。正常的顯示效果為:
4.思維發(fā)散:
既然實(shí)現(xiàn)了導(dǎo)航樣單一顏色設(shè)置忧饭,如果要顯示為圖片又該如何改呢逗堵,是否要新添加一個(gè)UIImageView子視圖?
實(shí)際上不需要眷昆,可以通過(guò)overlay的圖層的寄宿圖實(shí)現(xiàn),核心代碼如下:
self.overlay.layer.contents = (__bridge id)GET_IMAGE(@"welcome1").CGImage;
在實(shí)踐中汁咏,如果你給contents賦的不是CGImage亚斋,那么得到的圖層將是空白的。它真正要賦值的類(lèi)型應(yīng)該是CGImageRef攘滩,一個(gè)指向CGImage結(jié)構(gòu)的指針帅刊。
另外,可以設(shè)置顯示方式:
self.layerView.layer.contentsGravity = kCAGravityResizeAspect;
contentsGravity的效果等同于UIViewContentModeScaleAspectFit漂问, 同時(shí)它還能在圖層中等比例拉伸以適應(yīng)圖層的邊界赖瞒。
最后:
如果控件出現(xiàn)一些預(yù)料之外的行為,可以打開(kāi)XCode的view debugging查看視圖的位置和層級(jí)關(guān)系蚤假,找到問(wèn)題所在栏饮。