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è)框架里轰枝,WKWebView
較UIWebView
有哪些優(yōu)勢呢捅彻?
-
WKWebview
在性能、穩(wěn)定性上和UIwebview相比提升了非常多 -
WKWebView
更多的支持HTML5的特性 -
WKWebView
更快鞍陨,占用內(nèi)存可能只有UIWebView的1/3 ~ 1/4 -
WKWebView
高達(dá)60fps的滾動(dòng)刷新率和豐富的內(nèi)置手勢(Built-in gestures) -
WKWebView
具有Safari相同的JavaScript引擎Nitro(JJT四個(gè)進(jìn)程解釋執(zhí)行優(yōu)化js代碼)(Fast JavaScript) -
WKWebView
增加了加載進(jìn)度屬性 - Easy app-webpage communication
- Responsive scrolling
- 更省電量 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)行不同的處理,這樣的方式適合UIWebView
及WKWebView
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原生的提示
WKWebview
的UIDelegate
提供了幾個(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的坑
加載本地的 html , 在iOS9.0之前 file:/// 不能識(shí)別夯秃,得將本地加載到一個(gè) 臨時(shí)文件中加載座咆, iOS9.0之后 新增了一個(gè)
loadFileUrl
的方法不能打開一個(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;
}
- 相關(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)
不能 用
NSURLProtocal
截獲網(wǎng)絡(luò)了 , 該類協(xié)議方法都失效了網(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"]; (注意是大寫)
- 內(nèi)存泄露
在注入腳本的時(shí)候 調(diào)用addScriptMessageHandler
以后 ,在webview銷毀之前 一定要removeMessageHandler
箕戳,否則會(huì)內(nèi)存泄露
好了某残, 以上就是我在使用WKWebView遇到的問題,如果大家以后有使用WKWebview的需求陵吸, 希望該文會(huì)對(duì)大家有所幫助玻墅。。壮虫。