J'ai un module dans mon application iOS 7+ qui est un UIWebView. La page html charge un javascript qui crée des boutons de forme personnalisée (à l'aide de la bibliothèque Raphaeljs). Avec UIWebView, je règle le délégué à moi-même. La méthode déléguée webView: shouldStartLoadWithRequest: navigationType:
est appelée chaque fois que l’on appuie sur un de mes boutons personnalisés. Les requêtes ne doivent pas être traitées par le code HTML, mais par le code iOS. J'ai donc utilisé une convention de requête (lue quelque part ici sur stackoverflow) en utilisant "inapp" comme schéma de mes requêtes. Je vérifie ensuite pour l'hôte et prend les mesures appropriées.
Ce code fonctionne bien sous iOS 7. Mais les vues Web étant vides sous iOS 8 (bug?), J'ai donc décidé d'utiliser WKWebView pour iOS 8. Les vues Web affichent maintenant un rendu correct (et incroyablement plus rapide!), Mais mes boutons n’ont aucun effet.
J'ai essayé d'utiliser - (WKNaviation *)loadRequest:(NSURLRequest *)request
, mais ce n'est pas appelé.
Je ne trouve pas d'équivalent direct de la méthode de délégué UIWebView webView: shouldStartLoadWithRequest: navigationType:
. Quelle est la meilleure façon de traiter ces demandes avec WKWebView?
Relisez votre description. Vous devez savoir comment réimplémenter un pont Javascript/Objective-C avec WKWebView.
Je viens de le faire moi-même, en suivant le tutoriel à http://tetontech.wordpress.com/2014/07/17/objective-c-wkwebview-to-javascript-and-back/ et l'info à l'adresse http://nshipster.com/wkwebkit/
WKWebView intègre une méthode de communication intégrée entre Javascript et Objective-C/Swift: WKScriptMessageHandler
.
Commencez par inclure les en-têtes WebKit et le protocole WKScriptMessageHandler
dans l'en-tête de votre contrôleur de vue:
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@interface ViewController : UIViewController <WKScriptMessageHandler>
@end
Lors de l’initialisation de votre WKWebView
, vous devez le configurer avec un gestionnaire de messages de script. Nommez-le comme vous voulez, mais il me semble logique de le nommer pour votre application.
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];
Maintenant, implémentez userContentController:didReceiveScriptMessage:
. Cela se déclenche lorsque votre vue Web reçoit un message. Ainsi, le travail que vous faisiez précédemment avec webView:shouldStartLoadWithRequest:navigationType:
.
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message {
NSDictionary *sentData = (NSDictionary *)message.body;
NSString *messageString = sentData[@"message"];
NSLog(@"Message received: %@", messageString);
}
Vous êtes maintenant prêt à recevoir des messages de Javascript. L’appel de fonction que vous devez ajouter à votre Javascript est le suivant:
window.webkit.messageHandlers.myApp.postMessage({"message":"Hello there"});
Pour répondre à la question initiale, l'équivalent de webView:shouldStartLoadWithRequest:navigationType:
dans UIWebView est webView:decidePolicyForNavigationAction:decisionHandler:
dans WKWebView. Ces méthodes sont appelées avant chaque demande (y compris la demande initiale) et permettent de l’autoriser/de la refuser.
J'ai moi-même cherché une bonne explication, mais je n'en ai pas trouvé. J'ai utilisé les éléments suivants dans mon application et tout semble fonctionner (Edit: mis à jour à partir du commentaire de ccoroom ):
UIWebViewDelegate - webView:shouldStartLoadWithRequest:navigationType:
WKNavigationDelegate - webView:decidePolicyForNavigationAction:decisionHandler:
Voici les autres méthodes UIWebViewDelegate
:
UIWebViewDelegate - webViewDidStartLoad:
WKNavigationDelegate - webView:didCommitNavigation:
UIWebViewDelegate - webViewDidFinishLoad:
WKNavigationDelegate - webView:didFinishNavigation:
UIWebViewDelegate - webView:didFailLoadWithError:
WKNavigationDelegate - webView:didFailNavigation:withError:
- webView:didFailProvisionalNavigation:withError:
J'aimerais bien que quelqu'un me le confirme.
Edit: En fait, j'ai répondu à la question que vous aviez dans le titre (même si je ne suis plus sûr que webView:didCommitNavigation:
s'appelle exactement au même moment du cycle de vie), mais relisez votre description, elle ressemble Ce que vous devez réellement savoir, c'est comment réimplémenter un pont Javascript/Objective-C avec WKWebView. Regardez donc mon autre réponse.
Utilisez simplement la méthode suivante, elle fait partie de WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURLRequest *request = navigationAction.request;
NSString *url = [[request URL]absoluteString];
decisionHandler(WKNavigationActionPolicyAllow);
}
Dans Swift vous pouvez faire quelque chose comme ceci:
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
}
}
Et voici le lien dans la page Web:
<a href="http://action.is.needed/some-action">Hello world!</a>
pour Swift 4.2 : (extrait de 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
}
}
Vous pouvez ajouter Observer pour votre 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
}
}
N'oubliez pas de l'enlever à la vueWillDisappear
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[webView removeObserver:self forKeyPath:@"URL"];
}