2014-09-30 18 views
30

Ho un modulo nella mia app iOS 7+ che è un UIWebView. La pagina html carica un javascript che crea pulsanti personalizzati (usando la libreria Raphaeljs). Con UIWebView, imposto il delegato a se stesso. Il metodo delegato webView: shouldStartLoadWithRequest: navigationType: viene chiamato ogni volta che viene premuto uno dei miei pulsanti personalizzati. Le richieste non dovrebbero essere gestite dall'html, ma piuttosto dal codice iOS. Quindi ho usato una convenzione di richiesta (leggi da qualche parte qui su stackoverflow) usando "inapp" come schema delle mie richieste. Quindi controllo l'host e intraprendo l'azione appropriata.Metodo delegato UIWebView shouldStartLoadWithRequest: equivalente in WKWebView?

Questo codice funziona perfettamente su iOS 7. Ma le visualizzazioni Web appaiono vuote su iOS 8 (bug?), Quindi ho deciso di utilizzare WKWebView per dispositivi iOS 8. Le visualizzazioni web ora rendono bene (e incredibilmente più veloce!), Ma i miei pulsanti non hanno alcun effetto.

Ho provato a utilizzare - (WKNaviation *)loadRequest:(NSURLRequest *)request, ma non viene chiamato.

Non riesco a trovare un equivalente diretto del metodo delegato UIWebView webView: shouldStartLoadWithRequest: navigationType:. Qual è il modo migliore per gestire tali richieste con WKWebView?

risposta

7

Rileggendo la descrizione sembra che ciò che è effettivamente necessario sapere è come reimplementare un bridge Javascript/Objective-C utilizzando WKWebView.

Ho appena fatto questo io stesso, seguendo il tutorial a http://tetontech.wordpress.com/2014/07/17/objective-c-wkwebview-to-javascript-and-back/ e informazioni al http://nshipster.com/wkwebkit/

WKWebView ha un modo integrato di comunicazione tra JavaScript e Objective-C/Swift: WKScriptMessageHandler.

In primo luogo, includendo anche l'intestazione WebKit e WKScriptMessageHandler protocollo nell'intestazione del vostro controller della vista:

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

@end 

L'inizializzazione quando il vostro WKWebView, è necessario configurarlo con un gestore di messaggi script. Chiamalo come vuoi, ma a me sembra che nominarlo per la tua app abbia senso.

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

Ora, attuare userContentController:didReceiveScriptMessage:. Questo si attiva quando la tua webview riceve un messaggio, quindi fa il lavoro che stavi facendo in precedenza con webView:shouldStartLoadWithRequest:navigationType:.

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

Ora sei pronto per ricevere messaggi da Javascript. La funzione di chiamata è necessario aggiungere al vostro Javascript è questo:

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

In primo luogo, mi scuso per il ritardo. Ho testato la tua soluzione per uno dei miei link, e ha funzionato. Grazie! Applicherò la soluzione ovunque ora. Tuttavia, posso vedere il mio contenuto web sul simulatore, ma non riesco a vederlo sul mio dispositivo. Sul dispositivo, vedo ancora una pagina vuota. Qualche idea del perché? – invalidArgument

+1

@invalidArgument Caricate il contenuto Web dal web o dal vostro pacchetto? Perché da allora ho imparato che c'è un bug in WKWebView per il caricamento di contenuti locali: http://stackoverflow.com/questions/24882834/wkwebview-not-working-in-ios-8-beta-4 – SeanR

+0

È caricato dal mio pacchetto. Ho appena pubblicato una domanda interamente dedicata al problema della pagina vuota: http://stackoverflow.com/q/26455432/873436. Quel collegamento è stato molto utile, grazie ancora. Ha portato ad un altro problema, però. – invalidArgument

57

Ho cercato una buona spiegazione, ma non ne ho trovato uno. Ho usato il seguente nella mia app e tutto sembra funzionare (Edit: aggiornato sulla base ccoroom's comment):

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

Ecco gli altri UIWebViewDelegate metodi:

UIWebViewDelegate  - webViewDidStartLoad: 
WKNavigationDelegate - webView:didCommitNavigation: 

UIWebViewDelegate  - webViewDidFinishLoad: 
WKNavigationDelegate - webView:didFinishNavigation: 

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

Mi piacerebbe per qualcuno per confermare questo per me però.

Edit: In realtà, ho risposto alla domanda si aveva nel titolo (anche se non sono più fiducioso che webView:didCommitNavigation: si chiama allo stesso identico punto del ciclo di vita), ma rileggendo tua descrizione sembra che ciò che è effettivamente necessario sapere è come reimplementare un bridge Javascript/Objective-C usando WKWebView. Quindi dai un'occhiata alla mia altra risposta.

+0

Forse mi manca qualcosa, ma una grande differenza tra webView: s houldStartLoadWithRequest: navigationType: e webView: didStartProvisionalNavigation: sembra che quest'ultimo non abbia la NSUrlRequest. Come ottieni informazioni sulla richiesta? – ajh158

+0

@ ajh158 È possibile ottenere l'URL da WkWebView.URL, ma non sono sicuro degli altri dettagli della richiesta. – SeanR

+0

Non direi che i primi due sono uguali, dal momento che non è possibile valutare la richiesta e interromperla in WKNavigationDelegate. –

0

È possibile aggiungere Observer per il vostro 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 
    } 
} 

Non dimenticate di rimuoverlo in viewWillDisappear

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

Per rispondere alla domanda originale, l'equivalente di webView:shouldStartLoadWithRequest:navigationType: in UIWebView è webView:decidePolicyForNavigationAction:decisionHandler: in WKWebView. Questi metodi vengono chiamati prima di ogni richiesta (compresa la richiesta iniziale) e forniscono la possibilità di permetterlo/impedirlo.

+3

la risposta corretta secondo me –

26

basta utilizzare il seguente metodo, è una parte di WKNavigationDelegate

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

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

    decisionHandler(WKNavigationActionPolicyAllow); 
} 
5

In Swift si può fare qualcosa di simile:

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 
    } 

} 

E questo è il link nella pagina web:

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