1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/huqs-WKWebViewDemo

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
README.md 19 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 29.11.2024 10:04 527aa7b

Введение

Всегда слышал, что WKWebView гораздо мощнее UIWebView, но никогда не использовал его. Сегодня потратил некоторое время на изучение примера и разобрался в использовании его API. Чтобы в будущем было меньше проблем, а также для облегчения изучения, я написал эту статью, чтобы записать, как использовать и на что следует обратить внимание.

Важное замечание:

В процессе обучения я действительно пришёл к такому выводу, что WKWebView действительно намного мощнее, чем UIWebView. Его возможности взаимодействия с JS явно улучшены, а скорость загрузки повысилась.

Новые функции WKWebView:

  • Значительное улучшение производительности, стабильности и функциональности.
  • Разрешает загрузку и использование библиотеки Nitro JavaScript (ограничено в UIWebView).
  • Поддерживает больше функций HTML5.
  • Высокая частота обновления прокрутки до 60 кадров в секунду и встроенные жесты.
  • Аппаратное ускорение GPU.
  • KVO.
  • Реконструкция UIWebView в 14 классов и 3 протокола, см. официальную документацию.

Подготовка

Прежде всего, мы импортируем модуль в месте использования:

import Webkit

Перед изучением рекомендуется сначала прочитать соответствующие API в WKWebView, чтобы получить общее представление. Это поможет быстрее усвоить материал.

У него есть три метода инициализации:

public init()
public init(frame: CGRect)
public init(frame: CGRect, configuration: WKWebViewConfiguration)
Способ загрузки HTML в основном такой же, как и в UIWebView. Среди них метод loadFileURL обычно используется для загрузки веб-страниц или JS с сервера, а метод loadHTMLString обычно используется для загрузки локального HTML или JS:

public func loadRequest(request: NSURLRequest) -> WKNavigation?

// Поддержка только с iOS 9.0
@available(iOS 9.0, *)
public func loadFileURL(URL: NSURL, allowingReadAccessToURL readAccessURL: NSURL) -> WKNavigation?

// Обычно используется для загрузки локальных HTML или JS
public func loadHTMLString(string: String, baseURL: NSURL?) -> WKNavigation?

// Поддержка только с iOS 9.0
@available(iOS 9.0, *)
public func loadData(data: NSData, MIMEType: String, characterEncodingName: String, baseURL: NSURL) -> WKNavigation

Три основных делегата для взаимодействия:

WKNavigationDelegate, связанный с навигацией и загрузкой страниц.
WKUIDelegate, связанный с отображением пользовательского интерфейса при взаимодействии с JS, например, alert, confirm и prompt.
WKScriptMessageHandler, связанный с взаимодействием с JS, обычно это имя, внедряемое на стороне iOS, и сообщение, отправляемое с помощью window.webkit.messageHandlers.{NAME}.postMessage() на сторону JS.

Создание WKWebView

Сначала мы реализуем протоколы в ViewController:

class ViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate
Мы можем сначала создать конфигурацию WKWebView WKWebViewConfiguration, которая может устанавливать предпочтения, настройки взаимодействия с веб-страницей, объекты внедрения и настройки JS и т. д.:

// Создаём конфигурацию webview
let configuretion = WKWebViewConfiguration()

// Предпочтения webview
configuretion.preferences = WKPreferences()
configuretion.preferences.minimumFontSize = 10
configuretion.preferences.javaScriptEnabled = true
// По умолчанию нельзя автоматически открывать окна через JS, необходимо взаимодействие с пользователем
configuretion.preferences.javaScriptCanOpenWindowsAutomatically = false

// Настройка взаимодействия с содержимым веб-страницы через JS
configuretion.userContentController = WKUserContentController()

// Добавляем JS в HTML, так что можно напрямую вызывать добавленный метод JS в JS
let script = WKUserScript(source: "function showAlert() { alert('Метод JS, внедрённый Swift во время загрузки webview'); }",
  injectionTime: .AtDocumentStart,// Добавить JS при загрузке
  forMainFrameOnly: true) // Добавить только в mainFrame
configuretion.userContentController.addUserScript(script)

// Добавляем имя, которое можно использовать для отправки сообщений в JS через это имя:
// window.webkit.messageHandlers.AppModel.postMessage({body: 'xxx'})
configuretion.userContentController.addScriptMessageHandler(self, name: "AppModel")
Создаём объект и реализуем прокси:

