UIWebView & WKWebView 以及 WKWebView的坑

1. 移動(dòng)端展示W(wǎng)eb方式

目前,iOS端展示W(wǎng)eb的方式大致可分為四種:

  • safari 展示
  • iOS9提供的SFSafariViewController展示
  • UIWebview
  • WKWebview

第一種方式是我們?nèi)フ{(diào)用openUrl: 通過系統(tǒng)的瀏覽器展示我們的web頁面,這是蘋果提供給我們最原始的方法。

NSURL *URL = [NSURL urlWithString:@"http:[www.baidu.com"]];
[[UIApplication sharedApplication] openUrl:URL];

第二種是iOS9提供了一個(gè)繼承于UIViewController的瀏覽器,使用方式很簡單,直接initWithUrl: 然后present或者push 就可以訪問web頁, 查看它相關(guān)的api丸卷,實(shí)在是太少了, 從而無法自定義询刹,這個(gè)相當(dāng)于系統(tǒng)的safari谜嫉。

第三種就是iOS7.0之前大家都在用的UIWebView,它基于UIKit框架凹联,繼承UIView,整個(gè)頭文件寥寥無幾沐兰,不超過100行。下面介紹UIWebView的使用

typedef NS_ENUM(NSInteger, UIWebViewNavigationType) {
UIWebViewNavigationTypeLinkClicked,//用戶觸發(fā)了一個(gè)鏈接
UIWebViewNavigationTypeFormSubmitted,//用戶提交了一個(gè)表單
UIWebViewNavigationTypeBackForward,//用戶觸擊前進(jìn)前進(jìn)或返回按鈕
UIWebViewNavigationTypeReload,//用戶觸擊重新加載的按鈕
UIWebViewNavigationTypeFormResubmitted,//用戶重復(fù)提交表單
UIWebViewNavigationTypeOther//發(fā)生了其他行為
} __TVOS_PROHIBITED;

//2. 加載內(nèi)容關(guān)于分頁顯示幾種不同類型
typedef NS_ENUM(NSInteger, UIWebPaginationMode) {
UIWebPaginationModeUnpaginated,
UIWebPaginationModeLeftToRight,
UIWebPaginationModeTopToBottom,
UIWebPaginationModeBottomToTop,
UIWebPaginationModeRightToLeft
} __TVOS_PROHIBITED;

typedef NS_ENUM(NSInteger, UIWebPaginationBreakingMode) {
UIWebPaginationBreakingModePage,//默認(rèn)設(shè)置是這個(gè)屬性蔽挠,CSS屬性以頁樣式住闯。
UIWebPaginationBreakingModeColumn//當(dāng)UIWebPaginationBreakingMode設(shè)置這個(gè)屬性的時(shí)候,這個(gè)頁面內(nèi)容CSS屬性以column-break 代替page-breaking樣式澳淑。
} __TVOS_PROHIBITED;

@class UIWebViewInternal;
@protocol UIWebViewDelegate;

NS_CLASS_AVAILABLE_IOS(2_0) __TVOS_PROHIBITED @interface UIWebView : UIView <NSCoding, UIScrollViewDelegate>

@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;

@property (nonatomic, readonly, strong) UIScrollView *scrollView NS_AVAILABLE_IOS(5_0);

- (void)loadRequest:(NSURLRequest *)request;
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;

@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;

- (void)reload;
- (void)stopLoading;

- (void)goBack;
- (void)goForward;

@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
@property (nonatomic, readonly, getter=isLoading) BOOL loading;

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

//是否讓內(nèi)容伸縮至適應(yīng)屏幕當(dāng)前尺寸
@property (nonatomic) BOOL scalesPageToFit;

//這個(gè)屬性如果設(shè)置為YES比原,當(dāng)進(jìn)入到頁面視圖可以自動(dòng)檢測電話號(hào)碼,讓用戶可以單機(jī)號(hào)碼進(jìn)行撥打杠巡,不過現(xiàn)已棄用量窘。
@property (nonatomic) BOOL detectsPhoneNumbers NS_DEPRECATED_IOS(2_0, 3_0);

//這個(gè)屬性可以設(shè)定使電話號(hào)碼,網(wǎng)址氢拥,電子郵件和符合格式的日期等文字變?yōu)檫B接文字蚌铜。
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);

