iOS开发——从UIWebView说起

相比native开发,h5以其相对成熟、随时发布、开发成本低等特点,成为各大移动应用采取的通用web方案,在移动开发中有着举足重轻的地位。而无论Android还是iOS都有专门用来渲染h5页面的组件,即WebView。本篇主要从UIWebView开始,谈一谈iOS的混合开发。
什么是UIWebView
UIWebView是iOS内置的浏览器控件,继承自UIView,遵循UIScrollViewDelegate协议,方便开发者监听滚动事件。从iOS2.0开始,iOS开发者就可以使用UIWebView展示web内容。使用方法很简单,创建一个UIWebView对象,添加到当前页面的根视图上,然后load你要加载的url或本地资源即可。你还可以通过调用UIWebView提供的api实现页面的前进、后退、刷新等动作,甚至你还可以动态的设置网页内容。系统应用Safari就是采用这个控件实现。
UIWebView不但能加载远程的网页资源,还能加载绝大部分的常见文件,例如:htm/html、txt、pdf/keynote、ppt、doc/docx、音视频文件等等,可谓功能十分强大!后面将简述如何加载不同的文件方法。
UIWebView常用属性和方法
// 接收加载过程消息的代理对象,详见代理方法
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
// 与webview关联的滚动视图对象,通过该对象去处理webview的滚动行为
@property (nonatomic, readonly, strong) UIScrollView *scrollView NS_AVAILABLE_IOS(5_0);
// 当前webview加载内容位置的url请求
@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
// web页面是否支持缩放来适应view视图,默认值为NO,YES表示用户可以缩放页面
@property (nonatomic) BOOL scalesPageToFit;
// 是否可以回退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
// 是否可以前进
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
// 是否正在加载
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
// 是否播放内联视频,iphone默认为NO,设置为NO时为全屏播放
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0)
// 是否自动播放内嵌视屏,默认为YES
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0)
// 加载url请求,异步加载
- (void)loadRequest:(NSURLRequest *)request
// 设置主页的内容及baseURL,避免不安全url请求
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL
// 设置页面的内容、mimeType、encoding以及baseURL
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL
// 重新加载当前页面
-(void)reload;
// 停止加载任何主帧或子帧正在加载内容,如果没有内容加载,则什么也不做
-(void)stopLoading;
// 回退到history中的上个web页面
-(void)goBack;
// 前进到history中的下个页面
-(void)goForward;
// 执行js语句,返回执行结果
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
UIWebView协议UIWebViewDelegate
// url请求将要加载时的代理方法,即webview加载一个帧之前会调用该方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// 开始加载时的代理方法,即webview已经开始加载时会调用该代理方法
- (void)webViewDidStartLoad:(UIWebView *)webView;
// 与上一个对应,完成加载时的代理方法,即webview完成加载时会调用该代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView;
// 加载失败时的代理方法,即webview加载失败时会调用该方法,error指示失败的原因
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
UIWebView 页面加载
在UIWebView提供的接口中,加载页面内容的方法有3种:
- (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;
其中,第一种方法用于加载一个基于文件路径的URL请求,文件路径可以是本地,也可以是一个链接。而后面两种方法一般用于加载本地文件内容,正如注释提到了,避免不安全的url请求。
这里主要介绍一下本地文件的加载方法,UIWebView支持多种格式文件的加载,但是加载方式完全相同。
1. 获取文件路径
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
2. 构造URL请求
NSURLRequest *request = [NSURLRequest requestWithURL:fileUrl];
3. 加载请求内容
[webView loadRequest:request];
你也可以通过加载本地文件内容的方法试一试~
UIWebView的调试
UIWebView中展示的内容,包括html、js、css等,我们很容易通过调试工具去debug,常用的包括Safari、Chrome。
Safari调试
准备工作:
1.打开模拟器(iphone设备)中的web检查器功能

2.打开Mac上Safari的开发者模式

3.通过UIWebView展示一个web页面,然后打开Safari,开发->模拟器(真机)名->选择对应的html

Chrome调试
如何操作见这里:
如何用 Chrome DevTools 调试 iOS Safari
UIWebView与native交互
h5页面不仅用于展示web资源,也提供丰富的用户交互,包括点击事件、外部跳转、定位等等,因而与native系统进行交互是少不了的。例如最简单的场景,由于h5的局限性,无法直接访问用户手机的摄像头,这个时候就可以通过伪协议约定相应接口,通过js调用oc的方式将动作交由native端去实现,然后native完成动作后再回调给h5页面,实现双方的交互。
UIWebView与native的交互主要通过下面这两个方法去实现:
// UIWebView的代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// UIWebView的实例方法
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
操作比较简单,大家可以自行尝试。
其实,专业的js与oc通信并不是通过UIWebView去和native进行的,而是使用包括iOS7.0支持的JavaScriptCore、第三方开源库WebViewJavascriptBridge等更加方便、易于扩展的方法,通过在两者之间架构一层bridge去满足双方的相互调用。后面将通过专门的文章细致聊一下常用的oc与js交互方法,为hybrid跨平台方案提供应用基础。
UIWebView使用注意事项(坑点)
-
释放UIWebView对象之前首先要将其delegate设置为nil,否则可能会出现异常crash等问题,一般在dealloc方法中处理;
-
UIWebView对象不应该被添加到UIScrollView对像的视图上,否则可能会出现无法预料的问题,因为系统无法正确处理两个视图的点击、滚动、手势操作等事件;
-
加载本地html文件时,确保使用loadHTMLString:baseURL:方法,不要使用loadRequest:方法;
-
UIWebView的stringByEvaluatingJavaScriptFromString:方法为同步执行,当前线程将等待该方法执行完成,因此调用该方法将会导致app出现挂起现象;
小结
WebView的使用,满足了业务的快速迭代及随时发版,也满足了web开发向移动端开发迁移,目前作为通用的混合开发方案,也变得越来越成熟。但是UIWebView由于自身的问题和不足,一直困扰着开发者。后面将详细介绍UIWebView的替代品——WKWebView,iOS8.0提供的一款基于Webkit的iOS系统浏览器,极大改善开发者和用户的体验。