self.webView = WKWebView(frame: self.view.bounds, configuration: configuretion)

self.webView.navigationDelegate = self
self.webView.UIDelegate = self
Загружаем нашу локальную HTML-страницу:

let url = NSBundle.mainBundle().URLForResource("test", withExtension: "html")
self.webView.loadRequest(NSURLRequest(URL: url!))
self.view.addSubview(self.webView);
Затем добавляем кнопки «Назад» и «Вперёд» и добавляем элемент управления прогрессом загрузки на Webview:

self.progressView = UIProgressView(progressViewStyle: .Default)
self.progressView.frame.size.width = self.view.frame.size.width
self.progressView.backgroundColor = UIColor.redColor()
self.view.addSubview(self.progressView)

self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Предыдущая страница", style: .Done, target: self, action: "previousPage")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Следующая страница", style: .Done, target: self, action: "nextPage")

Навигация вперёд и назад

Обработка событий навигации вперёд и назад очень проста, но важно убедиться, что можно вернуться назад или перейти вперёд перед вызовом:

func previousPage() {
    if self.webView.canGoBack {
      self.webView.goBack()
    }
}

func nextPage() {
    if self.webView.canGoForward {
      self.webView.goForward()
    }
}
Конечно, помимо этих методов, есть ещё перезагрузка и другие.

KVO WKWebView

Для WKWebView есть три свойства, которые поддерживают KVO, поэтому мы можем отслеживать изменения их значений: загрузка, заголовок и предполагаемый прогресс, что соответственно означает: находится ли он в процессе загрузки, заголовок страницы и прогресс загрузки страницы (значение от 0.0 до 1.0).

// Отслеживаем свойства, поддерживающие KVO
self.webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)
self.webView.addObserver(self, forKeyPath: "title", options: .New, контекст: ноль)
self.webView.addObserver(self, forKeyPath: «estimatedProgress», options: Вот перевод текста на русский язык:

**Новый, context: nil)**
Затем можно переписать метод прослушивания для обработки. Здесь просто берётся заголовок страницы, обновляется индикатор выполнения, и при завершении загрузки вручную вызывается выполнение метода JS:

// MARK: - KVO
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
  if keyPath == "loading" {
    print("loading")
  } else if keyPath == "title" {
    self.title = self.webView.title
  } else if keyPath == "estimatedProgress" {
    print(webView.estimatedProgress)
    self.progressView.setProgress(Float(webView.estimatedProgress), animated: true)
  }

  // Загрузка завершена, теперь мы можем делать свои дела
  if !webView.loading {
    // Ручной вызов кода JS
    let js = "callJsAlert()";
    self.webView.evaluateJavaScript(js) { (_, _) -> Void in
      print("вызов JS alert")
    }

    UIView.animateWithDuration(0.55, animations: { () -> Void in
      self.progressView.alpha = 0.0;
    })
}
WKUIDelegate

Рассмотрим несколько методов делегата WKUIDelegate. Хотя они не обязательны для реализации, если в нашей странице есть вызовы методов alert, confirm или prompt, мы должны реализовать следующие методы и вызывать их вместо собственных всплывающих окон, так как после использования WKWebView вызовы alert, confirm и prompt в HTML больше не будут отображать всплывающие окна, а будут преобразованы в обратные вызовы нативных методов iOS:

// MARK: - WKUIDelegate
// Этот метод будет вызван, когда в HTML будет вызван метод JS alert().
// Обратите внимание, что после использования `WKWebView` вызовы alert() в JS больше не будут отображаться во всплывающем окне в HTML. Поэтому нам нужно вручную вызвать нативное всплывающее окно здесь.
func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) {
  let alert = UIAlertController(title: "Tip", message: message, preferredStyle: .Alert)
  alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in
    // Мы должны вызвать обратно JS
    completionHandler()
  }))

  self.presentViewController(alert, animated: true, completion: nil)
}

func webView(webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (Bool) -> Void) {
  let alert = UIAlertController(title: "Tip", message: message, preferredStyle: .Alert)
  alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in
    // После нажатия кнопки «Завершить» можно выполнить соответствующую обработку и затем вызвать JS-сторону
    completionHandler(true)
  }))
  alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (_) -> Void в
    // После нажатия кнопки «Отмена» можно выполнить соответствующую обработку и затем вызвать JS-сторону
    completionHandler(false)
  }))

  self.presentViewController(alert, animated: true, completion: nil)
}

