iOS开发之iOS与js的交互
2016-03-21 16:06
567 查看
iOS中UIWebView与js的交互
1.什么是DOM?
通过 JavaScript,您可以重构整个 HTML 文档。您可以添加、移除、改变或重排页面上的项目。要改变页面的某个东西,JavaScript 就需要获得对 HTML 文档中所有元素进行访问的入口。这个入口,连同对 HTML 元素进行添加、移动、改变或移除的方法和属性,都是通过文档对象模型来获得的(DOM)。
大家应该都学过DOM解析,我们来看一下XML DOM和HTML DOM的定义?
XML DOM:定义了一套标准的针对 XML 文档的对象;
HTML DOM:定义了一套标准的针对 HTML 文档的对象;
2.通过DOM访问HTML文档中的节点?
可以通过getElementById() 和 getElementsByTagName() 这两种方法,可查找整个 HTML 文档中的任何 HTML 元素。
3.stringByEvaluatingJavaScriptFromString: 方法,可以实现webview强大的交互功能;
例如:
4.使用UIWebView来加载网页时,怎么获取网页上这个按钮点击的事件?这个按钮本身就绑定了一个url,怎么让它点击之后当前的UIWebView不去加载这个url呢?是否能够换成我们想要跳转的界面,答案是肯定的!
解决办法:UIWebView的协议里面有个方法叫- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;可以获取它加载的网页上面的事件,比如单击了图片,单击了按钮等等。这个方法会在加载网页时执行,我们只需要在这个方法里,获取到js中的onclick点击事件的这个按钮,然后,阻止它去执行事件,然后,获取到js当中的按钮,重新添加一个我们自定义实现的事件即可!
代码如下:(不要忘了设置控制器为webView的代理,遵守协议UIWebViewDelegate)
从iOS7开始 苹果公布了JavaScriptCore.framework 它使得JS与OC的交互更加方便了.首先,在Xcode工程中我们需要导入JavaScriptCore.framework框架;
(导入框架的步骤,此处我不详细介绍了)
选中JavaScriptCore.framework后 点击右下角Add 添加完成.
创建完成之后我们导入一下头文件,
接着我们需要在网页加载完成的方法里,获取js中的按钮,并重新绑定事件即可;
如果为webView添加了delegate对象并实现该接口,那么在webView加载任何一个frame之前都会delegate对象的该方法,该方法的返回值用以控制是否允许加载目标链接页面的内容,返回YES将直接加载内容,NO则反之。并且UIWebViewNavigationType枚举,定义了页面中用户行为的分类,包括:
1.UIWebViewNavigationTypeLinkClicked,用户触击了一个链接。
2.UIWebViewNavigationTypeFormSubmitted,用户提交了一个表单。
3.UIWebViewNavigationTypeBackForward,用户触击前进或返回按钮。
4.UIWebViewNavigationTypeReload,用户触击重新加载的按钮。
5.UIWebViewNavigationTypeFormResubmitted,用户重复提交表单。
6.UIWebViewNavigationTypeOther,发生其它行为。
缺点:iOS的UIWebView比较耗内存,加载几个网上的网页之后就爆内存警告了,不处理的话很容易被评估拒掉APP。
解决办法:iOS的webView有两个类,一个叫UIWebView,另一个是WKWebView。WKWebView就是为了取代UIWebView而出现的,在APP开发中,若不需要兼容iOS8之前版本,都应该使用WKWebView。WKWebView的确比UIWebView强大很多,与JS交互的能力显示增强,在加载速度上有所提升。
WKWebView新特性
性能、稳定性、功能大幅度提升
允许JavaScript的Nitro库加载并使用(UIWebView中限制)
支持了更多的HTML5特性
此处使用swift里面的API给大家讲解:(需要在使用的地方引入模块 import Webkit)
1).加载HTML的方式与UIWebView的方式大致相同。其中,loadFileURL方法通常用于加载服务器的HTML页面或者JS,而loadHTMLString方法通常用于加载本地HTML或者JS:
方法如下:
2).与js交互用到的三个代理:
WKNavigationDelegate,与页面导航加载相关;
WKUIDelegate,与JS交互时的ui展示相关,比较JS的alert、confirm、prompt;
WKScriptMessageHandler,与js交互相关,通常是ios端注入名称,js端通过window.webkit.messageHandlers.{NAME}.postMessage()来发消息到iOS端;
(1).首先,我们在ViewController中先遵守协议:
(2).我们可以先创建一个WKWebView的配置项WKWebViewConfiguration,这可以设置偏好设置,与网页交互的配置,注入对象,注入js等:
(3).创建对象并遵守代理:
(4).加载我们的本地HTML页面:
(5).我们再添加前进、后退按钮和添加一个加载进度的控制显示在Webview上:
(6).对于前进后退的事件处理就很简单的,要注意判断一下是否可以后退、前进才调用:
(2).然后就可以重写监听的方法来处理。这里只是取页面的标题,更新加载的进度条,在加载完成时,手动调用执行一个JS方法:
(3).我们看看WKUIDelegate的几个代理方法,虽然不是必须实现的,但是如果我们的页面中有调用了js的alert、confirm、prompt方法,我们应该实现下面这几个代理方法,然后在原来这里调用native的弹出窗,因为使用WKWebView后,HTML中的alert、confirm、prompt方法调用是不会再弹出窗口了,只是转化成ios的native回调代理方法:
1.什么是DOM?
通过 JavaScript,您可以重构整个 HTML 文档。您可以添加、移除、改变或重排页面上的项目。要改变页面的某个东西,JavaScript 就需要获得对 HTML 文档中所有元素进行访问的入口。这个入口,连同对 HTML 元素进行添加、移动、改变或移除的方法和属性,都是通过文档对象模型来获得的(DOM)。
大家应该都学过DOM解析,我们来看一下XML DOM和HTML DOM的定义?
XML DOM:定义了一套标准的针对 XML 文档的对象;
HTML DOM:定义了一套标准的针对 HTML 文档的对象;
2.通过DOM访问HTML文档中的节点?
可以通过getElementById() 和 getElementsByTagName() 这两种方法,可查找整个 HTML 文档中的任何 HTML 元素。
3.stringByEvaluatingJavaScriptFromString: 方法,可以实现webview强大的交互功能;
例如:
//在界面当中使用js代码实现插入一张图片 NSString *str4 =@"var img = document.createElement('img');" "img.src = 'img_01.jpg';" "img.width = '320';" "img.height = '180';" "document.body.appendChild(img);"; //让webView去执行js代码操作 [webView stringByEvaluatingJavaScriptFromString:str4];
说明:经常遇到的一个问题,iOS中webView怎么样去截取加载的js中的button的点击事件,以及自定义按钮的点击事件?
4.使用UIWebView来加载网页时,怎么获取网页上这个按钮点击的事件?这个按钮本身就绑定了一个url,怎么让它点击之后当前的UIWebView不去加载这个url呢?是否能够换成我们想要跳转的界面,答案是肯定的!
解决办法:UIWebView的协议里面有个方法叫- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;可以获取它加载的网页上面的事件,比如单击了图片,单击了按钮等等。这个方法会在加载网页时执行,我们只需要在这个方法里,获取到js中的onclick点击事件的这个按钮,然后,阻止它去执行事件,然后,获取到js当中的按钮,重新添加一个我们自定义实现的事件即可!
代码如下:(不要忘了设置控制器为webView的代理,遵守协议UIWebViewDelegate)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { //判断是否是单击,如果是webView上的单击事件,则返回NO,不去执行js点击事件 if (navigationType == UIWebViewNavigationTypeLinkClicked) { NSURL *url = [request URL]; if([[UIApplication sharedApplication]canOpenURL:url]) { [[UIApplication sharedApplication]openURL:url]; } return NO; } return YES; }
从iOS7开始 苹果公布了JavaScriptCore.framework 它使得JS与OC的交互更加方便了.首先,在Xcode工程中我们需要导入JavaScriptCore.framework框架;
(导入框架的步骤,此处我不详细介绍了)
选中JavaScriptCore.framework后 点击右下角Add 添加完成.
创建完成之后我们导入一下头文件,
#import <JavaScriptCore/JavaScriptCore.h>
接着我们需要在网页加载完成的方法里,获取js中的按钮,并重新绑定事件即可;
//网页加载完成调用此方法 - (void)webViewDidFinishLoad:(UIWebView *)webView { //首先创建JSContext 对象(此处通过当前webView的键获取到jscontext) JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; //省略号中是获取需要js当中需要操作的按钮,可以通过getElementById() 或者 getElementsByTagName() 获取js中的按钮对象,此处主要看你要获取哪个按钮对象,自己去查看html源码查找即可! NSString *returnJS=@"alert('......')"; //准备执行的js代码 [context evaluateScript:returnJS];//通过oc方法调用js事件,执行按钮事件即可! //如果你想在iOS工程中获取html里所有的内容,可以通过下面的方法获取 // 获取所有的网页内容 NSString *str = @"document.body.outerHTML"; NSString *html = [webView stringByEvaluatingJavaScriptFromString:str]; NSLog(@"%@", html); }
如果为webView添加了delegate对象并实现该接口,那么在webView加载任何一个frame之前都会delegate对象的该方法,该方法的返回值用以控制是否允许加载目标链接页面的内容,返回YES将直接加载内容,NO则反之。并且UIWebViewNavigationType枚举,定义了页面中用户行为的分类,包括:
1.UIWebViewNavigationTypeLinkClicked,用户触击了一个链接。
2.UIWebViewNavigationTypeFormSubmitted,用户提交了一个表单。
3.UIWebViewNavigationTypeBackForward,用户触击前进或返回按钮。
4.UIWebViewNavigationTypeReload,用户触击重新加载的按钮。
5.UIWebViewNavigationTypeFormResubmitted,用户重复提交表单。
6.UIWebViewNavigationTypeOther,发生其它行为。
缺点:iOS的UIWebView比较耗内存,加载几个网上的网页之后就爆内存警告了,不处理的话很容易被评估拒掉APP。
解决办法:iOS的webView有两个类,一个叫UIWebView,另一个是WKWebView。WKWebView就是为了取代UIWebView而出现的,在APP开发中,若不需要兼容iOS8之前版本,都应该使用WKWebView。WKWebView的确比UIWebView强大很多,与JS交互的能力显示增强,在加载速度上有所提升。
WKWebView的使用及js交互
WKWebView是苹果在iOS8之后引入的新组件,目的是给出一个新的高性能的WKWebView解决方案,摆脱过去WKWebView的老旧笨重特别是内存占用量巨大的问题,它使用Nitro JavaScript 引擎,这意味着所有浏览器运行JavaScript将会跟safari一样快!WKWebView新特性
性能、稳定性、功能大幅度提升
允许JavaScript的Nitro库加载并使用(UIWebView中限制)
支持了更多的HTML5特性
此处使用swift里面的API给大家讲解:(需要在使用的地方引入模块 import Webkit)
1).加载HTML的方式与UIWebView的方式大致相同。其中,loadFileURL方法通常用于加载服务器的HTML页面或者JS,而loadHTMLString方法通常用于加载本地HTML或者JS:
方法如下:
public func loadRequest(request: NSURLRequest) -> WKNavigation? // iOS 9.0以后才支持 @available(iOS 9.0, *) public func loadFileURL(URL: NSURL, allowingReadAccessToURL readAccessURL: NSURL) -> WKNavigation? // 通常用于加载本地HTML或者JS public func loadHTMLString(string: String, baseURL: NSURL?) -> WKNavigation? // iOS 9.0以后才支持 @available(iOS 9.0, *) public func loadData(data: NSData, MIMEType: String, characterEncodingName: String, baseURL: NSURL) -> WKNavigation
2).与js交互用到的三个代理:
WKNavigationDelegate,与页面导航加载相关;
WKUIDelegate,与JS交互时的ui展示相关,比较JS的alert、confirm、prompt;
WKScriptMessageHandler,与js交互相关,通常是ios端注入名称,js端通过window.webkit.messageHandlers.{NAME}.postMessage()来发消息到iOS端;
(1).首先,我们在ViewController中先遵守协议:
class ViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate
(2).我们可以先创建一个WKWebView的配置项WKWebViewConfiguration,这可以设置偏好设置,与网页交互的配置,注入对象,注入js等:
// 创建一个webiview的配置项 let configuretion = WKWebViewConfiguration() // Webview的偏好设置 configuretion.preferences = WKPreferences() configuretion.preferences.minimumFontSize = 10 configuretion.preferences.javaScriptEnabled = true // 默认是不能通过JS自动打开窗口的,必须通过用户交互才能打开 configuretion.preferences.javaScriptCanOpenWindowsAutomatically = false // 通过js与webview内容交互配置 configuretion.userContentController = WKUserContentController() // 添加一个JS到HTML中,这样就可以直接在JS中调用我们添加的JS方法 let script = WKUserScript(source: "function showAlert() { alert('在载入webview时通过Swift注入的JS方法'); }", injectionTime: .AtDocumentStart,// 在载入时就添加JS forMainFrameOnly: true) // 只添加到mainFrame中 configuretion.userContentController.addUserScript(script) // 添加一个名称,就可以在JS通过这个名称发送消息: // window.webkit.messageHandlers.AppModel.postMessage({body: 'xxx'}) configuretion.userContentController.addScriptMessageHandler(self, name: "AppModel")
(3).创建对象并遵守代理:
self.webView = WKWebView(frame: self.view.bounds, configuration: configuretion) self.webView.navigationDelegate = self self.webView.UIDelegate = self
(4).加载我们的本地HTML页面:
let url = NSBundle.mainBundle().URLForResource("index", withExtension: "html") self.webView.loadRequest(NSURLRequest(URL: url!)) self.view.addSubview(self.webView);
(5).我们再添加前进、后退按钮和添加一个加载进度的控制显示在Webview上:
self.progressView = UIProgressView(progressViewStyle: .Default) self.progressView.frame.size.width = self.view.frame.size.width self.progressView.backgroundColor = UIColor.redColor() self.view.addSubview(self.progressView) self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "上一个界面", style: .Done, target: self, action: "previousPage") self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "下一个界面", style: .Done, target: self, action: "nextPage")
(6).对于前进后退的事件处理就很简单的,要注意判断一下是否可以后退、前进才调用:
func previousPage() { if self.webView.canGoBack { self.webView.goBack() } } func nextPage() { if self.webView.canGoForward { self.webView.goForward() } }
WKWebView的KVO
(1).对于WKWebView,有三个属性支持KVO,因此我们可以监听其值的变化,分别是:loading,title,estimatedProgress,对应功能表示为:是否正在加载中,页面的标题,页面内容加载的进度(值为0.0~1.0);// 监听支持KVO的属性 self.webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil) self.webView.addObserver(self, forKeyPath: "title", options: .New, context: nil) self.webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)
(2).然后就可以重写监听的方法来处理。这里只是取页面的标题,更新加载的进度条,在加载完成时,手动调用执行一个JS方法:
// MARK: - KVO override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { if keyPath == "loading" { print("loading") } else if keyPath == "title" { self.title = self.webView.title } else if keyPath == "estimatedProgress" { print(webView.estimatedProgress) self.progressView.setProgress(Float(webView.estimatedProgress), animated: true) } // 已经完成加载时,我们就可以做我们的事了 if !webView.loading { // 手动调用JS代码 let js = "callJsAlert()"; self.webView.evaluateJavaScript(js) { (_, _) -> Void in print("call js alert") } UIView.animateWithDuration(0.55, animations: { () -> Void in self.progressView.alpha = 0.0; }) } }
(3).我们看看WKUIDelegate的几个代理方法,虽然不是必须实现的,但是如果我们的页面中有调用了js的alert、confirm、prompt方法,我们应该实现下面这几个代理方法,然后在原来这里调用native的弹出窗,因为使用WKWebView后,HTML中的alert、confirm、prompt方法调用是不会再弹出窗口了,只是转化成ios的native回调代理方法:
// MARK: - WKUIDelegate // 这个方法是在HTML中调用了JS的alert()方法时,就会回调此API。 // 注意,使用了`WKWebView`后,在JS端调用alert()就不会在HTML // 中显示弹出窗口。因此,我们需要在此处手动弹出ios系统的alert。 func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) { let alert = UIAlertController(title: "Tip", message: message, preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in // We must call back js completionHandler() })) self.presentViewController(alert, animated: true, completion: nil) } func webView(webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (Bool) -> Void) { let alert = UIAlertController(title: "Tip", message: message, preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in // 点击完成后,可以做相应处理,最后再回调js端 completionHandler(true) })) alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (_) -> Void in // 点击取消后,可以做相应处理,最后再回调js端 completionHandler(false) })) self.presentViewController(alert, animated: true, completion: nil) } func webView(webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: (String?) -> Void) { let alert = UIAlertController(title: prompt, message: defaultText, preferredStyle: .Alert) alert.addTextFieldWithConfigurationHandler { (textField: UITextField) -> Void in textField.textColor = UIColor.redColor() } alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in // 处理好之前,将值传到js端 completionHandler(alert.textFields![0].text!) })) self.presentViewController(alert, animated: true, completion: nil) } func webViewDidClose(webView: WKWebView) { print(__FUNCTION__) }
苹果提供了更多简便的方式让native与js交互更加方便,通过让native注入名称,然后在js端自动转换成js的对象,就可以在js端通过对象的方式来发送消息到native端。如此一来,就简化了js与native的交互了!
相关文章推荐
- iOS 栈和堆的区别
- iOS边练边学--transform的简单介绍并且用transform实现键盘处理
- iOS开发 - runtime运行时一些实用方法
- iOS添加蒙层(遮盖层),遮盖层上放控件
- 获取DeviceToken值,iOS推送
- iOS知识 exclusiveTouch
- iOS经典资料收集
- KVO的概述与使用
- iOS的socket开发基础
- iOS开发调试
- iOS 禁止横屏
- iOS证书失效
- 超全!整理常用的iOS第三方资源
- IOS开发之Quartz2D绘图的使用
- iOS离线缓存
- ios .a静态库和framework静态库的创建以及使用
- 三言两语frame&bounds
- iOS WebView调用JS的一个小坑
- 搜索图标居中的搜索框~iOS风格搜索框
- iOS自定义加载等待视图-MBProgressHUD