//這個(gè)屬性決定了頁面用內(nèi)嵌HTML5播放視頻還是用本地的全屏控制。為了內(nèi)嵌視頻播放嫩海,不僅僅需要在這個(gè)頁面上設(shè)置這個(gè)屬性冬殃,還需要在HTML的viedeo元素必須包含webkit-playsinline屬性。默認(rèn)iPhone為NO叁怪,iPad為YES审葬。
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0); // iPhone Safari defaults to NO. iPad Safari defaults to YES

//這個(gè)屬性決定了HTML5視頻可以自動(dòng)播放還是需要用戶啟動(dòng)播放。iPhone和iPad默認(rèn)都是YES。
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0); // iPhone and iPad Safari both default to YES

//這個(gè)屬性決定了從這個(gè)頁面是否可以Air Play涣觉。iPhone和iPad上都是默認(rèn)YES痴荐。
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0); // iPhone and iPad Safari both default to YES

//這個(gè)值決定了網(wǎng)頁內(nèi)容的渲染是否在把內(nèi)容全部加到內(nèi)存中再去處理。如果設(shè)置為YES旨枯,只有網(wǎng)頁內(nèi)容加載到內(nèi)存里了才會(huì)去渲染。默認(rèn)為NO
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0); // iPhone and iPad Safari both default to NO

//這個(gè)屬性如果設(shè)置為YES混驰,用戶必須明確的點(diǎn)擊頁面上的元素或者相關(guān)聯(lián)的輸入頁面來顯示鍵盤攀隔。如果設(shè)置為NO,一個(gè)元素的焦點(diǎn)事件就會(huì)導(dǎo)致輸入視圖的顯示和自動(dòng)關(guān)聯(lián)這個(gè)元素栖榨。
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0); // default is YES
//設(shè)置頁面分頁模型選擇昆汹。
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
//這個(gè)屬性決定了CSS屬性是采用column-break 還是page-breaking樣式。
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);

//分頁的長度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
//分頁之間間距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
//分頁的個(gè)數(shù)
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);

//是否允許畫中畫播放The default value is YES on devices that support Picture in Picture (PiP) mode and NO on all other devices.
@property (nonatomic) BOOL allowsPictureInPictureMediaPlayback NS_AVAILABLE_IOS(9_0);

//3DTouch的預(yù)覽功能婴栽,默認(rèn)為NO
@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE_IOS(9_0); // default is NO
@end

__TVOS_PROHIBITED @protocol UIWebViewDelegate <NSObject>

@optional
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- (void)webViewDidStartLoad:(UIWebView *)webView;
- (void)webViewDidFinishLoad:(UIWebView *)webView;
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;

@end

第四種就是WKWebView,也是本文的重點(diǎn)满粗,它誕生于iOS8以后,介于我們現(xiàn)在的app最低版本已支持到iOS8了愚争,有這么好的東西映皆,何不嘗試一下呢?
WKWebView 在 WKKit 這個(gè)框架里轰枝,WKWebViewUIWebView有哪些優(yōu)勢呢捅彻?

  1. WKWebview在性能、穩(wěn)定性上和UIwebview相比提升了非常多
  2. WKWebView更多的支持HTML5的特性
  3. WKWebView更快鞍陨,占用內(nèi)存可能只有UIWebView的1/3 ~ 1/4
  4. WKWebView高達(dá)60fps的滾動(dòng)刷新率和豐富的內(nèi)置手勢(Built-in gestures)
  5. WKWebView具有Safari相同的JavaScript引擎Nitro(JJT四個(gè)進(jìn)程解釋執(zhí)行優(yōu)化js代碼)(Fast JavaScript)
  6. WKWebView增加了加載進(jìn)度屬性
  7. Easy app-webpage communication
  8. Responsive scrolling
  9. 更省電量 battery

以上信息可以在WWDC2014-206節(jié)-介紹 WebKit modern API 的時(shí)候提到步淹。

WKWebView 頭文件 屬性介紹

WKBackForwardList: 之前訪問過的 web 頁面的列表,可以通過后退和前進(jìn)動(dòng)作來訪問到诚撵。

WKBackForwardListItem: webview 中后退列表里的某一個(gè)網(wǎng)頁缭裆。

