Метод делегата UIWebView shouldStartLoadWithRequest: эквивалент в WKWebView?

В моем приложении iOS 7+ есть модуль UIWebView. Страница html загружает javascript, который создает кнопки произвольной формы (с использованием библиотеки Raphaeljs). С UIWebView я устанавливаю делегата на себя. Метод делегата webView: shouldStartLoadWithRequest: navigationType: вызывается каждый раз, когда нажимается одна из моих настраиваемых кнопок. Запросы должны обрабатываться не html, а кодом iOS. Поэтому я использовал соглашение о запросах (читайте здесь, в stackoverflow), используя «inapp» в качестве схемы моих запросов. Затем я проверяю хост и предпринимаю соответствующие действия.

Этот код отлично работает на iOS 7. Но веб-представления выглядят пустыми на iOS 8 (ошибка?), Поэтому я решил использовать WKWebView для устройств iOS 8. Веб-представления теперь отображаются нормально (и на удивление быстрее!), Но мои кнопки не действуют.

Я пробовал использовать - (WKNaviation *)loadRequest:(NSURLRequest *)request, но он не называется.

Я не могу найти прямой эквивалент метода делегата UIWebView webView: shouldStartLoadWithRequest: navigationType:. Как лучше всего обрабатывать эти запросы с помощью WKWebView?


person invalidArgument    schedule 30.09.2014    source источник


Ответы (7)


Перечитывая ваше описание, похоже, что вам действительно нужно знать, как заново реализовать мост Javascript / Objective-C с помощью WKWebView.

Я только что сделал это сам, следуя руководству по адресу http://tetontech.wordpress.com/2014/07/17/objective-c-wkwebview-to-javascript-and-back/ и информацию по адресу http://nshipster.com/wkwebkit/

WKWebView имеет встроенный способ связи между Javascript и Objective-C / Swift: WKScriptMessageHandler.

Сначала включите заголовки WebKit и протокол WKScriptMessageHandler в заголовок вашего контроллера представления:

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@interface ViewController : UIViewController <WKScriptMessageHandler>

@end

При инициализации вашего WKWebView вам необходимо настроить его с помощью обработчика сообщений сценария. Назовите его как хотите, но мне кажется, что название для вашего приложения имеет смысл.

    WKWebViewConfiguration *theConfiguration = 
          [[WKWebViewConfiguration alloc] init];
    [theConfiguration.userContentController 
          addScriptMessageHandler:self name:@"myApp"];

    _theWebView = [[WKWebView alloc] initWithFrame:self.view.frame 
                      configuration:theConfiguration];
    [_theWebView loadRequest:request];
    [self.view addSubview:_theWebView];

Теперь реализуйте userContentController:didReceiveScriptMessage:. Это срабатывает, когда ваш веб-просмотр получает сообщение, поэтому он выполняет ту работу, которую вы раньше выполняли с webView:shouldStartLoadWithRequest:navigationType:.

- (void)userContentController:(WKUserContentController *)userContentController 
                        didReceiveScriptMessage:(WKScriptMessage *)message {
    NSDictionary *sentData = (NSDictionary *)message.body;
    NSString *messageString = sentData[@"message"];
    NSLog(@"Message received: %@", messageString);
}

Теперь вы готовы получать сообщения от Javascript. Вызов функции, который вам нужно добавить в свой Javascript, выглядит следующим образом:

window.webkit.messageHandlers.myApp.postMessage({"message":"Hello there"});
person SeanR    schedule 07.10.2014
comment
Во-первых, прошу прощения за задержку. Я протестировал ваше решение по одной из своих ссылок, и оно сработало. Спасибо! Теперь буду везде применять раствор. Хотя я могу видеть свой веб-контент на симуляторе, но не вижу его на своем устройстве. На устройстве все еще отображается пустая страница. Есть идеи, почему? - person invalidArgument; 20.10.2014
comment
@invalidArgument Вы загружаете веб-контент из Интернета или из своего пакета? Поскольку с тех пор я узнал, что в WKWebView есть небольшая ошибка для загрузки локального контента: stackoverflow.com/questions/24882834/ - person SeanR; 20.10.2014
comment
Он загружается из моей связки. Я только что разместил вопрос, полностью посвященный проблеме с пустой страницей: stackoverflow.com/q/26455432/873436. Эта ссылка была очень полезной, еще раз спасибо. Однако это привело к другой проблеме. - person invalidArgument; 20.10.2014
comment
Это так сложно. Почему бы не использовать решитьPolicyForNavigationAction? - person OpenThread; 09.04.2016

Я сам искал хорошее объяснение, но не нашел. Я использовал следующее в своем приложении, и, похоже, все работает (Изменить: обновлено на основе комментарий ccoroom):

