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