iOS基础之webview
iOS中的Webview与Android中的Webview有一些差异,包括网络权限、加载页面、与JS交互等。不过大体使用思路是类似的,下面总结一下iOS中的Webview。
参考:
概述
现在iOS提供了两套webview:UIWebView
和WKWebView
。根据官方文档的建议,iOS8之后建议使用WKWebView
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.
由于历史原因,我司iOS客户端貌似二者均有采用。因此不可避免地均需要了解一下~。先看看最新的WKWebView
吧。
WKWebView
使用方式
基本的使用方式是:构建请求对象,实例化webview对象,然后发送请求即可
- (void)setWKWebview{
// 加载路径
NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
// 构造请求对象
NSURLRequest *request =[NSURLRequest requestWithURL:url];
// 实例化webview
WKWebView *webview = [[WKWebView alloc] initWithFrame:self.view.bounds];
// 将webview加载至当前视图
[self.view addSubview:webview];
// 发送请求
[webview loadRequest:request];
}
记得引入#import<WebKit/WebKit.h>
文件。
本地服务器
在iPhone模拟器上,可以直接在safari中通过localhost:port
访问本地服务器。
在Android是通过10.0.2.2:port
来访问本地服务器的。
请求http报错
在测试加载本地服务器文件时一直显示页面空白,找了半天也没有发现原因(没有任何错误也没有任何警告),后来测试iOS的网络请求时发现下面的警告
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
可以在info.plist
中修改相关的权限,参考https://stackoverflow.com/questions/31254725/transport-security-has-blocked-a-cleartext-http.
具体步骤:
- 在资源管理器打开
info.plist
,增加一项App Transport Security Settings
,选择数据类型为Dictionary
- 在
App Transport Security Settings
下增加一项Allow Arbitrary Loads
,选择类型为Boolean
,值为YES
info.plist
与AndroidManifest.xml
功能相似,用来修改程序的一些基础配置,这个后面再了解。
添加加载进度条
在手机QQ中打开网页可以看见一个渐变的进度条,非常漂亮,实际上WKWebview的进度条是可以定制的。参考:iOS WKWebView添加网页加载进度条。
JavaScript交互
WKWebview与JS的交互十分简单,调用webview对象的evaluateJavaScript
即可。
由于是测试Demo,为了减少文件数,我们将webview的navigationDelegate
代理指向当前视图控制器自身,
这是因为我们需要使用navigationDelegate
代理的didFinishNavigation
方法。
- (void)setWKWebview{
// 初始化webview对象
webview.navigationDelegate = self;
// ...
}
执行JavaScript代码
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
[webView evaluateJavaScript:@"document.body.style.backgroundColor = 'blue'" completionHandler:^(id result, NSError *error) {
NSLog(@"Result %@", result);
}];
}
上述代码从字面上很容易理解,页面加载完成执行一串JavaScript代码。关于WKNavigationDelegate更多可参考这里。此外如果需要测试alert
等方法,需要实现WKUIDelegate代理。
evaluateJavaScript
方法接受两个参数,需要执行的JS代码,以及执行完毕后的回调,执行完的JS表达式值会作为参数传递给该回调函数(比如上面的代码返回blue
字符串)。
JavaScript调用原生代码
实际上目前工作中接触到的项目,很少有通过OC去执行JS代码的。相反地,一般是通过原生实现接口然后封装成SDK提供给JS,比如跳转原生界面、分享页面等。这是通过WKScriptMessageHandler
协议来实现的。
WKScriptMessageHandler
依赖于两个类:
WKUserContentController
,用来向页面注入相关的原生接口,也可以直接向页面注入JS代码貌似WKScriptMessage
,可以理解为JS向原生方法传递的参数负载
为了实现调用原生的功能,在实例化webview时需要额外的一些配置,以便传入上面的两个类
- (void)setWKWebview{
NSURL *url = [NSURL URLWithString:@"http://localhost:9999"];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
// 定义JS可调用的原生方法
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addScriptMessageHandler:self name:@"nativeTest"];
// 配置webview,此处使用上面实例化的userContentController对象
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = userContentController;
// 传入配置
WKWebView *webview = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
[self.view addSubview:webview];
[webview loadRequest:request];
}
然后实现一个通用的消息接收器,根据消息携带的方法名和参数,执行对应的原生方法,达到JS调用原生的目的
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// message 包含了对应的方法名和参数
NSLog(@"方法名:%@", message.name);
NSLog(@"参数:%@", message.body);
// 方法名
NSString *methods = [NSString stringWithFormat:@"%@:", message.name];
SEL selector = NSSelectorFromString(methods);
// 调用方法
if ([self respondsToSelector:selector]) {
[self performSelector:selector withObject:message.body];
} else {
NSLog(@"未定义方法:%@", methods);
}
}
做完准备工作,接下来实现相关的原生方法,这里的方法在上面addScriptMessageHandler
处声明。
- (void)nativeTest:(NSString *)str {
// 可以在原生中做许多事情...
NSLog(@"Hello this is OC");
NSLog(str);
}
最后就是web端调用相关的接口的了
btn.onclick = function(){
// window.webkit.messageHandlers 为注入的全局对象,携带相关的接口方法
console.log(window.webkit.messageHandlers)
// nativeTest接口被实现为一个对象,通过postMessage方法调用原生方法并传递相关参数
window.webkit.messageHandlers.nativeTest.postMessage('Hello from JS')
}
这样在userContentController
的message中,对应的message.name
即为‘nativeTest’,而message.body
的值即为'Hello from JS',大功告成。
还有个需要注意的问题就是JS传递参数的类型,一般来讲OC会接收一个dist
类型的参数,对应JS的对象,所以十分方便。
调试
参考iOS调试UIWebView和WKWebView加载的H5页面
模拟器调试
在模拟器上调试页面十分简单
- 打开模拟器,然后打开模拟器中的safari,打开需要调试的页面网址
- 打开Mac上的safari,在“Devalop > iOS Simulator”菜单下,会出现模拟器中已打开的网页列表,选中模拟器中打开的页面就可以调试了
这里貌似跟webview没有多大关系,实际上app中的webview页面也可以通过这种方式进行调试,十分方便~
最近在项目中遇见了一个比较神奇的BUG,在Chrome的分辨率模拟器中功能是正常的,而在iPhone的浏览器中却显示不正常,然后通过模拟器打开居然复现了这个BUG,然后根据safari调试很快就定位了问题然后得以解决,说明这个iPhone模拟器跟真机还是很接近的。
如果打开Safari,在Develop菜单下没有找到iOS Simulator
的选项,可以试试重启Safari,这个貌似是个BUG。
唯一的问题是需要掌握safari浏览器下的调试方法,与Chrome还是有一些区别的(有点不太习惯)。
真机调试
查资料的时候发现历史上使用真机调试需要注册$99的开发者账号,然后下载调试证书啥的...幸好现在2017年都快结束了,好了不说废话了,下面说说操作步骤。
获得调试证书
首先打开XCode
,选择Preferences->Accounts
,绑定Apple ID
,然后点击Manage Certificates
,新增一个调试证书即可
关联设备
通过USB连接iPhone真机,选择Window->Devices and Simulators
,可以查看到已关联的设备,貌似现在可以支持无线设备调试了,具体可以参考这里,不过我没有发现相关选项~
选择运行平台
这里有一篇文章,对iOS项目的运行做了比较详细的介绍。
XCode项目编译方案和运行目标是分开的,我们只需要在切换编译右侧的运行平台,选择对应的设备或模拟器运行项目即可。
这里遇见了几个问题:
- 项目的目标版本和真机的系统版本不一致,可以通过修改项目的
Deployment Target
版本号调整,整个项目的设置可以通过左侧资源管理器中,点击项目目录名称打开(那个蓝色的xcode图标) requires a development team
错误,原因是项目Signing
设置下的Team
默认为空导致的,选择一个开发组即可。- 在真机上提示“您的设备管理设置不允许在此台iPhone上使用开发者XX的应用”,这个可以通过iPhone设置->描述文件->开发者文件->信任即可。
好吧,上面的操作基本上都是针对iOS真机调试,不过在真机上调试webview页面,这些步骤也是必备的。
小结
至此对于iOS中的webview已有大致了解,最后的工作就是完成iPhoneX的页面样式适配~这是学习OC和WKWebview的主要目的。
此外关于OC中的一些语法,比如代理、回调函数、选择器等,需要进一步了解。
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。