iOS 11 引入了安全區(qū)(safeArea)的概念嗡载,如果UIWebView從(0,0)開(kāi)始布局雨效,即UIWebView延伸到statusBar或navigationBar下方微宝,則WebView.scrollView.contentInset將自動(dòng)調(diào)整為:
// statusBarHeight: 狀態(tài)欄高度
// navigationBarHeight: 導(dǎo)航欄高度
// originalInsetTop: 開(kāi)發(fā)者設(shè)置的contentInset.top,默認(rèn)為0
WebView.scrollView.contentInset = UIEdgeInsetsMake(customInsetTop + statusBarHeight + navigationBarHeight, 0, 0, 0)
因此蝎宇,如果UIWebView從(0弟劲,0)開(kāi)始布局且存在狀態(tài)欄和導(dǎo)航欄的情況下,UIWebView將整體向下偏移(statusBarHeight + navigationBarHeight)
目前主要有兩種解決方案(同樣適用于WKWebView):
1姥芥、客戶端適配:
WebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
2兔乞、H5適配:
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
采用上面兩種方法雖然解決了UIWebView整體向下偏移的問(wèn)題,但是發(fā)現(xiàn)部分頁(yè)面中使用-webkit-overflow-scrolling屬性的子列表依然會(huì)向下偏移,用Xcode查看視圖層級(jí)后發(fā)現(xiàn):一旦使用-webkit-overflow-scrolling屬性庸追,WebView.scrollView內(nèi)部將生成一個(gè)新的UIScrollView:(準(zhǔn)確地說(shuō)是UIWebOverflowScrollView霍骄,這是UIScrollView的子類,)而且UIWebOverflowScrollView的contentInset被自動(dòng)調(diào)整為:
UIWebOverflowScrollView.contentInset = UIEdgeInsetsMake(statusBarHeight + navigationBarHeight, 0, 0, 0)
這導(dǎo)致了子列表向下偏移的現(xiàn)象锚国,通過(guò)調(diào)整contentInsetAdjustmentBehavior屬性:
UIWebOverflowScrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
可以將UIWebOverflowScrollView向上提回到正常位置腕巡,但是UIWebOverflowScrollView是WebKit根據(jù)H5使用-webkit-overflow-scrolling屬性而動(dòng)態(tài)生成的私有圖層,H5調(diào)用的時(shí)機(jī)和WebKit生成UIWebOverflowScrollView的時(shí)機(jī)并不確定血筑,客戶端很難準(zhǔn)確地獲取到UIWebOverflowScrollView绘沉。目前看,使用viewport-fit=cover也只能調(diào)整UIWebView.scrollView不要根據(jù)safeArea自動(dòng)偏移, 但是對(duì)UIWebView.scrollView內(nèi)部生成的子scrollView豺总,如UIWebOverflowScrollView卻無(wú)能為力(當(dāng)然车伞,WKWebView不存在這樣的問(wèn)題,蘋(píng)果積極地fix了這個(gè)Bug), UIWebView卻是比較尷尬了喻喳。
我們注意到iOS 11 UIViewController新增的屬性additionalSafeAreaInsets可以調(diào)整安全區(qū)域的大小另玖,根據(jù)文檔推測(cè):既然子列表向下偏移是因?yàn)榘踩珔^(qū)域?qū)е碌模敲赐ㄟ^(guò)調(diào)整additionalSafeAreaInsets的值將安全區(qū)從導(dǎo)航下方延伸到整個(gè)手機(jī)屏幕表伦,應(yīng)該可以解決子列表向下偏移的問(wèn)題:
webViewController.additionalSafeAreaInsets = UIEdgeInsetsMake(-64, 0, 0, 0)
這樣調(diào)整后谦去,安全區(qū)確實(shí)可以延伸到導(dǎo)航欄下方,但是依然無(wú)法延伸到狀態(tài)欄下方蹦哼,而狀態(tài)欄的高度為20pt, 這導(dǎo)致UIWebView.scrollView自動(dòng)向下偏移20pt, 實(shí)際上我們要調(diào)整當(dāng)前window根視圖控制器的additionalSafeAreaInsets才能將安全區(qū)延伸到statusBar下:
window.rootViewController.navigationController.additionalSafeAreaInsets = UIEdgeInsetsMake(-64, 0, 0, 0)
但是這樣要調(diào)整rootViewController感覺(jué)也不是很優(yōu)雅鳄哭,最后我們使出黑魔法,直接hook了UIScrollView的初始化方法
@implementation UIScrollView (Swizzle)
+ (void)load
{
[self jr_swizzleMethod:@selector(initWithFrame:) withMethod:@selector(qzInitWithFrame:) error:nil];
}
- (UIScrollView *)qzInitWithFrame:(CGRect)frame
{
UIScrollView *scrollview = [self qzInitWithFrame:frame];
if (scrollview && [scrollview isKindOfClass:NSClassFromString([NSString stringWithFormat:@"U%@Ov%@Scr%@", @"IWeb", @"erflow", @"ollView"])]) {
if (@available(iOS 11.0, *)) {
scrollview.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
}
return scrollview;
}
@end