从 UIWebView 到 WKWebView

栏目: IT技术 · 发布时间: 4年前

内容简介:由于众所周知的原因,UIWebView 要离开历史的舞台了。于是在最新的版本中更改 UIWebView 为 WKWebView, 现在上线了,简单做一个记录。接入 WKWebView 很简单,但是会出现以下问题UIWebView 的 UserAgent 可以统一设置,但是 WKWebView 不行。WKWebView 提供了单独的属性 customUserAgent 用来设置 UserAgent。

前因

由于众所周知的原因,UIWebView 要离开历史的舞台了。于是在最新的版本中更改 UIWebView 为 WKWebView, 现在上线了,简单做一个记录。

问题

接入 WKWebView 很简单,但是会出现以下问题

1. UserAgent 问题

UIWebView 的 UserAgent 可以统一设置,但是 WKWebView 不行。WKWebView 提供了单独的属性 customUserAgent 用来设置 UserAgent。

首先在 didFinishLaunchingWithOptions 方法中获取 UA

注意:WKWebView 的 evaluateJavaScript 是异步回调,所以需要把 webView 声明为属性

func setWebviewUserAgent() {
      webView?.evaluateJavaScript("navigator.userAgent", completionHandler: { (object, error) -> 			Void in
          if let defaultUserAgent = object as? String {
              // 系统 UA + 自定义 UA
              let newUserAgent = defaultUserAgent + UserAgent.userAgentString
              UserDefaults.standard.set(newUserAgent, forKey: "UserAgent")
              UserDefaults.standard.synchronize()
          }
      })
}

然后再设置给 WKWebView

if let ua = UserDefaults.standard.string(forKey: "UserAgent") {
	webView.customUserAgent = ua
}

2.Cookie 问题

App 一直是自动管理 cookie 的(用户登录成功后,cookie 会自动保存到 HTTPCookieStorage 中),UIWebView 也会自动从 HTTPCookieStorage 中读取 cookie,但是 WKWebView 不会,这就需要我们手动管理 cookie 了。

创建 CookieManager 单例类

// 以下为伪代码
class CookieManager {
    
    static let shared = CookieManager()
    fileprivate init(){}
    fileprivate let CookieStoreKey = "com.xxx.app.Cookie"
    
    var cookieString: String? {
        return UserDefaults.standard.string(forKey: CookieStoreKey)
    }
    
    fileprivate func setNewCookie() {
        if let cookies = HTTPCookieStorage.shared.cookies {
            var cookieString = ""
            for cookie in cookies {
                cookieString += "\(cookie.name)=\(cookie.value);"
            }
            UserDefaults.standard.set(cookieString, forKey: CookieStoreKey)
            UserDefaults.standard.synchronize()
        }
    }
    
    fileprivate func deleteCookie() {
        UserDefaults.standard.set(nil, forKey: CookieStoreKey)
        UserDefaults.standard.synchronize()
        
        let storage = HTTPCookieStorage.shared
        if let cookies = storage.cookies {
            for cookie in cookies {
                storage.deleteCookie(cookie)
            }
        }
    }
    
	//..
    
    // 用户升级新版本后 注册cookie
    func registerCookies() {
        if let _ = CurrentUser {
            if cookieString == nil {
                setNewCookie()
            }
        } else {
            deleteCookie()
        }
    }
    
    // 登录/退出登录 更新cookie
    func updateCookie() {
        if let _ = CurrentUser {
            setNewCookie()
            // 更新 webview cookiew
            NotificationCenter.default.post(name: .updateWebviewCookieNotification, object: nil)
        } else {
            deleteCookie()
        }
    }
    
}

然后设置给 WKWebView

fileprivate func setupWebview() {
    let wkUController = WKUserContentController()
    setCookieByJS(wkUController)

    let config = WKWebViewConfiguration()
    config.userContentController = wkUController

    webView = WKWebView(frame: CGRect(x: 0, y: 0, width: ScreenWidth, height: ScreenHeight), configuration: config)
    // ...
    view.addSubview(webView)
  
  	NotificationCenter.default.addObserver(self, selector: #selector(self.updateCookieAction), name: .updateWebviewCookieNotification, object: nil)
}

fileprivate func loadRequest() {
    guard let url = url else { return }
    let request = NSMutableURLRequest(url: url)
    self.request = request

    setCookie(request)
    webView.load(request as URLRequest)
}

@objc func updateCookieAction() {
    let wkUController = webView.configuration.userContentController
    setCookieByJS(wkUController)

    if let request = request {
         setCookie(request)
    }
}

func setCookie(_ request: NSMutableURLRequest) {
    request.addValue("xxx", forHTTPHeaderField: "Cookie")
}
    
func setCookieByJS(_ userController: WKUserContentController) {
    let cookieScript = WKUserScript(source: "xxx", injectionTime: .atDocumentStart, forMainFrameOnly: false)
    userController.addUserScript(cookieScript)
}

3.WKWebView 白屏问题

当 WKWebView 内存占用过大的时候,会发生白屏现象,需要重新加载页面

func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
	webView.reload()
}

4.JS 的 alert()、confirm()、prompt()方法无法正常执行 问题

由于安全机制的问题,WKWebView 默认对 JavaScript 下 alert 类的方法(包括alert()、confirm()、prompt())做了拦截,如果要想正常使用,需要实现 WKWebView 的三个代理方法

func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
    let action = UIAlertAction(title: "确认", style: .default) { (action) in
        completionHandler()
    }
    UIViewController.topMost?.showAlert("提示", message: message, alertActions: [action])
}
    
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
    let action = UIAlertAction(title: "确认", style: .default) { (action) in
        completionHandler(true)
    }
    let action2 = UIAlertAction(title: "取消", style: .cancel) { (action) in
        completionHandler(false)
    }
    UIViewController.topMost?.showAlert("提示", message: message, alertActions: [action, action2])
}
    
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
    let alertController = UIAlertController(title: prompt, message: "", preferredStyle: UIAlertController.Style.alert)
    alertController.addTextField { (textField) in
        textField.text = defaultText
    }
    let action = UIAlertAction(title: "完成", style: .default) { (action) in
        completionHandler(alertController.textFields?.first?.text)
    }
    alertController.addAction(action)
    UIViewController.topMost?.present(alertController, animated: true, completion: nil)
}

5.点击H5页面文本输入框页面放大 问题

当文本输入框太小时(好像是低于17px)会出现这类问题,页面太多,前端不太方便处理时,可以在App中这样处理

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    webView.scrollView.setZoomScale(0, animated: false)
}

func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
    webView.scrollView.setZoomScale(0, animated: false)
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

爆品手记

爆品手记

金错刀 / 中国友谊出版公司 / 2016-9-20 / 39.80

互联网时代,一切都被颠覆。 B2B、B2C、O2O等商业模式的建立,对传统企业构成了巨大冲击。人们的生意往来逐渐从线下转移到了线上,传统的定位理论逐渐失效,依靠爆品引爆市场才是王道;传统企业经营多年的渠道营销模式正遭遇前所未有的阻力,网上商城正成为众多商家角逐血拼的主要战场。 在互联网的黑暗森林里,一切传统的商业模式统统失效,一场依靠爆品点燃市场、引爆市场、占据市场的营销革命正悄然兴起......一起来看看 《爆品手记》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具