转自:http://www.cnblogs.com/kenshincui/p/4042190.html#UIWebView
网络开发中还有一个常用的UI控件UIWebView,它是iOS中内置的浏览器控件,功能十分强大。如一些社交软件往往在应用程序内不需要打开其他浏览器就能看一些新闻之类的页面,就是通过这个控件实现的。需要注意的是UIWebView不仅能加载网络资源还可以加载本地资源,目前支持的常用的文档格式如:html、pdf、docx、txt等。
浏览器实现
下面将通过一个UIWebView开发一个简单的浏览器,界面布局大致如下:
在这个浏览器中将实现这样几个功能:
1.如果输入以”file://”开头的地址将加载Bundle中的文件
2.如果输入以“http”开头的地址将加载网络资源
3.如果输入内容不符合上面两种情况将使用bing搜索此内容
// // KCMainViewController.m // UIWebView // // Created by Kenshin Cui on 14-3-22. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h"' #define kFILEPROTOCOL @"file://" //注意添加如下协议 @interface KCMainViewController ()<UISearchBarDelegate,UIWebViewDelegate>{ UIWebView *_webView; UIToolbar *_toolbar; UISearchBar *_searchBar; UIBarButtonItem *_barButtonBack; UIBarButtonItem *_barButtonForward; } @end @implementation KCMainViewController #pragma mark - 界面UI事件 - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } #pragma mark - 私有方法 #pragma mark 界面布局 -(void)layoutUI{ /*添加地址栏*/ _searchBar=[[UISearchBar alloc]initWithFrame:CGRectMake(0, 20, 320, 44)]; _searchBar.delegate=self; [self.view addSubview:_searchBar]; /*添加浏览器控件*/ _webView=[[UIWebView alloc]initWithFrame:CGRectMake(0, 64, 320, 460)]; _webView.dataDetectorTypes=UIDataDetectorTypeAll;//数据检测,例如内容中有邮件地址,点击之后可以打开邮件软件编写邮件 _webView.delegate=self; [self.view addSubview:_webView]; /*添加下方工具栏*/ _toolbar=[[UIToolbar alloc]initWithFrame:CGRectMake(0, 524, 320, 44)]; UIButton *btnBack=[UIButton buttonWithType:UIButtonTypeCustom]; btnBack.bounds=CGRectMake(0, 0, 32, 32); [btnBack setImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal]; [btnBack setImage:[UIImage imageNamed:@"back_disable.png"] forState:UIControlStateDisabled]; [btnBack addTarget:self action:@selector(webViewBack) forControlEvents:UIControlEventTouchUpInside]; _barButtonBack=[[UIBarButtonItem alloc]initWithCustomView:btnBack]; _barButtonBack.enabled=NO; UIBarButtonItem *btnSpacing=[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; UIButton *btnForward=[UIButton buttonWithType:UIButtonTypeCustom]; btnForward.bounds=CGRectMake(0, 0, 32, 32); [btnForward setImage:[UIImage imageNamed:@"forward.png"] forState:UIControlStateNormal]; [btnForward setImage:[UIImage imageNamed:@"forward_disable.png"] forState:UIControlStateDisabled]; [btnForward addTarget:self action:@selector(webViewForward) forControlEvents:UIControlEventTouchUpInside]; _barButtonForward=[[UIBarButtonItem alloc]initWithCustomView:btnForward]; _barButtonForward.enabled=NO; _toolbar.items=@[_barButtonBack,btnSpacing,_barButtonForward]; [self.view addSubview:_toolbar]; } #pragma mark 设置前进后退按钮状态 -(void)setBarButtonStatus{ if (_webView.canGoBack) {//检查网页能否返回前一页 _barButtonBack.enabled=YES; }else{ _barButtonBack.enabled=NO; } if(_webView.canGoForward){//检查能否进入后一页 _barButtonForward.enabled=YES; }else{ _barButtonForward.enabled=NO; } } #pragma mark 后退 -(void)webViewBack{ [_webView goBack];//网页返回前一页 } #pragma mark 前进 -(void)webViewForward{ [_webView goForward];//网页返回后一页 } #pragma mark 浏览器请求 -(void)request:(NSString *)urlStr{ //创建url NSURL *url; //如果file://开头的字符串则加载bundle中的文件 if([urlStr hasPrefix:kFILEPROTOCOL]){ //取得文件名 NSRange range= [urlStr rangeOfString:kFILEPROTOCOL]; NSString *fileName=[urlStr substringFromIndex:range.length]; url=[[NSBundle mainBundle] URLForResource:fileName withExtension:nil]; }else if(urlStr.length>0){ //如果是http请求则直接打开网站 if ([urlStr hasPrefix:@"http"]) {//检查前缀 url=[NSURL URLWithString:urlStr]; }else{//如果不符合任何协议则进行搜索 urlStr=[NSString stringWithFormat:@"http://m.bing.com/search?q=%@",urlStr]; } urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];//url编码 url=[NSURL URLWithString:urlStr]; } //创建请求 NSURLRequest *request=[NSURLRequest requestWithURL:url]; //加载请求页面 [_webView loadRequest:request];//WebView的方法 } #pragma mark - WebView 代理方法 #pragma mark 开始加载 -(void)webViewDidStartLoad:(UIWebView *)webView{ //显示网络请求加载 [UIApplication sharedApplication].networkActivityIndicatorVisible=true;//设置网络连接指示标志 } #pragma mark 加载完毕 -(void)webViewDidFinishLoad:(UIWebView *)webView{ //隐藏网络请求加载图标 [UIApplication sharedApplication].networkActivityIndicatorVisible=false; //设置按钮状态 [self setBarButtonStatus]; } #pragma mark 加载失败 -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ NSLog(@"error detail:%@",error.localizedDescription); UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"系统提示" message:@"网络连接发生错误!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil]; [alert show]; } #pragma mark - SearchBar 代理方法 #pragma mark 点击搜索按钮或回车 -(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{ [self request:_searchBar.text]; } @end
运行效果:
其实UIWebView整个使用相当简单:创建URL->创建请求->加载请求,无论是加载本地文件还是Web内容都是这三个步骤。UIWebView内容加载事件同样是通过代理通知外界,常用的代理方法如开始加载、加载完成、加载出错等,这些方法通常可以帮助开发者更好的控制请求加载过程。
注意:UIWebView打开本地pdf、word文件依靠的并不是UIWebView自身解析,而是依靠MIME Type识别文件类型并调用对应应用打开。
UIWebView与页面交互
UIWebView与页面的交互主要体现在两方面:使用ObjC方法进行页面操作、在页面中调用ObjC方法两部分。和其他移动操作系统不同,iOS中所有的交互都集中于一个stringByEvaluatingJavaScriptFromString:方法中,以此来简化开发过程。
在iOS中操作页面
1.首先在request方法中使用loadHTMLString:加载了html内容,当然你也可以将html放到bundle或沙盒中读取并且加载。
2.然后在webViewDidFinishLoad:代理方法中通过stringByEvaluatingJavaScriptFromString: 方法可以操作页面中的元素,例如在下面的方法中读取了页面标题、修改了其中的内容。
// // KCMainViewController.m // UIWebView // // Created by Kenshin Cui on 14-3-22. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h"' @interface KCMainViewController ()<UISearchBarDelegate,UIWebViewDelegate>{ UIWebView *_webView; } @end @implementation KCMainViewController #pragma mark - 界面UI事件 - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; [self request]; } #pragma mark - 私有方法 #pragma mark 界面布局 -(void)layoutUI{ /*添加浏览器控件*/ _webView=[[UIWebView alloc]initWithFrame:CGRectMake(0, 20, 320, 548)]; _webView.dataDetectorTypes=UIDataDetectorTypeAll;//数据检测类型,例如内容中有邮件地址,点击之后可以打开邮件软件编写邮件 _webView.delegate=self; [self.view addSubview:_webView]; } #pragma mark 浏览器请求 -(void)request{ //加载html内容 NSString *htmlStr=@"<html>\ <head><title>Kenshin Cui's Blog</title></head>\ <body style=\"color:#0092FF;\">\ <h1 id=\"header\">I am Kenshin Cui</h1>\ <p>iOS 开发系列</p>\ </body></html>"; //加载请求页面 [_webView loadHTMLString:htmlStr baseURL:nil]; } #pragma mark - WebView 代理方法 #pragma mark 开始加载 -(void)webViewDidStartLoad:(UIWebView *)webView{ //显示网络请求加载 [UIApplication sharedApplication].networkActivityIndicatorVisible=true; } #pragma mark 加载完毕 -(void)webViewDidFinishLoad:(UIWebView *)webView{ //隐藏网络请求加载图标 [UIApplication sharedApplication].networkActivityIndicatorVisible=false; //取得html内容 NSLog(@"%@",[_webView stringByEvaluatingJavaScriptFromString:@"document.title"]); //修改页面内容 NSLog(@"%@",[_webView stringByEvaluatingJavaScriptFromString:@"document.getElementById('header').innerHTML='Kenshin Cui\\'s Blog'"]); } #pragma mark 加载失败 -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ NSLog(@"error detail:%@",error.localizedDescription); UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"系统提示" message:@"网络连接发生错误!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil]; [alert show]; } @end
运行效果:
页面中调用ObjC方法
页面中的js是无法直接调用ObjC方法的,但是可以变换一下思路:当需要进行一个js操作时让页面进行一个重定向,并且在重定向过程中传入一系列参数。在UIWebView的代理方法中有一个webView: shouldStartLoadWithRequest:navigationType方法,这个方法会在页面加载前执行,这样可以在这里拦截重定向,并且获取定向URL中的参数,根据这些参数约定一个方法去执行。
当访问百度搜索手机版时会发现,有时候点击页面中的某个元素可以调出iOS操作系统的UIActionSheet,下面不妨模拟一下这个过程。首先需要定义一个js方法,为了方便扩展,这个js保存在MyJs.js文件中存放到Bundle中,同时在页面中加载这个文件内容。MyJs.js内容如下:
function showSheet(title,cancelButtonTitle,destructiveButtonTitle,otherButtonTitle) { var url='kcactionsheet://?'; var paramas=title+'&'+cancelButtonTitle+'&'+destructiveButtonTitle; if(otherButtonTitle){ paramas+='&'+otherButtonTitle; } window.location.href=url+ encodeURIComponent(paramas); } var blog=document.getElementById('blog'); blog.οnclick=function(){ showSheet('系统提示','取消','确定',null); };
这个js的功能相当单一,调用showSheet方法则会进行一个重定向,调用过程中需要传递一系列参数,当然这些参数都是UIActionSheet中需要使用的,注意这里约定所有调用UIActionSheet的方法参数都以”kcactionsheet”开头。
然后在webView: shouldStartLoadWithRequest:navigationType方法中截获以“kcactionsheet”协议开头的请求,对于这类请求获得对应参数调用UIActionSheet。看一下完整代码:
// // KCMainViewController.m // UIWebView // // Created by Kenshin Cui on 14-3-22. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" @interface KCMainViewController ()<UISearchBarDelegate,UIWebViewDelegate>{ UIWebView *_webView; } @end @implementation KCMainViewController #pragma mark - 界面UI事件 - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; [self request]; } #pragma mark - 私有方法 #pragma mark 界面布局 -(void)layoutUI{ /*添加浏览器控件*/ _webView=[[UIWebView alloc]initWithFrame:CGRectMake(0, 20, 320, 548)]; _webView.dataDetectorTypes=UIDataDetectorTypeAll;//数据检测类型,例如内容中有邮件地址,点击之后可以打开邮件软件编写邮件 _webView.delegate=self; [self.view addSubview:_webView]; } #pragma mark 显示actionsheet -(void)showSheetWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle destructiveButtonTitle:(NSString *)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitle{ UIActionSheet *actionSheet=[[UIActionSheet alloc]initWithTitle:title delegate:nil cancelButtonTitle:cancelButtonTitle destructiveButtonTitle:destructiveButtonTitle otherButtonTitles:otherButtonTitle, nil]; [actionSheet showInView:self.view]; } #pragma mark 浏览器请求 -(void)request{ //加载html内容 NSString *htmlStr=@"<html>\ <head><title>Kenshin Cui's Blog</title></head>\ <body style=\"color:#0092FF;\">\ <h1 id=\"header\">I am Kenshin Cui</h1>\ <p id=\"blog\">iOS 开发系列</p>\ </body></html>"; //加载请求页面 [_webView loadHTMLString:htmlStr baseURL:nil]; } #pragma mark - WebView 代理方法 #pragma mark 页面加载前(此方法返回false则页面不再请求) -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ if ([request.URL.scheme isEqual:@"kcactionsheet"]) { NSString *paramStr=request.URL.query; NSArray *params= [[paramStr stringByRemovingPercentEncoding] componentsSeparatedByString:@"&"]; id otherButton=nil; if (params.count>3) { otherButton=params[3]; } [self showSheetWithTitle:params[0] cancelButtonTitle:params[1] destructiveButtonTitle:params[2] otherButtonTitles:otherButton]; return false; } return true; } #pragma mark 开始加载 -(void)webViewDidStartLoad:(UIWebView *)webView{ //显示网络请求加载 [UIApplication sharedApplication].networkActivityIndicatorVisible=true; } #pragma mark 加载完毕 -(void)webViewDidFinishLoad:(UIWebView *)webView{ //隐藏网络请求加载图标 [UIApplication sharedApplication].networkActivityIndicatorVisible=false; //加载js文件 NSString *path=[[NSBundle mainBundle] pathForResource:@"MyJs.js" ofType:nil]; NSString *jsStr=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; //加载js文件到页面 [_webView stringByEvaluatingJavaScriptFromString:jsStr]; } #pragma mark 加载失败 -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ NSLog(@"error detail:%@",error.localizedDescription); UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"系统提示" message:@"网络连接发生错误!" delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil]; [alert show]; } @end
运行效果: