Интерфейс приложения
Мы уже добавили в проект (код доступен на GitHub – прим. ред.) перечисление WebViewNavigationAction, которое описывает три действия: назад, вперед, перезагрузить. Создадим для них SwiftUI View, и назовем ее WebNavigationView, в который добавим кнопки действий. Поскольку WebView подгружает веб-страницу из интернета, а это действие не происходит мгновенно, добавим LoaderView чтобы показать пользователю прогресс загрузки.
Пока действие нажатия на кнопку action оставим пустыми. Вернемся к ним позже.
ViewModel
Мы добрались до самого интересного! Теперь подружим все элементы вместе. Для начала создадим ViewModel и подумаем, чего мы хотим от WebView.
- Получить заголовок веб-страницы – добавим свойство WebTitle;
- Определять действия навигации – добавим свойство webViewNavigationPublisher;
- Определять когда нужно показать LoaderView – добавим свойство isLoaderVisible.
Обновим ContentView, определим ViewModel и состояния isLoaderVisible:
Пора добавить весь созданный интерфейс в ContentView
Теперь вернемся в WebView и добавим свойство @ObservedObject var viewModel: ViewModel
, а также метод makeCoordinator, который будет возвращать Coordinator для взаимодействия с функциями делагата из WKNavigationDelegate.
Напишем реализацию класса Coordinator.
Прежде чем заняться навигацией в WebView, проверим что все работает. Изменим состояние isLoaderVisible на true и посмотрим результат в preview.
Погружение в WKNavigationDelegate
Давайте посмотрим, что нам предлагает реализовать протокол. Перейдите к его определению (Jump to Definition в Xcode; или зажмите ⌃⌘ и кликните). Как видите, все методы опциональные и не требуют реализации.
Далее приведем описание функций, чтобы вы их знали и могли использовать в своих целях.
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!)
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error)
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!)
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!)
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error)
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
func webViewWebContentProcessDidTerminate(_ webView: WKWebView)
func webView(_ webView: WKWebView, authenticationChallenge challenge: URLAuthenticationChallenge, shouldAllowDeprecatedTLS decisionHandler: @escaping (Bool) -> Void)
Web Navigation
Добавим реализацию основных функций делегата в проект и пропишем везде вывод в консоль, для наглядности (пользователю нужно видеть, что происходит при загрузке).
В методе makeUIView, у WebView укажем координатор, где мы реализовали протокол WKNavigationDelegate
webView.navigationDelegate = context.coordinator
Поскольку мы добавили реализацию decidePolicyFor, нужно явно определить политику навигации. WKNavigationActionPolicy – это перечисление с двумя значениями allow и cancel. На данном этапе мы разрешим все.
decisionHandler(.allow, preferences)
Протестируйте проект и посмотрите, как работает приложение.
Займемся состоянием загрузки веб-страницы. Из описаний функций становится понятно, когда нужно скрыть или показать LoaderView – мы просто сообщим ViewModel необходимое состояние.
self.parent.viewModel.isLoaderVisible.send(true)
В ContentView обработаем это действие. Вызовем у VStack onReceive и установим состоянию isLoaderVisible значение из ViewModel
Отлично, процесс загрузки веб-страницы работает как часы.
Вернемся в WebView и добавим логику для действий навигации в функции didStartProvisionalNavigation
Осталось добавить обработчики действий в WebNavigationView, которые мы оставили пустыми. Необходимо передать ViewModel во View и отправить соответствующее действие для каждой кнопки. Например, для перезагрузки:
viewModel.webViewNavigationPublisher.send(.reload)
Прежде чем тестировать навигацию, поработаем над получением title веб-страницы.
При помощи метода evaluateJavaScript из webView мы можем вызвать любой код на JavaScript и получать результат в Swift, т.е. получить любую информацию с веб-страницы. В методе didFinish, когда навигация завершена, получим title и сообщим его ViewModel.
Теперь покажем его в WebNavigationView.
Для этого добавим состояние @State var webTitle = ""
, значение которого будем показывать в Text (разместим его между Divider и Spacer).
Готово! Давайте протестируем, как это работает.
Первая часть цикла доступна по ссылке. Продолжение следует…
Комментарии