web-dev-qa-db-ja.com

didFinishNavigationが呼び出されたときに、WKWebViewのロードが完了しませんでした-WKWebViewのバグ?

目標:Webサイトの読み込みが完了した後にWKWebViewのスクリーンショットを撮る

採用された方法:

  • UIViewControllerでWKWebView変数を定義しました
  • WKWebViewの画像を取得するscreen capture()という拡張メソッドを作成しました

  • UIViewControllerを作成してWKNavigationDelegateを実装しました

  • Wkwebview.navigationDelegate = selfを設定します(UIViewController init内)

  • UIViewcontrollerにdidFinishNavigation委任funcを実装して、WKWebViewの画面キャプチャ拡張メソッドを呼び出しました。

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

質問:

  • Iシミュレーターをデバッグすると、WebサイトがWKWebViewでまだレンダリングされていなくても、コントロールがdidFinishNavigation()funcに到達することがわかります
  • それに応じて、撮影された画像のスクリーンショットは白いブロブです。

ここで何が欠けていますか? WKWebViewで可能なすべてのデリゲート関数を調べましたが、WKWebViewでのコンテンツの読み込みの完了を表すものは他にありません。回避策があれば助けていただければ幸いです


更新: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
 }
23
shrutim

WKWebViewは委任を使用してコンテンツの読み込みが完了したことを通知しません(そのため、目的に合った委任メソッドが見つかりません)。 WKWebViewがまだロードされているかどうかを知る方法は、KVO(キー値監視)を使用してloadingプロパティを監視することです。このようにして、loadingtrueからfalseに変更されたときに通知を受け取ります。

これは、これをテストするとどうなるかを示すループアニメーションGIFです。 Webビューをロードし、KVOを介してそのloadingプロパティに応答してスナップショットを取得します。上部のビューはWebビューです。下の(押しつぶされた)ビューはスナップショットです。ご覧のとおり、スナップショットはロードされたコンテンツをキャプチャします。

enter image description here

12
matt

まだこれに対する答えを探している人にとって、マークされた答えはBSです、彼はそれを受け入れさせるために彼の方法を強要しました。
「ロード」とwebView(webView:WKWebView、didFinishNavigation navigation:WKNavigation!)は両方とも同じことを行い、メインリソースがロードされているかどうかを示します。これは、Webページ/ Webサイト全体がロードされることを意味するものではありません。Webサイトの実装に本当に依存しているからです。スクリプトやリソース(画像、フォントなど)を読み込んで表示できるようにする必要がある場合、ナビゲーションが完了しても何も表示されません。ウェブサイトによって行われたネットワーク呼び出しはウェブビューによって追跡されず、ナビゲーションのみが追跡されているため、いつWebサイトが完全に読み込まれたかはわかりません。

25
Santhosh R

ここに私がそれを解決した方法があります:

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")
    }
}

更新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
    }
}
8
Esqarrouth

不完全なソリューションのために、ここで多くの手を放棄します。 「ロード」のオブザーバーは、NOに切り替えたときにレイアウトがまだ行われておらず、ページサイズの正確な読み取り値を取得できないため、信頼できません。

ページサイズをレポートするためにビューにJSを挿入することも、実際のページコンテンツによっては問題になる可能性があります。

WKWebViewは、明らかに、スクローラー(正確には「WKWebScroller」サブクラス)を使用します。したがって、最善の策は、そのスクローラーのcontentSizeを監視することです。

    - (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));
        }
    }

ContentSizeに気をつけてください:それはLOTをトリガーします。 webViewが別のスクロール領域に埋め込まれている場合(フォーム要素として使用している場合など)、ビュー全体をスクロールすると、そのサブビューwebViewは同じ値の変更をトリガーします。したがって、不必要にサイズを変更したり、不必要な更新を行わないようにしてください。

このソリューションは、iPad Air 2上のiOS 12でHigh Sierra XCode 10からシミュレーションを行いました。

ページコンテンツがSwift=またはObjective-cから読み込まれたかどうかを確認するのは良い選択ではありません。特に、多くの動的コンテンツを含む非常に複雑なページの場合。

WebページのJavaScriptからiOSコードを通知するより良い方法。これにより、非常に柔軟で効果的になり、domまたはページがロードされたときにiOSコードを通知できます。

詳細については、ここpostで確認できます。

1
Kris Roofe