WKFrameInfo: 包含一個(gè)網(wǎng)頁的布局信息。

WKNavigation: 包含一個(gè)網(wǎng)頁的加載進(jìn)度信息寿烟。

WKNavigationAction: 包含可能讓網(wǎng)頁導(dǎo)航變化的信息澈驼,用于判斷是否做出導(dǎo)航變化。

WKNavigationResponse: 包含可能讓網(wǎng)頁導(dǎo)航變化的返回內(nèi)容信息筛武,用于判斷是否做出導(dǎo)航變化盅藻。

WKPreferences: 概括一個(gè) webview 的偏好設(shè)置。

WKProcessPool: 表示一個(gè) web 內(nèi)容加載池畅铭。

WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法氏淑。

WKScriptMessage: 包含網(wǎng)頁發(fā)出的信息。

WKUserScript: 表示可以被網(wǎng)頁接受的用戶腳本硕噩。

WKWebViewConfiguration: 初始化 webview 的設(shè)置假残。

WKWindowFeatures: 指定加載新網(wǎng)頁時(shí)的窗口屬性。

protocal:

WKNavigationDelegate: 提供了追蹤主窗口網(wǎng)頁加載過程和判斷主窗口和子窗口是否
進(jìn)行頁面加載新頁面的相關(guān)方法。

WKScriptMessageHandler: 提供從網(wǎng)頁中收消息的回調(diào)方法辉懒。

WKUIDelegate: 提供用原生控件顯示網(wǎng)頁的方法回調(diào)阳惹。

以上為WKWebView 頭文件及協(xié)議方法

2. web進(jìn)階

OC如何給JS注入對(duì)象及JS如何給iOS發(fā)送消息

WKWebView沒出來之前,在iOS7,系統(tǒng)提供了JaveScriptCore 這個(gè)獲取Context眶俩,然后注入JS函數(shù)莹汤,JS 調(diào)用OC的方法
在iOS7,之前我們只能通過曲線救國的方式,讓JS去調(diào)用OC的方法颠印, 我們定義自己的協(xié)議scheme纲岭,比如
ubaby:// aa/aaaa
根據(jù)不同的協(xié)議進(jìn)行不同的處理,這樣的方式適合UIWebViewWKWebView

WKWebView提供一套JS 和 OC 交互的方法
WKUserContentController通過該類线罕,用戶可以注入JS 方法 止潮。

js端 :{
    function openImage {
        window.webkit.messageHandlers.openImage.possMessage('');
    }
}

OC 端

WKUserContentController *userController = [[WKUserContentController alloc] init];
 [userController addScriptMessageHandler:self name:@"openImage"];

handler需要實(shí)現(xiàn)
WKScriptMessageHandler協(xié)議里的 


- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
}

這樣就實(shí)現(xiàn)了 js --- > oc的交互
WKWebview 給我們提供里一個(gè)evaluateJavaScript:該方法 可以讓OC 調(diào)用 JS 里面的方法
同樣我們可以 自己注入一個(gè)JS 函數(shù),然后 通過evaluateJavaScript調(diào)用

NSString *jsGetImages = @"    
 function openImage {
         
    }
";

WKUserScript *script = [[WKUserScript alloc] initWithSource:jsGetImages  injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];

[userController addUserScript:script];

[webView evaluateJavaScript:@"openImage"  completionHandler:^(id result ,NSError *error) {
    NSLog(@"%@",result);
}];

JS調(diào)用 alert钞楼, confirm , prompt 不在采用JS原生的提示

WKWebviewUIDelegate提供了幾個(gè) 方法喇闸,供我們實(shí)現(xiàn)我們自己樣式的

//  針對(duì)alert 
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
// 針對(duì) confirm
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 針對(duì) prompt
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

可以通過以上方法自定我們自己的彈框

WKWebView的 KVO

WKWebView很多屬性都可以被監(jiān)聽,然后我們進(jìn)一步對(duì)其處理询件,比如:estimatedProgress燃乍, 這個(gè)屬性是加載進(jìn)度, 通過監(jiān)聽它的變化宛琅,我們可以知道網(wǎng)頁的加載進(jìn)度橘沥。

