最近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)中無問題,但是在ios10.0下會(huì)出現(xiàn)如下bug(返回按鈕和titleView都不顯示):
項(xiàng)目中使用了UINavigationBar + Extension擴(kuò)展,通過對(duì)UINavigationBar添加子視圖的方式設(shè)置背景圖。
使用的源碼如下:
-(void)ls_setBackgroundColor:(UIColor *)backgroundColor{
if (!self.overlay) {
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
//導(dǎo)航欄下面有一條1像素高的灰色的線,這是一張圖片,可能過設(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視圖卻顯示不出來。由于創(chuàng)建的視圖未按預(yù)期顯示,
首先,通過Debug->View Debugging->Capture View Hierarchy查看視圖層級(jí)關(guān)系。發(fā)現(xiàn)返回按鈕和titleView是存在的,但是被overlay視圖擋住了,所以未顯示出來。如圖:
然后,打印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];
//去掉灰色的線。其實(shí)這個(gè)線也是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)航欄問題。正常的顯示效果為:
4.思維發(fā)散:
既然實(shí)現(xiàn)了導(dǎo)航樣單一顏色設(shè)置,如果要顯示為圖片又該如何改呢,是否要新添加一個(gè)UIImageView子視圖?
實(shí)際上不需要,可以通過overlay的圖層的寄宿圖實(shí)現(xiàn),核心代碼如下:
self.overlay.layer.contents = (__bridge id)GET_IMAGE(@"welcome1").CGImage;
在實(shí)踐中,如果你給contents賦的不是CGImage,那么得到的圖層將是空白的。它真正要賦值的類型應(yīng)該是CGImageRef,一個(gè)指向CGImage結(jié)構(gòu)的指針。
另外,可以設(shè)置顯示方式:
self.layerView.layer.contentsGravity = kCAGravityResizeAspect;
contentsGravity的效果等同于UIViewContentModeScaleAspectFit, 同時(shí)它還能在圖層中等比例拉伸以適應(yīng)圖層的邊界。
最后:
如果控件出現(xiàn)一些預(yù)料之外的行為,可以打開XCode的view debugging查看視圖的位置和層級(jí)關(guān)系,找到問題所在。