iPhone 应该如何运行软件呢?像 OS X 上的应用程序,或者 web 页面,以及 Safari 都应该如何运行起来呢?仿照 OS X 去构建 iPhone OS 的方法已经广泛地被大家熟知了,这种方法到今天也留下了不少争议。
Web 一直是 iOS 系统上的二级公民(讽刺的是,其实现今移动网页响应式设计的出现大多是 iPhone 推动的)。UIWebView笨重难用,还有内存泄漏,和 Nirtro JavaScript 引擎谈笑风生的 Safari 不知道要比它高到哪里去了。
然而,这所有的一切都会因为WKWebView和WebKit框架其他部分的出现而发生改变。
WKWebView是现代 WebKit API 在 iOS 8 和 OS X Yosemite 应用中的核心部分。它代替了 UIKit 中的UIWebView和 AppKit 中的WebView,提供了统一的跨双平台 API。
自诩拥有 60fps 滚动刷新率、内置手势、高效的 app 和 web 信息交换通道、和 Safari 相同的 JavaScript 引擎,WKWebView毫无疑问地成为了 WWDC 2014 上的最亮点。
UIWebView&UIWebViewDelegate这个两个东西是如何在 WKWebKit 中被重构成 14 个类 3 个协议的呢。虽然这次的变化确实带来了不少的新功能,但请一定不要因此感到恐慌!
WKWebKit Framework
Classes
WKBackForwardList: 之前访问过的 web 页面的列表,可以通过后退和前进动作来访问到。
WKBackForwardListItem: webview 中后退列表里的某一个网页。
WKFrameInfo: 包含一个网页的布局信息。
WKNavigation: 包含一个网页的加载进度信息。
WKNavigationAction: 包含可能让网页导航变化的信息,用于判断是否做出导航变化。
WKNavigationResponse: 包含可能让网页导航变化的返回内容信息,用于判断是否做出导航变化。
WKPreferences: 概括一个 webview 的偏好设置。
WKProcessPool: 表示一个 web 内容加载池。
WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。
WKScriptMessage: 包含网页发出的信息。
WKUserScript: 表示可以被网页接受的用户脚本。 > -WKWebViewConfiguration: 初始化 webview 的设置。
WKWindowFeatures: 指定加载新网页时的窗口属性。
Protocols
WKNavigationDelegate: 提供了追踪主窗口网页加载过程和判断主窗口和子窗口是否进行页面加载新页面的相关方法。
WKScriptMessageHandler: 提供从网页中收消息的回调方法。
WKUIDelegate: 提供用原生控件显示网页的方法回调。
UIWebView和WKWebView的 API 区别
WKWebView继承了UIWebView大部分的接口,这让 app 来继承 WKWebKit 也简单了许多(同时随着更新 iOS 8 的越来越多这也成为了某种必需)。
看一下这两个类的 API 区别:
页面加载
访问历史
页码
WKWebView目前缺少关于页码相关的 API。
var paginationMode: UIWebPaginationMode
var paginationBreakingMode: UIWebPaginationBreakingMode
var pageLength: CGFloat
var gapBetweenPages: CGFloat
var pageCount: Int { get }
重构分离开的WKWebViewConfiguration
下面这些UIWebView的属性被重构进了在初始化WKWebView传入的设置对象:
var allowsInlineMediaPlayback: Bool
var mediaPlaybackRequiresUserAction: Bool
var mediaPlaybackAllowsAirPlay: Bool
var suppressesIncrementalRendering: Bool
JavaScript ↔︎ Swift 对话机制
相对于UIWebView最大的提升就是数据在可以 app 和 web 内容之间传递。
使用用户脚本来注入 JavaScript
WKUserScript允许在正文加载之前或之后注入到页面中。这个强大的功能允许在页面中以安全且唯一的方式操作网页内容。
一个简单的例子如下,用户改变背景的用户脚本被插入到网页中:
Swift
letsource="document.body.style.background =\"#777\";"
letuserScript=WKUserScript(source:source,injectionTime:.AtDocumentEnd,forMainFrameOnly:true)
letuserContentController=WKUserContentController()
userContentController.addUserScript(userScript)
letconfiguration=WKWebViewConfiguration()
configuration.userContentController=userContentController
self.webView=WKWebView(frame:self.view.bounds,configuration:configuration)
WKUserScript对象可以以 JavaScript 源码形式初始化,初始化时还可以传入是在加载之前还是结束时注入,以及脚本影响的是这个布局还是仅主要布局。于是用户脚本被加入到WKUserContentController中,并且以WKWebViewConfiguration属性传入到WKWebView的初始化过程中。
这个样例可以简单扩展为更为高级的页面修改方法,例如去除广告、隐藏评论等,
web 和 app 通讯机制也通过 message handler 有很大提升。
就想在Safari 审查元素功能中的console.log能在调试终端打印信息一样,网页中的信息也可以通过调用这个函数被传到 app 里:
JavaScript
window.webkit.messageHandlers.{NAME}.postMessage()
这个 API 真正神奇的地方在于 JavaScript 对象可以自动转换为 Objective-C 或 Swift 对象。
Handler 的名字可以通过WKScriptMessageHandler协议中的addScriptMessageHandler()接口函数设置:
Swift
classNotificationScriptMessageHandler:NSObject,WKScriptMessageHandler{funcuserContentController(userContentController:WKUserContentController,didReceiveScriptMessagemessage:WKScriptMessage!){println(message.body)}}letuserContentController=WKUserContentController()lethandler=NotificationScriptMessageHandler()userContentController.addScriptMessageHandler(handler,name:"notification")
于是当通知进入 app 的时候,比如说在页面中创建一个新对象,相关信息就可以这样传递:
JavaScript
window.webkit.messageHandlers.notification.postMessage({body:"..."});
添加用户脚本来对 web 事件监听并用 Message Handler 将信息传回 app。
同样的方法也可以用来收集页面信息用于 app 的页面展示或数据分析。
例如,如果某人要针对 NSHipster.com 做一个特别的浏览器,就可以加一个能够呼出相似文章列表的按钮:
JavaScriptSwift
// document.location.href == "http://nshipster.com/webkit"functiongetRelatedArticles(){varrelated=[];varelements=document.getElementById("related").getElementsByTagName("a");for(i=0;i
如果你的 app 只是对网页内容做了很简单的一层包装,那么WKWebView可以彻底改变这种状况。你对于性能和兼容性的所有愿望都将变为现实。所有你想要的东西都在这了。
如果你是一个原生纯粹主义者,你可能会被 iOS 8 新带来强大和扩展性功能吓到。有一个秘密就是,例如 Messages 这种原生应用都应用了 WebKit 来渲染复杂的页面元素。你可能尚且没有意识到,但事实是,webview 在移动开发最佳实践中应该得到一席之地。