Всегда слышал, что WKWebView гораздо мощнее UIWebView, но никогда не использовал его. Сегодня потратил некоторое время на изучение примера и разобрался в использовании его API. Чтобы в будущем было меньше проблем, а также для облегчения изучения, я написал эту статью, чтобы записать, как использовать и на что следует обратить внимание.
В процессе обучения я действительно пришёл к такому выводу, что WKWebView действительно намного мощнее, чем UIWebView. Его возможности взаимодействия с JS явно улучшены, а скорость загрузки повысилась.
Новые функции WKWebView:
Прежде всего, мы импортируем модуль в месте использования:
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 )