web-dev-qa-db-fra.com

WKWebView n'a pas terminé le chargement, quand didFinishNavigation est appelé - Bug dans WKWebView?

Objectif: réaliser une capture d'écran de WKWebView après le chargement du site Web.

Méthode employée:

  • Défini une var WKWebView dans UIViewController
  • Création d'une méthode d'extension appelée capture d'écran () qui prend une image de WKWebView

  • Fait mon UIViewController pour implémenter WKNavigationDelegate

  • Définissez wkwebview.navigationDelegate = self (dans l'initialisation UIViewController)

  • Implémentation de la fonction de délégation didFinishNavigation dans UIViewcontroller pour appeler la méthode d'extension de capture d'écran pour WKWebView

func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
    let img = webView.screenCapture()
}

Des questions:

  • Lorsque je débogue mon simulateur, je remarque que le contrôle atteint la fonction didFinishNavigation () bien que le site Web n'ait pas encore été rendu dans WKWebView
  • De manière correspondante, la capture d'écran est un blob blanc.

Qu'est-ce que j'oublie ici? J'ai examiné toutes les fonctions de délégation possibles pour WKWebView et rien d'autre ne semble représenter la fin du chargement du contenu dans WKWebView. J'apprécierais l'aide sur s'il y a un travail autour


Mise à jour: Ajout du code de capture d'écran que j'utilise pour prendre une capture d'écran pour l'affichage Web

class func captureEntireUIWebViewImage(webView: WKWebView) -> UIImage? {

    var webViewFrame = webView.scrollView.frame
    if (webView.scrollView.contentSize != CGSize(width: 0,height: 0)){
    webView.scrollView.frame = CGRectMake(webViewFrame.Origin.x, webViewFrame.Origin.y, webView.scrollView.contentSize.width, webView.scrollView.contentSize.height)

    UIGraphicsBeginImageContextWithOptions(webView.scrollView.contentSize, webView.scrollView.opaque, 0)
    webView.scrollView.layer.renderInContext(UIGraphicsGetCurrentContext())
     var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()

     webView.scrollView.frame = webViewFrame         
     return image
    }

    return nil
 }
19
shrutim

WKWebView n'utilise pas la délégation pour vous avertir que le chargement du contenu est terminé (c'est pourquoi vous ne pouvez trouver aucune méthode de délégation qui convienne à votre objectif). Pour savoir si un WKWebView est toujours en cours de chargement, utilisez KVO (observation de la valeur d'une clé) pour surveiller sa propriété loading. De cette manière, vous recevez une notification lorsque loading passe de true à false.

Voici un gif animé en boucle montrant ce qui se passe lorsque je teste cela. Je charge une vue Web et répond à sa propriété loading par le biais de KVO pour prendre un instantané. La vue supérieure est la vue Web; la vue inférieure (écrasée) est l’instantané. Comme vous pouvez le constater, l’instantané capture le contenu chargé:

enter image description here

14
matt

Pour ceux qui cherchent encore une réponse à cette question, la réponse marquée est BS, il s’est forcé à la faire accepter. 
"loading" et webView (webView: WKWebView, navigation didFinishNavigation: WKNavigation!) font tous les deux la même chose, indiquent si la ressource principale est chargée. Maintenant, cela ne signifie pas que toute la page Web/site Web est chargée, car cela dépend vraiment de la mise en œuvre du site Web. S'il doit charger des scripts et des ressources (images, polices, etc.) pour se rendre visible, vous ne verrez toujours rien une fois la navigation terminée, car les appels réseau effectués par le site Web ne sont pas suivis par la vue Web. suivi, de sorte qu'il ne sache pas vraiment quand le site Web sera complètement chargé.

20
Santhosh R

Voici comment je l'ai résolu:

class Myweb: WKWebView {

    func setupWebView(link: String) {
        let url = NSURL(string: link)
        let request = NSURLRequest(URL: url!)
        loadRequest(request)
        addObserver(self, forKeyPath: "loading", options: .New, context: nil)
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        guard let _ = object as? WKWebView else { return }
        guard let keyPath = keyPath else { return }
        guard let change = change else { return }
        switch keyPath {
        case "loading":
            if let val = change[NSKeyValueChangeNewKey] as? Bool {
                if val {
                } else {
                    print(self.loading)
                    //do something!
                }
            }
        default:break
        }
    }

    deinit {
        removeObserver(self, forKeyPath: "loading")
    }
}

Mettre à jour Swift 3.1 

override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    guard let _ = object as? WKWebView else { return }
    guard let keyPath = keyPath else { return }
    guard let change = change else { return }

    switch keyPath {
    case "loading":
        if let val = change[NSKeyValueChangeKey.newKey] as? Bool {
            //do something!
        }
    default:
        break
    }
}
9
Esqarrouth

Ce n'est pas un bon choix pour vérifier si le contenu de la page chargé depuis Swift ou objective-c est spécialement conçu pour les pages très complexes comportant de nombreux contenus dynamiques.

Un meilleur moyen de vous informer du code ios à partir du javascript de la page Web. Cela le rend très flexible et efficace, vous pouvez notifier le code ios lorsqu'un dom ou la page est chargée.

Vous pouvez consulter mon post ici pour plus d'informations.

1
Kris Roofe

Beaucoup de mains abandonnées ici, pour des solutions incomplètes. L'observateur sur "chargement" n'est pas fiable car lorsqu'il passe en mode NON, la mise en page n'a pas encore eu lieu et vous ne pouvez obtenir une lecture précise du format de page.

L'injection de JS dans la vue pour signaler la taille de la page peut également être problématique en fonction du contenu réel de la page.

WKWebView utilise évidemment un scroller (une sous-classe "WKWebScroller" pour être exact). Donc, le mieux est de surveiller le contentSize de ce défilement.

    - (void) viewDidLoad {
    //...super etc
    [self.webKitView.scrollView addObserver: self
                                 forKeyPath: @"contentSize"
                                    options: NSKeyValueObservingOptionNew
                                    context: nil];
    }

    - (void) dealloc
    {
        // remove observer
        [self.webKitView.scrollView removeObserver: self
                                        forKeyPath: @"contentSize"];
    }

    - (void) observeValueForKeyPath: (NSString*) keyPath
                           ofObject: (id) object
                             change: (NSDictionary<NSKeyValueChangeKey,id>*) change
                            context: (void*) context
    {
        if ([keyPath isEqualToString: @"contentSize"])
        {
            UIScrollView*   scroller    =   (id) object;
            CGSize          scrollFrame =   scroller.contentSize;

            NSLog(@"scrollFrame = {%@,%@}",
                  @(scrollFrame.width), @(scrollFrame.height));
        }
    }

Méfiez-vous de la taille du contenu: cela se déclenche BEAUCOUP. Si votre WebView est intégrée à une autre zone de défilement (comme si vous l'utilisiez comme élément de formulaire), lorsque vous faites défiler la totalité de la vue, cette sous-vue WebView déclenchera les modifications pour les mêmes valeurs. Alors, assurez-vous de ne pas redimensionner inutilement ou provoquer des rafraîchissements inutiles.

Cette solution testée sur iOS 12 sur iPad Air 2 sim via High Sierra XCode 10.

0
Martin-Gilles Lavoie