2014-09-30 12 views
30

Mam moduł wewnątrz mojej aplikacji na iOS 7+, która jest UIWebView. Strona html ładuje javascript, który tworzy przyciski o niestandardowych kształtach (przy użyciu biblioteki Raphaeljs). Dzięki UIWebView ustawiam delegata na siebie. Metoda delegata webView: shouldStartLoadWithRequest: navigationType: jest wywoływana za każdym razem, gdy naciśnięty jest jeden z moich przycisków niestandardowych. Żądania nie powinny być obsługiwane przez html, ale raczej przez kod iOS. Więc użyłem konwencji o żądaniach (przeczytaj gdzieś tutaj na stackoverflow) używając "inapp" jako schematu moich żądań. Następnie sprawdzam hosta i podejmuję odpowiednie działania.Metoda delegowania UIWebView shouldStartLoadWithRequest: odpowiednik w WKWebView?

Ten kod działa poprawnie na iOS 7. Ale widoki sieciowe są puste na iOS 8 (błąd?), Więc zdecydowałem się użyć WKWebView na urządzenia z systemem iOS 8. Widoki w sieci są teraz w porządku (i niesamowicie szybsze!), Ale moje przyciski nie działają.

Próbowałem użyć - (WKNaviation *)loadRequest:(NSURLRequest *)request, ale nie zostało to wywołane.

Nie mogę znaleźć bezpośredniego odpowiednika metody delegowania UIWebView webView: shouldStartLoadWithRequest: navigationType:. Jaki jest najlepszy sposób obsługi tych żądań za pomocą WKWebView?

Odpowiedz

7

Ponownie czytając twój opis, wygląda na to, co musisz wiedzieć o tym, jak ponownie zaimplementować mostek JavaScript/Objective-C za pomocą WKWebView.

Właśnie zrobić to samodzielnie, zgodnie z samouczka w http://tetontech.wordpress.com/2014/07/17/objective-c-wkwebview-to-javascript-and-back/ i informacji na http://nshipster.com/wkwebkit/

WKWebView posiada wbudowany w sposób komunikowania się między JavaScript i Objective-C/SWIFT: WKScriptMessageHandler.

Po pierwsze, to nagłówki WebKit i WKScriptMessageHandler protokół w nagłówku Państwa zdanie kontrolera:

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

@end 

podczas inicjalizacji swoją WKWebView, trzeba skonfigurować go z obsługi komunikatów skrypt. Nazwij to, co chcesz, ale wydaje mi się, że nazwanie go dla Twojej aplikacji ma sens.

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]; 

Teraz zaimplementuj userContentController:didReceiveScriptMessage:. Ta funkcja jest uruchamiana, gdy twoja wiadomość sieci Web otrzyma wiadomość, więc wykonuje ona pracę, którą wcześniej wykonywałeś z webView:shouldStartLoadWithRequest:navigationType:.

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

Jesteś teraz gotowy do odbierania wiadomości z Javascript. Funkcja zadzwonić trzeba dodać do swojej JavaScript jest taka:

window.webkit.messageHandlers.myApp.postMessage({"message":"Hello there"}); 
+0

Po pierwsze, przepraszam za opóźnienie. Przetestowałem twoje rozwiązanie dla jednego z moich linków i zadziałało. Dziękuję Ci! Zastosuję to rozwiązanie wszędzie teraz. Chociaż widzę zawartość sieci na symulatorze, ale nie widzę jej na moim urządzeniu. Na urządzeniu wciąż widzę pustą stronę. Każdy pomysł, dlaczego? – invalidArgument

+1

@invalidArgument Czy ładujesz zawartość internetową z Internetu lub swojego pakietu? Ponieważ dowiedziałem się, że w serwisie WKWebView występuje niewielki problem z ładowaniem lokalnych treści: http://stackoverflow.com/questions/24882834/wkwebview-not-working-in-ios-8-beta-4 – SeanR

+0

załadowany z mojego pakietu. Właśnie napisałem pytanie poświęcone pustej stronie: http://stackoverflow.com/q/26455432/873436. Ten link był bardzo pomocny, dzięki jeszcze raz. Doprowadziło to jednak do innego problemu. – invalidArgument

57

Sam szukałem dobrego wyjaśnienia, ale go nie znalazłem. Użyłem następujących w mojej aplikacji i wszystko wydaje się działać (Edit: aktualizacja na podstawie ccoroom's comment):

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

Oto pozostałe UIWebViewDelegate metody:

UIWebViewDelegate  - webViewDidStartLoad: 
WKNavigationDelegate - webView:didCommitNavigation: 

UIWebViewDelegate  - webViewDidFinishLoad: 
WKNavigationDelegate - webView:didFinishNavigation: 

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

Chciałbym za ktoś mi to potwierdził.

Edit: Faktycznie, mam odpowiedział na pytanie trzeba było w tytule (chociaż nie jestem już pewien, że webView:didCommitNavigation: nazywa się dokładnie w tym samym momencie w cyklu), ale ponownie go przeczytaniu opisu wygląda na to, co musisz wiedzieć o tym, jak ponownie zaimplementować mostek JavaScript/Objective-C za pomocą WKWebView. Więc spójrz na moją drugą odpowiedź.

+0

Może brakuje mi czegoś, ale główna różnica między webView: s houldStartLoadWithRequest: navigationType: i webView: didStartProvisionalNavigation: wydaje się, że ten ostatni nie otrzymuje NSUrlRequest. Jak uzyskać informacje o żądaniu? – ajh158

+0

@ ajh158 Możesz uzyskać adres URL z WkWebView.URL, ale nie jestem pewien innych szczegółów wniosku. – SeanR

+0

Nie powiedziałbym, że twoje pierwsze dwie są takie same, ponieważ nie można ocenić żądania i zatrzymać go w WKNavigationDelegate. –

0

Możesz dodać Observer dla 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 
    } 
} 

Nie zapomnij usunąć go w viewWillDisappear

-(void)viewWillDisappear:(BOOL)animated{ 
[super viewWillDisappear:animated]; 
[webView removeObserver:self forKeyPath:@"URL"]; 
} 
59

Aby odpowiedzieć na oryginalne pytanie, odpowiednikiem webView:shouldStartLoadWithRequest:navigationType: w UIWebView jest webView:decidePolicyForNavigationAction:decisionHandler: w WKWebView. Te metody są wywoływane przed każdym żądaniem (łącznie z początkowym żądaniem) i umożliwiają mu akceptację/odrzucenie.

+3

poprawna odpowiedź w mojej opinii –

26

Wystarczy użyć następującą metodę, to część WKNavigationDelegate

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

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

    decisionHandler(WKNavigationActionPolicyAllow); 
} 
5

W Swift można zrobić coś takiego:

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 
    } 

} 

I to jest link na stronie internetowej:

<a href="http://action.is.needed/some-action">Hello world!</a> 
Powiązane problemy