iOS开发 - WKWebView
分为几大块:
- JS 与OC 交互的方式有哪些?
- WKWebView相关API介绍
- WKWebView加载本地HTML
- 进度条显示(KVO获取其他属性,如:title)
- 使用中一些问题
JS 与OC 交互的方式有哪些?
1、在JS 中做一次URL跳转,然后在OC中拦截跳转。(这里分为UIWebView 和 WKWebView两种);
2、利用WKWebView 的 MessageHandler(WKWebView 是 iOS8推出的);
3、利用系统库 JavaScriptCore 来和UIWebView做相互调用(JavaScriptCore iOS 7推出的);
4、利用第三方库WebViewJavascriptBridge;
5、利用第三方cordova库,以前叫PhoneGap(这是一个库平台的库);
JS_OC
JS与OC交互 - URL拦截
iOS下JS与OC互相调用(一)--UIWebView 拦截URL
iOS下JS与OC互相调用(二)--WKWebView 拦截URL
JS与OC交互 - 利用WKWebView的MessageHandler
iOS下JS与OC互相调用(三)--WKWebView的MessageHandler
JS与OC交互 - UIWebView与系统库JavaScriptCore
iOS下JS与OC互相调用(四)--UIWebView与JavaScriptCore
JS与OC交互 - 第三方库WebViewJavascriptBridge
iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge
iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge
JS与OC交互 - 第三方cordova库
iOS下JS与OC互相调用(七)--Cordova 基础
iOS下JS与OC互相调用(八)--Cordova详解+实战
WKWebView相关API介绍
WKWebView
@interface WKWebView : UIView
Starting in iOS 8.0 and OS X 10.10, use WKWebView to add web content to your app. Do not use UIWebView or WebView.
// 初始化webview相关API
// 拷贝一个configuration来初始化一个网页使用。它是一个属性的集合,这个集合用来初始化网页。
@property(nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
// 初始化一个网页视图,设置其大小和配置
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration;
// 从解档数据获取一个初始化的对象。
- (nullable instancetype)initWithCoder:(NSCoder *)coder;
// 查看webview信息相关API
// WKWebView继承自UIView, 所以如果想设置scrollView的一些属性, 需要对此属性进行配置
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
// 页面标题, 一般使用KVO动态获取
@property (nullable, nonatomic, readonly, copy) NSString *title;
// 当前请求的URL,支持KVO的
@property (nullable, nonatomic, readonly, copy) NSURL *URL;
// 自定义UserAgent, 会覆盖默认的值 API_AVAILABLE(macosx(10.11), ios(9.0))
@property (nullable, nonatomic, copy) NSString *customUserAgent;
// 当前提交的导航的SecTrustRef对象,支持KVO。 API_AVAILABLE(macosx(10.12), ios(10.0))
@property (nonatomic, readonly, nullable) SecTrustRef serverTrust;
// 两个delegaete
// The web view's navigation delegate.
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// The web view's user interface delegate.
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
// 加载content相关API
// 页面加载进度, 一般使用KVO动态获取
@property (nonatomic, readonly) double estimatedProgress;
// 标识页面中的所有资源是否通过安全加密连接来加载,支持KVO的
@property (nonatomic, readonly) BOOL hasOnlySecureContent;
// 当前是否在加载内容
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
// 重新加载当前页面。
- (nullable WKNavigation *)reload;
// 重新加载当前页面,如果可能,使用缓存验证条件执行端到端重新验证。
- (nullable WKNavigation *)reloadFromOrigin;
// 停止加载当前页面上的所有资源。
- (void)stopLoading;
// 加载请求API
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// 加载URL API_AVAILABLE(macosx(10.11), ios(9.0))
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL;
// 直接加载HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// 直接加载data API_AVAILABLE(macosx(10.11), ios(9.0))
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL;
// Scaling Content 缩放相关API
#if !TARGET_OS_IPHONE
// 标识是否支持放大手势,默认为NO
@property (nonatomic) BOOL allowsMagnification;
// 放大因子,默认为1
@property (nonatomic) CGFloat magnification;
// 根据设置的缩放因子来缩放页面,并居中显示结果在指定的点
- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;
#endif
// 导航相关API
// 标识是否支持左、右swipe手势是否可以前进、后退
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;
// 是个WKBackForwardList对象,记录网页中跳转的记录
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
// 是否可以招待goback操作,它是支持KVO的
@property (nonatomic, readonly) BOOL canGoBack;
// 是否可以执行gofarward操作,支持KVO
@property (nonatomic, readonly) BOOL canGoForward;
// 返回上一页面,如果不能返回,则什么也不干
- (nullable WKNavigation *)goBack;
// 进入下一页面,如果不能前进,则什么也不干
- (nullable WKNavigation *)goForward;
/* 前进/后退到一个指定的记录上
item必须是WebView的WKBackForwardList中的一个记录
返回一个新的WKNavigation对象
如果已经在当前记录,或者没有找到,则会返回nil
*/
- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
/* API_AVAILABLE(macosx(10.11), ios(9.0))
在iOS上,这个属性只在支持3D Touch的设备上支持
在iOS 10及以后的系统版本默认值是YES,之前的默认值是NO
如果将该属性的值设置为YES,则iOS用户可以按下链接来预览链接,并可以检测到地址和电话号码等数据。这样的预览被用户称为peeks
如果用户在链接预览上更深入地按下,预览将pop到目标网址。该动作会将应用程序切换到Safari
如果您想在iOS中支持链接预览,但又想要在应用程序中保留用户,那么您可以将WKWebView类转换为SFSafariViewController类
如果您使用WebView作为应用内浏览器,那么进行这种更改是最佳选择
SFSafariViewController自动支持链接预览
iOS 10.0+开始支持自定义链接预览,你也可以通过这种方式来实现用户预览网页并pop后仍然留在应用内而不用切换到Safari,只是你需要做一些额外的适配工作
*/
@property (nonatomic) BOOL allowsLinkPreview;
// 执行JavaScript
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
// 截屏
#if TARGET_OS_IPHONE
- (void)takeSnapshotWithConfiguration:(nullable WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(UIImage * _Nullable snapshotImage, NSError * _Nullable error))completionHandler API_AVAILABLE(ios(11.0));
WKWebViewConfiguration
WKWebViewConfiguration
只会在webview第一次初始化的时候使用,你不能用此类来改变一个已经初始化完成的webview的配置。
// OC与JS交互使用,通过此属性来执行JavaScript代码来修改页面的行为
@property (nonatomic, strong) WKUserContentController *userContentController;
//***********下面属性一般不需要设置
// 首选项设置,
//可设置最小字号, 是否允许执行js
//是否通过js自动打开新的窗口
@property (nonatomic, strong) WKPreferences *preferences;
// 是否允许播放媒体文件
@property (nonatomic) BOOL allowsAirPlayForMediaPlayback
// 需要用户来操作才能播放的多媒体类型
@property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback
// 是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
@property (nonatomic) BOOL allowsInlineMediaPlayback;
......
WKUserContentController
WKUserContentController
是JavaScript
与原生进行交互的桥梁, 主要使用的方法有:
// 注入JavaScript与原生交互协议
// JS 端可通过 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 发送消息
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
// 移除注入的协议, 在deinit方法中调用
- (void)removeScriptMessageHandlerForName:(NSString *)name;
// 通过WKUserScript注入需要执行的JavaScript代码
- (void)addUserScript:(WKUserScript *)userScript;
// 移除所有注入的JavaScript代码
- (void)removeAllUserScripts;
......
使用WKUserContentController
注入的交互协议, 需要遵循WKScriptMessageHandler
协议, 在其协议方法中获取JavaScript
端传递的事件和参数:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
WKScriptMessage
包含了传递的协议名称及参数, 主要从下面的属性中获取:
// 协议名称, 即上面的add方法传递的name
@property (nonatomic, readonly, copy) NSString *name;
// 传递的参数
@property (nonatomic, readonly, copy) id body;
// 发送消息的webView
@property (nullable, nonatomic, readonly, weak) WKWebView *webView;
//发送消息的帧
@property (nonatomic, readonly, copy) WKFrameInfo *frameInfo;
WKPreferences
// webView的偏好设置
// minimum font size in points default is 0;
@property (nonatomic) CGFloat minimumFontSize;
// 是否启用javaScript default value is YES.
@property (nonatomic) BOOL javaScriptEnabled;
// 不通过用户交互,是否可以打开窗口
// default NO in iOS and YES in OS X.
@property (nonatomic) BOOL javaScriptCanOpenWindowsAutomatically;
.......
WKNavigationDelegate
// 当WevView的内容开始加载时触发
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 当页面加载内容过程中发生错误时触发
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
// 当WebView开始接收网页内容时触发
-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 当前页面加载完成后触发
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 页面发生错误时触发
-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error;
// 当WebContent进程中止时触发
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView;
/* 决定是否允许或者取消一次页面加载请求
WKNavigationAction描述了触发本次请求的行为
decisionHandler block在app已经决定允许或取消页面请求时调用
这个block携带一个参数,是WKNavigationActionPolicy枚举类型
WKNavigationActionPolicy枚举包括WKNavigationActionPolicyCancel取消和WKNavigationActionPolicyAllow允许两个枚举值
你可以立即调用该block或者保存block并在以后的时间异步调用它
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
/* 在已经收到response时决定是否允许或者取消页面的加载
WKNavigationResponse描述了response的信息
decisionHandler block在app已经决定允许或取消页面加载时调用
这个block携带一个参数,是WKNavigationResponsePolicy枚举类型
WKNavigationResponsePolicy枚举类型包括WKNavigationResponsePolicyCancel取消和WKNavigationResponsePolicyAllow允许两个枚举值
你可以立即调用该block或者保存block并在以后的时间异步调用它。
*/
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 当WebView需要响应网页的登录请求时触发
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
WKUIDelegate
// 创建新的webview
// 可以指定配置对象、导航动作对象、window特性
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
// webview关闭时回调
- (void)webViewDidClose:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
// 调用JS的alert()方法
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
// 调用JS的confirm()方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 调用JS的prompt()方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
WKWebView加载本地HTML
iOS中wkwebview加载本地html的要点
iOS之在webView中引入本地html,image,js,css文件的方法
方法一:WKWebView加载网页方法:loadRequest:
本地HTML路径获取方式有两种:pathForResource
和 URLForResource
NSString * localurl_1 = [[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSURL alloc] initWithString:localurl_1]];
[_wkWebView loadRequest:request];
NSURL * localurl = [[NSBundle mainBundle] URLForResource:@"error" withExtension:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:localurl];
[_wkWebView loadRequest:request];
WKWebView加载后发现 pathForResource
加载失败,可以看出:
1、pathForResource 的结果是纯路径
2、URLForResource 的结果是加file协议的路径
方法二:WKWebView加载网页方法:loadHTMLString: baseURL:
这种方式就是使用pathForResource
来获取路径
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSString *localHtml = [NSString stringWithContentsOfFile:urlStr encoding:NSUTF8StringEncoding error:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[_wkWebView loadHTMLString:localHtml baseURL:fileURL];
进度条显示(顺带获取title)
在适当的位置添加KVO观察者,离开时要移除
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// KVO 观察WebView 进度条 和 title
[_wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
[_wkWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 移除观察者
[_wkWebView removeObserver:self forKeyPath:@"estimatedProgress"];
[_wkWebView removeObserver:self forKeyPath:@"title"];
}
创建进度条
// Progress 进度条
_progressView = [[UIProgressView alloc] init];
_progressView.tintColor = [UIColor greenColor];
_progressView.trackTintColor = [UIColor clearColor];
[self.view addSubview:_progressView];
在KVO回调中修改页面
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
if ([change[@"new"] floatValue] < [change[@"old"] floatValue]) {
return;
}
if ([change[@"new"] floatValue] == 1.0) {
[_progressView setProgress:1.0 animated:YES];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_progressView.hidden = YES;
[_progressView setProgress:0 animated:NO];
});
} else {
_progressView.hidden = NO;
[_progressView setProgress:[change[@"new"] floatValue] animated:YES];
}
} else if ([keyPath isEqualToString:@"title"]){
self.title = change[@"new"];
}
}
日常使用中的一些问题
1、wkwebview中 h5绝对布局不生效
//新特性,解决偏移的问题
if (@available(iOS 11.0, *)) {
_wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
self.automaticallyAdjustsScrollViewInsets = NO;
}
然后:前端需要在meta标签中增加 iPhoneX的适配---适配方案viewport-fit:cover
2、iOS 12中WKWebView中表单 键盘弹起自动上移,导致的兼容问题
WKWebView会自动监听键盘弹出,并做上下移动处理(效果如同IQKeyboardManage这些库),但是在iOS12中会有一些问题,键盘收起后,控件不恢复原状,或者部分控件消失等不兼容问题
解决方案:
// iOS 12中WKWebView中表单 键盘弹起自动上移,但键盘下移webview不恢复
if (@available(iOS 12.0, *)) {
_wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
}
参考文档
简介
WKWebView详解
WKWebViewTips
[iOS]WKWebView的使用--API篇
WKWebView浅析
WKWebView代理方法解析
使用中的问题
iOS 11 WKWebView新特性
iOS WKWebView无法处理URL Scheme和App Store链接的问题解决
WKWebView使用过程中遇到的坑
iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(上)
iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(中)
iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(下)