亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS9中的WebKit 與 Safari帶來的驚喜

2020-07-26 03:32:08
字體:
來源:轉載
供稿:網友

每個用過 UIWebView 的iOS開發者對其諸多的限制和有限的功能也深有感觸。悻然,自iOS8推出 WebKit 框架后將改變這一窘境。在本文我將會深入WebKit來體驗一下它給我們帶來的好處,同時也看看在iOS9中新加入的 SFSafariViewController 有些什么新的驚喜。

通用的瀏覽行為

所謂的通用瀏覽行為主要可以歸納為以下的幾種:

網頁載入進度
前進
后退
刷新

如果每個用到 WebView 的 app都要做一個專用的Controller也挺麻煩的,我以前就直接采用其它第三方寫好的包來完成。

但現在,如果用 WKWebView 將變得很方便,以代碼說話吧:

class ViewController: UIViewController {  var webView: WKWebView!  @IBOutlet weak var progressView: UIProgressView!      required init(coder aDecoder: NSCoder) {    super.init(coder: aDecoder)!        // 實例化 WKWebView    self.webView = WKWebView(frame: CGRectZero)  }     override func viewDidLoad() {    super.viewDidLoad()    // 編程式加入 WKWebView    view.addSubview(webView)    view.insertSubview(webView, aboveSubview: progressView)    webView.translatesAutoresizingMaskIntoConstraints = false        let widthConstraint = NSLayoutConstraint(item:webView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1 , constant: 0)    view.addConstraint(widthConstraint)        let heightConstraint = NSLayoutConstraint(item:webView,attribute: .Height, relatedBy: .Equal,toItem: view, attribute: .Height, multiplier:1, constant: -46)        view.addConstraint(heightConstraint)        // 檢測webView對象屬性的變化    webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)    webView.addObserver(self, forKeyPath: "title", options: .New, context: nil)        //加載網頁    let request = NSURLRequest(URL: NSURL(string: "http://ray.dotnetage.com")!)        webView.loadRequest(request)      }  override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {      if (keyPath == "loading") {      // 檢測按鈕的可用性      forwardButton.enabled = webView.canGoBack      backButton.enabled = webView.canGoBack      stopButton.image = webView.loading ? UIImage(name: "Cross") : UIImage(named: "Syncing")    } else if keyPath == "title" {      title = webView.title    } else if keyPath == "estimatedProgress" {      progressView.hidden = webView.estimatedProgress == 1      progressView.setProgress(Float(webView.estimatedProgress), animated: true)    }  }}

這些代碼我覺得沒什么好說的,除了WKWebView不能通過 IB 來可視化構建外,以上的代碼最多是將 Autolayout 部分的代碼優化一下就是了。寫一寫,做個 Example 就懂了。

與 Javascript 通信

通過WebKit就不需要通過 javascript 橋的方式來與DOM通信了。其實這也不是什么新技術,早再 windows98 在VB或者在Delphi中也可以通過COM接口用完全相類似的手法與DOM通信了。

廢話不多說,講講 WebKit 的基本原理吧。以下是 WebKit Host 的Web進程 與 App 主進程的通信關系示意圖:

這里包含兩個過程

執行 javascript 腳本

我們可以將 javascript 腳本包含于 App 的 Bundle 內,作為應用程序資源。在運行期將其通過 WebKit 注入至目標網頁內執行。

首先我們要準備一個目標網頁,這里就以我自己的博客來做一個示例(http://ray.dotnetage.com)。在 App 中用WebKit打開是這樣的

現在,我就將我博客上首頁的大標題的文字改掉,具體的代碼很簡單:

$(".page-header h1").text("iOS注入測試");

然后,在 iOS項目內增加一個叫 inject.js 的腳本文件,將上述代碼復制其內。

在 App 內包含的 javascript 腳本最好先在瀏覽器的控制臺內執行一次,以確保腳本自身是可以被正確執行的。如果腳本中含有潛在錯誤,在App內是無法檢測得到的。

然后,在控制器的構造函數內創建一個 WKWebViewConfiguration 實例,并作為參數傳入 WKWebView的構造函數,具體代碼如下:

// ViewController.swiftimport WebKitclass ViewController : UIViewController {	var webView: WKWebView!	required init(coder aDecoder: NSCoder) {	  super.init(coder: aDecoder)!	  	  let configuation = WKWebViewConfiguration()	  configuation.userContentController.addUserScript(getUserScript("inject"))	  	  self.webView = WKWebView(frame: CGRectZero,configuration: configuation)	}  // 從資源中讀取 javascript 腳本  func getUserScript(fromName: String)-> WKUserScript {    let filePath = NSBundle.mainBundle().pathForResource(fromName, ofType: "js")    let js = try! String(contentsOfFile: filePath!, encoding: NSUTF8StringEncoding)    return WKUserScript(source: js, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)  }	...}

此代碼段中需要注意的另一點是在自定義方法 getUserScript() 所返回的 WKUserScript 對象。我們可以通過 injectionTime 決定將腳本注入至HTML的開始部分還是在文檔的尾部。

再次執行代碼,效果如下:

也就是說我們可以在 app 內通過 WebKit 注入javascript后就可以任意地操控頁面內的所有 DOM 對象!

javascript 的回調

除了從 app 一端將代碼注入到瀏覽器,執行一個動作。某些情況下我們還需要從網頁上做某一些處理后,例如將網頁內的某些元素讀出并轉為一個 json 對象集合,回傳給 App 處理。又或者我們的 app 在加載一個網頁之后想一次性地讀出頁面內的所有圖像,當用戶點擊這些圖像的時候我們用 app 的本地方式來全屏預覽,諸如此類。在這些語境下:

我們都得從網頁內返回對象

也就是說,在網頁的進程內要向 app 進程通信,那么我們就需要在腳本中使用:

webkit.messageHandlers.{MessageName}.postMessage([params]);

這個方法在標準的HTML5瀏覽器是不能直接執行的,例如 Chrome和 Safair。只有通過 WebKit Host 的頁面才會出現這個 webkit 對象。 這并不難理解,只是 WebKit 在加載頁面后向 windows 注入了 webkit 這個實例,使得 javascript 可以通過它來向 app 發送信息。

如果我們要向 app 發送一個信息,例如:在頁面上的一個按鈕被點擊后,執行 app 內打開相冊的代碼,那么就得先在 javascript 上寫好這樣的代碼:

$("#mybutton").click(function(){  webkit.messageHandlers.openPhotoLibrary.postMessage();});

請留意 openPhotoLibrary 這個對象在Swift是沒有,當這個方法被回傳到 Swift 的時候這只是一個消息的名字,而在Swift中要接收這種來至于瀏覽器發送的信息我們的控制器就需要實現 WKScriptMessageHandler 這個接口,它只有一個方法,我們多花些篇幅直接將這個接口的代碼打開:

/*! A class conforming to the WKScriptMessageHandler protocol provides a method for receiving messages from JavaScript running in a webpage. */public protocol WKScriptMessageHandler : NSObjectProtocol {    /*! @abstract Invoked when a script message is received from a webpage.   @param userContentController The user content controller invoking the   delegate method.   @param message The script message received.   */  @available(iOS 8.0, *)  public func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)}

那么,我們就直接實現這個接口:

class ViewController: UIViewController, WKScriptMessageHandler {  required init(coder aDecoder: NSCoder) {    // ... 之前的代碼同上    configuation.userContentController.addScriptMessageHandler(self, name: "openPhotoLibrary")    self.webView = WKWebView(frame: CGRectZero,configuration: configuation)  }  ...  func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {    if message.name == "openPhotoLibrary" {      // 這里就可以加入打開相冊的代碼了    }  }}

從代碼就可以看出原理的一二:

在構造 WKWebView 之前要用 addScriptMessageHandler 方法向配置對象注冊一個消息名,這里的例程是 "openPhotoLibrary"。
實現 WKScriptMessageHandler 接口,從 userContentController() 方法的 message.name 參數中判斷消息的源頭,執行對應的代碼。

另外,如果我們需要從javascript腳本中向 app 傳入對象,可以直接在 postMessage() 方法內將對象作為參數輸入,但通常這個參數的類型應該是一個數組或者是普通的JSON對象,這樣在 app 才能用字典對象將其從新讀出。

例如,我從當前網頁中將所有的菜單的地址和名稱讀出,并生成了一個 menus 的 javascript 數組對象:

var menus = $(".navbar a").map(function(n,i){  return {    title: $(n).text,    link: $(n).attr("href")  };});webkit.messageHandlers.didFetchMenus.postMessage(menus);

這里就略過接口實現,直接看 userContentController 方法實現:

var menus: [Menus]?func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {  if message.name == "didFetchMenus" {    if let resultArray = message.body as? [Dictionary<String,String>] {      menus = resultArray.map{ Menu(dict: $0) }      // 這里就取出并將JSON轉換為 Swift 的Menu對象了      print(menus)    }  }}

iOS9 中的 Safair 瀏覽器

在 iOS9 中加入了 SafariServices 這個新的模塊,其作用就是提供了一個全功能的內嵌式 Safair,通過
SFSafariViewController 就能像普通的 控制器那樣使用。

以下是一個簡單的例子

import UIKitimport SafariServicesclass ViewController: UIViewController {  @IBAction func openBrowser(sender: AnyObject) {    let safari = SFSafariViewController(URL:NSURL(string:"http://www.apple.com")!)          self.showViewController(safari, sender: self)  }}

SFSafariViewController 和 WebKit 的最大區別是 SFSafariViewController 沒有什么可控制方法,只是一個可以完全嵌入到 app 中的一個控制器,避免了像以前那樣如果打開一個外部鏈接要跳出當前的app,而且 SFSafariViewController 嵌入的 Safari 和 Safari 內的所有功能是一樣的,同樣支持 3D Touch 和切頁的等特色功能。且當我們的 app 采用外部網絡帳號進行集成登錄時,Safari 能更直接獲取到當前 app的應用上下文,而無須再跳出重新在外部登入后再返回至App。這無疑是大大地增強了 app 在與 Safari 集成的時的使用體驗。

在 Apple 的開發者網站上對 WebKit 與 SafariServices 的選擇上給出了這樣的意見:

如果需要與網頁交互則選擇 WebKit
如果需要與Safari具有同樣的使用體驗且不需要與網頁交互推薦使用 SafariServices

這確實是一項很不錯的更新。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日韩ab片| 国产成人精品电影久久久| 超碰日本道色综合久久综合| 亚洲国产精品成人va在线观看| 亚洲美女福利视频网站| 最新国产精品拍自在线播放| 亚洲国产福利在线| 亚洲精品中文字幕av| 黑人精品xxx一区一二区| 51精品国产黑色丝袜高跟鞋| 国产亚洲精品一区二区| 亚洲欧美日韩区| 欧美一区三区三区高中清蜜桃| 欧美日本国产在线| 55夜色66夜色国产精品视频| 91在线视频成人| 国产精品高清在线观看| 啪一啪鲁一鲁2019在线视频| 亚洲欧洲一区二区三区在线观看| 成人午夜黄色影院| 亚洲精品xxxx| 中文字幕日韩视频| 久久久久久国产精品三级玉女聊斋| 国产精品天天狠天天看| 日韩精品在线视频观看| 欧美与黑人午夜性猛交久久久| 国内精品400部情侣激情| 亚洲精品成人免费| 国产精品美女免费视频| 久久久久久久国产精品| 欧美性生交大片免网| 亚洲影院污污.| 久久久久久com| 日韩在线视频播放| 综合久久五月天| 国产主播欧美精品| 大伊人狠狠躁夜夜躁av一区| 97超级碰碰人国产在线观看| 狠狠躁18三区二区一区| 欧美成人精品在线| 久久精品国产久精国产一老狼| 日韩av免费一区| 精品久久久久久久久久久久| 国内揄拍国内精品| 国产在线精品播放| 一级做a爰片久久毛片美女图片| 俺去亚洲欧洲欧美日韩| 国产精品偷伦免费视频观看的| 国产精品一区二区3区| 国产婷婷成人久久av免费高清| 亚洲а∨天堂久久精品喷水| 亚洲精品久久久久久下一站| 日韩在线观看成人| 亚洲桃花岛网站| 欧美精品videosex极品1| 成人在线播放av| 中文字幕久精品免费视频| 亚洲国产中文字幕在线观看| 日韩成人av一区| 91久久国产婷婷一区二区| 亚洲人成啪啪网站| 日韩中文字幕视频| 国产一区二区视频在线观看| 亚洲视频在线免费看| 国产精品www色诱视频| 亚洲精品乱码久久久久久金桔影视| 亚洲国产天堂久久综合| 亚洲高清福利视频| 国产精品日韩专区| 欧美激情一区二区三级高清视频| 136fldh精品导航福利| 日韩亚洲精品视频| 亚洲成av人乱码色午夜| 精品久久久久久中文字幕大豆网| 亚洲成色777777在线观看影院| 亚洲另类图片色| 97视频国产在线| 亚洲人成电影网站色…| 国产精品高潮呻吟久久av无限| 精品国产一区二区三区久久久狼| 欧美一级高清免费播放| 亚洲第一福利网| 欧美精品久久久久a| 午夜精品久久久久久久白皮肤| 国产视频精品久久久| 色综合91久久精品中文字幕| 中文字幕日韩有码| 日产精品久久久一区二区福利| 综合国产在线观看| 欧美视频专区一二在线观看| 欧美一级视频在线观看| 在线播放国产精品| 日本亚洲欧洲色| 国产欧美最新羞羞视频在线观看| 欧美精品一区在线播放| 亚洲天堂精品在线| 91精品在线观看视频| 日本欧美中文字幕| 欧美激情视频在线免费观看 欧美视频免费一| 一本一本久久a久久精品牛牛影视| 亚洲精品久久久久中文字幕二区| 亚洲成人999| 亚洲欧美制服丝袜| 91精品国产免费久久久久久| 日韩在线观看网址| 中日韩美女免费视频网站在线观看| 色爱av美腿丝袜综合粉嫩av| 欧洲成人免费视频| 国产一区二区三区视频在线观看| 国产不卡在线观看| 欧美男插女视频| 日韩在线观看精品| 国产亚洲精品久久久久动| 国产精品一香蕉国产线看观看| 欧美精品日韩三级| 欧美亚洲视频在线看网址| 中文字幕日韩视频| 国产视频精品久久久| 亚洲激情在线观看视频免费| 7m第一福利500精品视频| 亚洲综合小说区| 欧美影院成年免费版| 91精品国产色综合久久不卡98口| 国产精品直播网红| 日韩高清人体午夜| 欧美成人免费在线观看| 日韩中文有码在线视频| 这里只有精品丝袜| 成人欧美一区二区三区在线湿哒哒| 欧美老女人性视频| 日韩精品在线免费观看视频| 一区二区三区 在线观看视| 国产日韩精品在线| 久久视频免费观看| 欧美孕妇与黑人孕交| 国产精品九九久久久久久久| 亚洲国产一区二区三区在线观看| 色噜噜亚洲精品中文字幕| 欧美日韩国产第一页| 日韩av网站大全| 亚洲影院在线看| 久久精品国产69国产精品亚洲| 国模gogo一区二区大胆私拍| 亚洲精品久久久久久久久久久| 免费91麻豆精品国产自产在线观看| 亚洲一区二区三区久久| 欧美日韩国产成人在线观看| 久久精品国产视频| 伊人av综合网| 久久久亚洲国产天美传媒修理工| 国产精品欧美激情在线播放| 欧美亚洲国产日韩2020| 狠狠操狠狠色综合网| 日韩在线免费视频观看| 欧美视频在线观看免费网址| 亚洲女人被黑人巨大进入| 国产亚洲视频在线观看| 日韩黄在线观看| 国产一区二区精品丝袜| 性欧美在线看片a免费观看| 欧美视频免费在线| 韩剧1988在线观看免费完整版| 日韩美女在线观看一区| 国产亚洲人成网站在线观看|