1.WKWebView - 读取本地HTML(ios9以前)
当使用loadRequest来读取本地的HTML时,WKWebView是无法读取成功的,后台会出现如下的提示:
Could not create a sandbox extension for
原因是WKWebView是不允许通过loadRequest的方法来加载本地根目录的HTML文件。
而在iOS9的SDK中加入了以下方法来加载本地的HTML文件:
if #available(iOS 9.0, *) {
WKWebView.loadFileURL:allowingReadAccessToURL:
}
但是在iOS9以下的版本是没提供这个便利的方法的。以下为解决方案的思路,就是在iOS9以下版本时,先将本地HTML文件的数据copy到tmp目录中,然后再使用loadRequest来加载。但是如果在HTML中加入了其他资源文件,例如js,css,image等必须一同copy到temp中。这个是最蛋疼的事情了。
解决方法如下
//将文件copy到tmp目录
func fileURLForBuggyWKWebView8(fileURL: NSURL) throws -> NSURL {
// Some safety checks
var error:NSError? = nil;
if (!fileURL.fileURL || !fileURL.checkResourceIsReachableAndReturnError(&error)) {
throw error ?? NSError(
domain: "BuggyWKWebViewDomain",
code: 1001,
userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
}
// Create "/temp/www" directory
let fm = NSFileManager.defaultManager()
let tmpDirURL = NSURL.fileURLWithPath(NSTemporaryDirectory()).URLByAppendingPathComponent("www")
try! fm.createDirectoryAtURL(tmpDirURL, withIntermediateDirectories: true, attributes: nil)
// Now copy given file to the temp directory
let dstURL = tmpDirURL.URLByAppendingPathComponent(fileURL.lastPathComponent!)
let _ = try? fileMgr.removeItemAtURL(dstURL)
try! fm.copyItemAtURL(fileURL, toURL: dstURL)
// Files in "/temp/www" load flawlesly :)
return dstURL
}
//方法调用
var filePath = NSBundle.mainBundle().pathForResource("file", ofType: "pdf")
if #available(iOS 9.0, *) {
// iOS9. One year later things are OK.
webView.loadFileURL(fileURL, allowingReadAccessToURL: fileURL)
} else {
// iOS8. Things can be workaround-ed
// Brave people can do just this
// fileURL = try! pathForBuggyWKWebView8(fileURL)
// webView.loadRequest(NSURLRequest(URL: fileURL))
do {
fileURL = try fileURLForBuggyWKWebView8(fileURL)
webView.loadRequest(NSURLRequest(URL: fileURL))
} catch let error as NSError {
print("Error: " + error.debugDescription)
}
}
2. WKWebView - WKNavigationDelegate使用
特别提醒一点,在使用以下delegate的方法时
optional func webView(_ webView: WKWebView!, decidePolicyForNavigationAction navigationAction: WKNavigationAction!, decisionHandler decisionHandler: ((WKNavigationActionPolicy) -> Void)!)
需执行decisionHandler的block。
例如:
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
let url = navigationAction.request.URL
if let url = url, host = url.host {
if WebLink.isNeedHost(host) {
decisionHandler(.Allow)
return
}
UIApplication.sharedApplication().openURL(url)
decisionHandler(.Cancel)
return
}
decisionHandler(.Cancel)
}
3.WKWebView - JS执行方法
WKWebView JS执行方法与UIWebView不一样了。
func evaluateJavaScript(_ javaScriptString: String!, completionHandler completionHandler: ((AnyObject!, NSError!) -> Void)!)
completionHandler 拥有两个参数,一个是返回错误,一个可以返回执行脚本后的返回值
4.WKWebView - Toolbar (KVO)
WKWebView 底部Toolbar,前进后退以及刷新的功能,采用KVO监听的方式
let webView: WKWebView = WKWebView()
var webLink: WebLink?
@IBOutlet var webLinkToolbar: WebLinkToolbar!
private var kvoContext = 0
// MARK: Initialize
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(_ webLink: WebLink) {
self.init(nibName: nil, bundle: nil)
self.webLink = webLink
}
override func viewDidLoad() {
super.viewDidLoad()
/*! @abstract A Boolean value indicating whether horizontal swipe gestures
will trigger back-forward list navigations.
@discussion The default value is NO.
*/
webView.allowsBackForwardNavigationGestures = true
webView.navigationDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = false
webView.addObserver(self, forKeyPath: "loading", options: .New, context: &kvoContext)
//add constraints for webview
self.view.backgroundColor = UIColor.whiteColor()
self.view.insertSubview(webView, atIndex: 0)
self.view.addConstraints([
NSLayoutConstraint(item: self.view, attribute: .Top, relatedBy: .Equal, toItem: webView, attribute: .Top, multiplier: CGFloat(1.0), constant: CGFloat(0.0)),
NSLayoutConstraint(item: self.view, attribute: .Right, relatedBy: .Equal, toItem: webView, attribute: .Right, multiplier: CGFloat(1.0), constant: CGFloat(0.0)),
NSLayoutConstraint(item: self.view, attribute: .Bottom, relatedBy: .Equal, toItem: webView, attribute: .Bottom, multiplier: CGFloat(1.0), constant: CGFloat(0.0)),
NSLayoutConstraint(item: self.view, attribute: .Left, relatedBy: .Equal, toItem: webView, attribute: .Left, multiplier: CGFloat(1.0), constant: CGFloat(0.0))
])
self.view.updateConstraints()
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
//load toolbar form xib
NSBundle.mainBundle().loadNibNamed("WebLinkToolbar", owner: self, options: nil)
self.webLinkToolbar.translatesAutoresizingMaskIntoConstraints = false
self.webLinkToolbar.goBackButton.addTarget(self, action: "goBack:", forControlEvents: .TouchUpInside)
self.webLinkToolbar.goForwardButton.addTarget(self, action: "goForward:", forControlEvents: .TouchUpInside)
self.webLinkToolbar.reloadButton.addTarget(self, action: "reload:", forControlEvents: .TouchUpInside)
self.view.addSubview(self.webLinkToolbar)
//add constraints for toolbar
var constraints = [NSLayoutConstraint]()
constraints.appendContentsOf(NSLayoutConstraint.constraintsWithVisualFormat("H:|[webLinkToolbar]|", options: [], metrics: nil, views: ["webLinkToolbar": self.webLinkToolbar]))
constraints.appendContentsOf(NSLayoutConstraint.constraintsWithVisualFormat("V:[webLinkToolbar(44)]|", options: [], metrics: nil, views: ["webLinkToolbar": self.webLinkToolbar]))
NSLayoutConstraint.activateConstraints(constraints)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Web ViewのInsetを変更
if self.webLinkToolbar.hidden {
let inset = UIEdgeInsetsZero
self.webView.scrollView.contentInset = inset
self.webView.scrollView.scrollIndicatorInsets = inset
}
else {
let inset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: self.webLinkToolbar.frame.height, right: 0.0)
self.webView.scrollView.contentInset = inset
self.webView.scrollView.scrollIndicatorInsets = inset
}
// タイトルを設定
self.navigationItem.title = webLink.title()
// ロード
if #available(iOS 9.0, *) {
switch webLink {
case .License, .LocalAgreement:
let url = NSURL.init(fileURLWithPath: webLink.urlString())
webView.loadFileURL(url, allowingReadAccessToURL: url)
return
default:
break;
}
}
webView.loadRequest(webLink.request())
}
// MARK: KVO
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &kvoContext {
if keyPath == "loading" {
self.webLinkToolbar.goBackButton.enabled = self.webView.canGoBack
self.webLinkToolbar.goForwardButton.enabled = self.webView.canGoForward
}
}
else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
// MARK: Action for Web Link Toolbar
func goBack(sender: UIButton) {
// back
self.webView.goBack()
}
func goForward(sender: UIButton) {
// forward
self.webView.goForward()
}
func reload(sender: UIButton) {
if let currentURL = self.webView.URL {
let request = NSURLRequest(URL: currentURL)
self.webView.loadRequest(request)
}
}
/*
deinit must remove observer
*/
deinit {
self.webView.removeObserver(self, forKeyPath: "loading", context: &kvoContext)
}