UIWebViewDelegate     - webView:shouldStartLoadWithRequest:navigationType:
WKNavigationDelegate  - webView:decidePolicyForNavigationAction:decisionHandler:

Вот другие UIWebViewDelegate методы:

UIWebViewDelegate     - webViewDidStartLoad:
WKNavigationDelegate  - webView:didCommitNavigation:

UIWebViewDelegate     - webViewDidFinishLoad:
WKNavigationDelegate  - webView:didFinishNavigation:

UIWebViewDelegate     - webView:didFailLoadWithError:
WKNavigationDelegate  - webView:didFailNavigation:withError:
                      - webView:didFailProvisionalNavigation:withError:

Я бы хотел, чтобы кто-нибудь подтвердил это для меня.

Изменить: На самом деле, я ответил на ваш вопрос в заголовке (хотя я больше не уверен, что webView:didCommitNavigation: вызывается в той же точке жизненного цикла), но перечитал ваше описание похоже, что вам действительно нужно знать, как заново реализовать мост Javascript / Objective-C с помощью WKWebView. Так что взгляните на мой другой ответ.

person SeanR    schedule 01.10.2014
comment
Возможно, мне что-то не хватает, но основная разница между webView: shouldStartLoadWithRequest: navigationType: и webView: didStartProvisionalNavigation: кажется, что последний не получает NSUrlRequest. Как получить информацию о запросе? - person ajh158; 01.11.2014
comment
@ ajh158 Вы можете получить URL-адрес из WkWebView.URL, но я не уверен в других деталях запроса. - person SeanR; 03.11.2014
comment
Я бы не сказал, что ваши первые два одинаковые, поскольку вы не можете оценить запрос и остановить его в WKNavigationDelegate. - person Travis M.; 14.08.2015
comment
webView: shouldStartLoadWithRequest: navigationType: аналогом является webView: resolvePolicyForNavigationAction: solutionHandler: - person ccoroom; 03.11.2015
comment
Это отвечает на заголовок вопроса. Тело вопроса - действительно отдельный вопрос, на что намекает @SeanR. - person Morkrom; 23.12.2015
comment
@ ajh158 Мы можем получить запрос от WKNavigationAction (WKNavigationAction.request) в - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;. Вы должны использовать вышеупомянутый метод делегата вместо webView:shouldStartLoadWithRequest:navigationType:. - person Avinash B; 13.11.2018

Чтобы ответить на исходный вопрос, эквивалент webView:shouldStartLoadWithRequest:navigationType: в UIWebView равен webView:decidePolicyForNavigationAction:decisionHandler: в WKWebView. Эти методы вызываются перед выполнением каждого запроса (включая начальный запрос) и предоставляют возможность разрешить / запретить его.

person robnasby    schedule 27.01.2015

Просто используйте следующий метод, он является частью WKNavigationDelegate

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    NSURLRequest *request = navigationAction.request;
    NSString *url = [[request URL]absoluteString];

    decisionHandler(WKNavigationActionPolicyAllow);
}
person Benzi Heler    schedule 25.02.2015
comment
resolvePolicyForNavigationAction он не вызывает. Мне нужно добавить что-нибудь еще для вызова этого метода на моей стороне javascript? - person Mihir Oza; 07.11.2019

В Swift вы можете сделать что-то вроде этого:

func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {

    switch navigationAction.request.URLString {
    case "http://action.is.needed/some-action":
        self.someFunc()
        decisionHandler(.Cancel)
        break
    default:
        decisionHandler(.Allow)
        break
    }

}

И это ссылка на веб-странице:

<a href="http://action.is.needed/some-action">Hello world!</a>
person He Yifei 何一非    schedule 30.06.2016

для swift 4.2: (взято из Yifei He 何 一 非.)

 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {

        let url = navigationAction.request.url
        let urlStr = url?.absoluteString
        switch urlStr {
        case BASE_URL:
            //self.someFunc()
            decisionHandler(.cancel)
            break
        default:
            decisionHandler(.allow)
            break
        }

    }
person ingconti    schedule 27.12.2018
comment
решитьPolicyFor часто не будет достигнуто, потому что временная навигация не удастся - person ericjam; 18.03.2019

Вы можете добавить наблюдателя для своего WKWebView

 static void* keyValueObservingContext = &keyValueObservingContext;

[webView addObserver:self forKeyPath:@"URL" options:0 context:keyValueObservingContext];


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
 {

if ([keyPath isEqualToString:@"URL"]){
// do something
  }
}

Не забудьте убрать его в viewWillDisappear

-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[webView removeObserver:self forKeyPath:@"URL"];
}
person ramzi shadid    schedule 16.10.2014