// WKWebViewController.swift
// ProductionReport
import UIKit
import WebKit
class WKWebViewController: UIViewController {
// MARK: - lazy
fileprivate lazy var webView: WKWebView = { [unowned self] in
// 創(chuàng)建webveiew
// 創(chuàng)建一個(gè)webiview的配置項(xiàng)
let configuretion = WKWebViewConfiguration()
// Webview的偏好設(shè)置
configuretion.preferences = WKPreferences()
configuretion.preferences.minimumFontSize = 10
configuretion.preferences.javaScriptEnabled = true
// 默認(rèn)是不能通過(guò)JS自動(dòng)打開窗口的徽级,必須通過(guò)用戶交互才能打開
configuretion.preferences.javaScriptCanOpenWindowsAutomatically = false
// 通過(guò)js與webview內(nèi)容交互配置
configuretion.userContentController = WKUserContentController()
// 添加一個(gè)JS到HTML中程腹,這樣就可以直接在JS中調(diào)用我們添加的JS方法
let js = "function showAlert() { alert('在載入webview時(shí)通過(guò)Swift注入的JS方法'); }"
let script = WKUserScript(source: js, injectionTime: .atDocumentStart,// 在載入時(shí)就添加JS
forMainFrameOnly: true) // 只添加到mainFrame中
configuretion.userContentController.addUserScript(script)
// 添加一個(gè)名稱跺涤,就可以在JS通過(guò)這個(gè)名稱發(fā)送消息:
// window.webkit.messageHandlers.AppModel.postMessage({body: 'xxx'})
configuretion.userContentController.add(self as WKScriptMessageHandler, name: "MingModel")
let webView = WKWebView(frame: self.view.bounds, configuration: configuretion)
webView.navigationDelegate = self
webView.uiDelegate = self
return webView
}()
fileprivate lazy var progressView: UIProgressView = {
let progressView = UIProgressView(progressViewStyle: .bar)
progressView.frame.size.width = self.view.frame.size.width
// 這里可以改進(jìn)度條顏色
progressView.tintColor = UIColor.green
return progressView
}()
// MARK: - 生命周期
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
view.addSubview(webView)
view.insertSubview(progressView, aboveSubview: webView)
}
convenience init(navigationTitle: String, urlStr: String) {
self.init(nibName: nil, bundle: nil)
navigationItem.title = navigationTitle
webView.load(URLRequest(url: URL(string: urlStr)!))
}
convenience init(navigationTitle: String, url: URL) {
self.init(nibName: nil, bundle: nil)
navigationItem.title = navigationTitle
webView.load(URLRequest(url: url))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = UIRectEdge()
webView.addObserver(self, forKeyPath: "loading", options: .new, context: nil)
webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "loading" {
// print("loading")
} else if keyPath == "title" {
title = self.webView.title
} else if keyPath == "estimatedProgress" {
print(webView.estimatedProgress)
progressView.setProgress(Float(webView.estimatedProgress), animated: true)
}
UIView.animate(withDuration: 0.5) {
self.progressView.isHidden = (self.progressView.progress == 1)
}
if webView.isLoading {
// 手動(dòng)調(diào)用JS代碼
let js = "callJsAlert()"
webView.evaluateJavaScript(js, completionHandler: { (any, err) in
debugPrint(any)
})
}
}
// 移除觀察者
deinit {
webView.removeObserver(self, forKeyPath: "loading")
webView.removeObserver(self, forKeyPath: "title")
webView.removeObserver(self, forKeyPath: "estimatedProgress")
}
}
// MARK: - WKScriptMessageHandler
extension WKWebViewController: WKScriptMessageHandler {
// WKScriptMessageHandler:必須實(shí)現(xiàn)的函數(shù)草慧,是APP與js交互,提供從網(wǎng)頁(yè)中收消息的回調(diào)方法
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
print(message.webView)
}
}
// MARK: - WKNavigationDelegate
extension WKWebViewController: WKNavigationDelegate {
// 決定導(dǎo)航的動(dòng)作吸祟,通常用于處理跨域的鏈接能否導(dǎo)航瑟慈。WebKit對(duì)跨域進(jìn)行了安全檢查限制,不允許跨域屋匕,因此我們要對(duì)不能跨域的鏈接
// 單獨(dú)處理葛碧。但是,對(duì)于Safari是允許跨域的过吻,不用這么處理进泼。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
let hostname = (navigationAction.request as NSURLRequest).url?.host?.lowercased()
print(hostname)
print(navigationAction.navigationType)
// 處理跨域問(wèn)題
if navigationAction.navigationType == .linkActivated && hostname!.contains(".baidu.com") {
// 手動(dòng)跳轉(zhuǎn)
UIApplication.shared.openURL(navigationAction.request.url!)
// 不允許導(dǎo)航
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print(#function)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
print(#function)
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print(#function)
completionHandler(.performDefaultHandling, nil)
}
}
// MARK: - WKUIDelegate
extension WKWebViewController: WKUIDelegate {
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> 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.present(alert, animated: true, completion: nil)
}
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
completionHandler(true)
}
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
completionHandler("woqu")
}
func webViewDidClose(_ webView: WKWebView) {
print("close")
}
}
// WebViewController.swift
// ProductionReport
import UIKit
// MARK: - Class: WebViewController
class WebViewController: UIViewController {
// MARK: - 屬性
fileprivate var webView = UIWebView(frame: .zero)
fileprivate var urlStr: String?
fileprivate let loadProgressAnimationView: LoadProgressAnimationView = LoadProgressAnimationView(frame: CGRect(x: 0, y: 0, width: MGScreenW, height: 3))
// MARK: - 生命周期
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
view.addSubview(webView)
webView.addSubview(loadProgressAnimationView)
webView.scalesPageToFit = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init(navigationTitle: String, urlStr: String) {
self.init(nibName: nil, bundle: nil)
navigationItem.title = navigationTitle
webView.loadRequest(URLRequest(url: URL(string: urlStr)!))
self.urlStr = urlStr
}
override func viewDidLoad() {
super.viewDidLoad()
view.frame = MGScreenBounds
automaticallyAdjustsScrollViewInsets = true
view.backgroundColor = UIColor.white
view.backgroundColor = UIColor.colorWithCustom(r: 230, g: 230, b: 230)
buildRightItemBarButton()
webView.frame = CGRect(x: 0, y: navHeight, width: MGScreenW, height: view.mg_height)
webView.backgroundColor = UIColor.colorWithCustom(r: 230, g: 230, b: 230)
webView.delegate = self
webView.dataDetectorTypes = .all // 設(shè)置某些數(shù)據(jù)變?yōu)殒溄有问较怂洌@個(gè)枚舉可以設(shè)置如電話號(hào)缘琅,地址,郵箱等轉(zhuǎn)化為鏈接
webView.mediaPlaybackAllowsAirPlay = true //設(shè)置音頻播放是否支持ari play功能
webView.suppressesIncrementalRendering = true // 設(shè)置是否將數(shù)據(jù)加載如內(nèi)存后渲染界面
webView.keyboardDisplayRequiresUserAction = true // 設(shè)置用戶交互模式
// webView.paginationMode = .topToBottom // 這個(gè)屬性用來(lái)設(shè)置一種模式廓推,當(dāng)網(wǎng)頁(yè)的大小超出view時(shí),將網(wǎng)頁(yè)以翻頁(yè)的效果展示
webView.scrollView.contentInset = UIEdgeInsets(top: -navHeight, left: 0, bottom: navHeight, right: 0)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
// MARK: - 導(dǎo)航欄
private func buildRightItemBarButton() {
let rightButton = UIButton(frame: CGRect(x: 0, y: 0, width: 60, height: 44))
rightButton.setImage(UIImage(named: "v2_refresh"), for: UIControlState.normal)
rightButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -53)
rightButton.addTarget(self, action: #selector(WebViewController.refreshClick), for: UIControlEvents.touchUpInside)
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: rightButton)
}
// MARK: - Action
func refreshClick() {
if urlStr != nil && urlStr!.characters.count > 1 {
webView.loadRequest(URLRequest(url: URL(string: urlStr!)!))
}
}
}
// MARK: - UIWebViewDelegate
extension WebViewController: UIWebViewDelegate {
func webViewDidStartLoad(_ webView: UIWebView) {
loadProgressAnimationView.startLoadProgressAnimation()
}
func webViewDidFinishLoad(_ webView: UIWebView) {
loadProgressAnimationView.endLoadProgressAnimation()
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
showHint(hint: "\(error)")
loadProgressAnimationView.endLoadProgressAnimation()
}
// func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
// let str = request.url?.absoluteString
// if (str?.hasPrefix("tel"))! {
// UIApplication.shared.openURL((URL(string: str!))!)
// }
// return true
// }
}
// MARK: -
// MARK: - Class: LoadProgressAnimationView
class LoadProgressAnimationView: UIView {
var viewWidth: CGFloat = 0
override var frame: CGRect {
willSet {
if frame.size.width == viewWidth {
self.isHidden = true
}
super.frame = frame
}
}
override init(frame: CGRect) {
super.init(frame: frame)
viewWidth = frame.size.width
backgroundColor = UIColor.randomColor()
self.frame.size.width = 0
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - 加載進(jìn)度動(dòng)畫
func startLoadProgressAnimation() {
self.frame.size.width = 0
isHidden = false
UIView.animate(withDuration: 0.8, animations: { () -> Void in
self.frame.size.width = self.viewWidth * 0.70
}) { (finish) -> Void in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.08, execute: {
UIView.animate(withDuration: 0.3, animations: {
self.frame.size.width = self.viewWidth * 0.85
})
})
}
}
func endLoadProgressAnimation() {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.frame.size.width = self.viewWidth*0.99
}) { (finish) -> Void in
self.isHidden = true
}
}
}