func webView(webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: (String?) -> Void) {
  let alert = UIAlertController(title: prompt, message: defaultText, preferredStyle: .Alert)

  alert.addTextFieldWithConfigurationHandler { (textField: UITextField) -> Void in
    textField.textColor = UIColor.redColor()
  }
  alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (_) -> Void in
     // Обработка перед передачей значения в JS
    completionHandler(alert.textFields![0].text!)
  }))

  self.presentViewController(alert, animated: true, completion: nil)
}

func webViewDidClose(webView: WKWebView) {
  print(__FUNCTION__)
}
WKScriptMessageHandler

Далее рассмотрим WKScriptMessageHandler. Это имя инъекции JS, которое позволяет отправлять сообщения на нативную сторону через window.webkit.messageHandlers.{InjectedName}.postMessage(). Нам нужно следовать этому протоколу и реализовать его методы, чтобы получать сообщения и выполнять соответствующие действия. У этого протокола есть только один метод:

// MARK: - WKScriptMessageHandler
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
  print(message.body)
  // Если мы внедрили много имён в начале, то нам нужно различать их для обработки
  if message.name == "AppModel" {
    print("имя сообщения — AppModel")
  }}
Этот метод является хорошим API. Мы внедряем имя в JS, и оно автоматически преобразуется в объект JS, после чего мы можем отправлять сообщения на нативный конец.

WKNavigationDelegate

Ещё один важный делегат  WKNavigationDelegate. У него есть множество методов, которые позволяют контролировать навигацию по страницам.

Порядок вызова следующий:
1. Этот метод используется для определения, разрешена ли навигация. Safari  единственный браузер, который разрешает междоменные переходы, поэтому нам необходимо отдельно обрабатывать междоменные ссылки.

// Определяет действие навигации, обычно используется для обработки междоменных ссылок. WebKit проверяет безопасность и ограничивает междоменную навигацию, поэтому мы должны отдельно обрабатывать ссылки, которые не могут переходить по доменам. Однако Safari разрешает междоменную навигацию, и нам не нужно это обрабатывать.
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
    print(__FUNCTION__) Данный текст написан на языке Swift.

**Если navigationAction.navigationType == .LinkActivated и hostname не содержит строку «baidu.com», то:**
* выполняется ручное перенаправление с помощью метода openURL;
* навигация запрещается с помощью decisionHandler(.Cancel).

**В противном случае:**
* альфа-значение progressView устанавливается равным 1.0;
* вызывается decisionHandler(.Allow).

Этот метод является прокси-методом, который вызывается при начале загрузки страницы и аналогичен методу didStartLoad в UIWebView.

Следующий метод определяет, разрешать ли навигацию. Если навигация запрещена, переход по ссылке не произойдёт.

Ещё один метод вызывается, когда контент начинает поступать для основного фрейма. Это исходный комментарий к API. То есть этот метод будет вызван, когда содержимое страницы достигнет основного фрейма. Если необходимо внедрить какой-либо JavaScript в основной фрейм, это можно сделать здесь.

Также в тексте описывается метод, который вызывается после завершения загрузки страницы.

Если загрузка завершается неудачно, вызывается следующий метод.

Существуют и другие методы, которые обычно не нужны. Однако если необходимо обрабатывать перенаправления, можно реализовать следующий прокси-метод для получения перенаправлений.

Для обработки запросов, требующих авторизации или сертификатов, необходимо обработать следующий метод для предоставления соответствующей обработки авторизации и т. д.

Когда загрузка страницы прекращается, можно обработать следующий метод. Если обработка не требуется, его можно не реализовывать.

Далее представлен код на Swift, который вызывает функцию JavaScript:

```swift
self.webView.evaluateJavaScript("redHeader('来自Swift的调用')") { (sender:AnyObject?, error:NSError?) -> Void in
print("sender:\(sender)   error:\(error)")
}

В конце представлен HTML-код.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/huqs-WKWebViewDemo.git
git@api.gitlife.ru:oschina-mirror/huqs-WKWebViewDemo.git
oschina-mirror
huqs-WKWebViewDemo
huqs-WKWebViewDemo
huqs