WKWebView的坑

  1. 加載本地的 html , 在iOS9.0之前 file:/// 不能識(shí)別夯秃,得將本地加載到一個(gè) 臨時(shí)文件中加載座咆, iOS9.0之后 新增了一個(gè)loadFileUrl的方法

  2. 不能打開一個(gè) 新的頁面, 即 target = "_blank"仓洼, 我們需要在協(xié)議方法里面判斷

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    
WKFrameInfo *frameInfo = navigationAction.targetFrame;
    if (![frameInfo isMainFrame]) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}
  1. 相關(guān)的 Scheme 和 AppStore links 都無法直接跳轉(zhuǎn)

要在

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

}

里面 進(jìn)行判斷介陶,通過
[[UIApplication sharedApplication] openURL:url];
去跳轉(zhuǎn)

  1. 不能 用NSURLProtocal截獲網(wǎng)絡(luò)了 , 該類協(xié)議方法都失效了

  2. 網(wǎng)頁不再獲取默認(rèn)的cookie色建, 如果后端有該方面的需求哺呜,我們就得自己傳遞cookie給后端

//1. js 注入 cookie
    NSMutableDictionary *cookiesDictionary = [NSMutableDictionary dictionary];
    NSArray *cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
    
    for (NSHTTPCookie *cookie in cookies) {
        [cookiesDictionary setValue:cookie.value forKey:cookie.name];
    }
    
    NSMutableArray *cookieArray = [NSMutableArray array];
    for (NSString *key in cookiesDictionary) {
        [cookieArray addObject:[NSString stringWithFormat:@"%@=%@",key,cookiesDictionary[key]]];
    }
    
    if (cookieArray.count > 0) {
        WKUserScript *userScript = [[WKUserScript alloc] initWithSource:[NSString stringWithFormat:@"document.cookie='%@'",[cookieArray componentsJoinedByString:@"&"]] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
        [userContentController addUserScript:userScript];
    }
 

// 2. 還可以通過 requestHeader 傳遞該cookie

     NSString *cookie =  @"cookieKey1=cookieValue1;cookieKey2=cookieValue2";```

    [mutableRequest addValue:cookie forHTTPHeaderField:@"Cookie"]; (注意是大寫)
  1. 內(nèi)存泄露
    在注入腳本的時(shí)候 調(diào)用addScriptMessageHandler 以后 ,在webview銷毀之前 一定要removeMessageHandler箕戳,否則會(huì)內(nèi)存泄露

好了某残, 以上就是我在使用WKWebView遇到的問題,如果大家以后有使用WKWebview的需求陵吸, 希望該文會(huì)對(duì)大家有所幫助玻墅。。壮虫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末澳厢,一起剝皮案震驚了整個(gè)濱河市环础,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剩拢,老刑警劉巖线得,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異徐伐,居然都是意外死亡贯钩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門办素,熙熙樓的掌柜王于貴愁眉苦臉地迎上來角雷,“玉大人,你說我怎么就攤上這事摸屠∥铰蓿” “怎么了粱哼?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵季二,是天一觀的道長。 經(jīng)常有香客問我揭措,道長胯舷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任绊含,我火速辦了婚禮桑嘶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘躬充。我一直安慰自己逃顶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布充甚。 她就那樣靜靜地躺著以政,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伴找。 梳的紋絲不亂的頭發(fā)上盈蛮,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音技矮,去河邊找鬼抖誉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛衰倦,可吹牛的內(nèi)容都是我干的袒炉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼樊零,長吁一口氣:“原來是場噩夢啊……” “哼梳杏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤十性,失蹤者是張志新(化名)和其女友劉穎叛溢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劲适,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡楷掉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霞势。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烹植。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖愕贡,靈堂內(nèi)的尸體忽然破棺而出草雕,到底是詐尸還是另有隱情,我是刑警寧澤固以,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布墩虹,位于F島的核電站,受9級(jí)特大地震影響憨琳,放射性物質(zhì)發(fā)生泄漏诫钓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一篙螟、第九天 我趴在偏房一處隱蔽的房頂上張望菌湃。 院中可真熱鬧,春花似錦遍略、人聲如沸惧所。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽下愈。三九已至,卻和暖如春寞忿,著一層夾襖步出監(jiān)牢的瞬間驰唬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工腔彰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叫编,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓霹抛,卻偏偏與公主長得像搓逾,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杯拐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容