WKWebView не завершил загрузку, когда вызывается функция FinishNavigation - ошибка в WKWebView?

Цель: сделать снимок экрана WKWebView после завершения загрузки сайта

Используемый метод:

  • Определено WKWebView var в UIViewController
  • Создан метод расширения, называемый захватом экрана(), который принимает изображение WKWebView

  • Сделал мой UIViewController для реализации WKNavigationDelegate

  • Установите wkwebview.navigationDelegate = self (в инициализации UIViewController)

  • Реализована функция делегирования didFinishNavigation в UIViewcontroller для вызова метода расширения захвата экрана для WKWebView

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

Вопросы:

  • Когда я отлаживаю i-симулятор, я замечаю, что элемент управления достигает функции didFinishNavigation(), хотя веб-сайт еще не отображен в WKWebView
  • Соответственно сделанный снимок экрана - это белый кадр.

Что мне здесь не хватает? Я просмотрел все возможные функции делегирования для WKWebView, и ничто иное не представляет собой завершение загрузки контента в WKWebView. Поблагодарите помощь, если есть работа вокруг


Обновление: добавление кода скриншота, который я использую, чтобы сделать снимок экрана для веб-представления

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
 }

Ответ 1

WKWebView не использует делегирование, чтобы вы знали, когда загрузка контента завершена (почему вы не можете найти какой-либо метод делегата, который соответствует вашей цели). Способ узнать, все еще загружается WKWebView, - использовать KVO (наблюдение за ключевыми значениями), чтобы посмотреть его свойство loading. Таким образом вы получаете уведомление, когда loading изменяется от true до false.

Вот циклический анимированный gif, показывающий, что происходит, когда я проверяю это. Я загружаю веб-представление и отвечаю на его свойство loading через KVO, чтобы сделать снимок. Верхний вид - это веб-представление; нижний (сжатый) вид - это моментальный снимок. Как вы можете видеть, моментальный снимок захватывает загруженный контент:

enter image description here

Ответ 2

          Для тех, кто все еще ищет ответ на этот вопрос, отмеченный ответ - BS, он просто заставил его принять его.   "загрузка" и webView (webView: WKWebView, didFinishNavigation navigation: WKNavigation!) делают то же самое, указывают, загружен ли основной ресурс. Теперь это не означает, что загружается вся веб-страница/веб-сайт, потому что это действительно зависит от реализации веб-сайта. Если вам нужно загрузить сценарии и ресурсы (изображения, шрифты и т.д.), Чтобы сделать себя видимыми, вы ничего не увидите после завершения навигации, потому что сетевые вызовы, сделанные веб-сайтом, не отслеживаются веб-просмотром, отслеживается, поэтому он не будет знать, когда сайт загружен полностью.

Ответ 3

Вот как я это решил:

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

Ответ 4

Много ручного отказа здесь, для неполных решений. Наблюдатель при "загрузке" не является надежным, потому что, когда он переключился на "НЕТ", макет еще не произошел, и вы не можете получить точное представление о размере страницы.

Внедрение 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: он запускается много. Если ваше веб-представление встроено в другую область прокрутки (например, если вы используете его в качестве элемента формы), то при прокрутке всего представления это веб-представление подпредставления вызовет изменения для тех же значений. Поэтому убедитесь, что вы не изменяете размеры без необходимости и не вызываете ненужных обновлений.

Это решение протестировано на iOS 12 на iPad Air 2 с симулятором High Sierra XCode 10.

Ответ 5

Не рекомендуется выбирать, загружено ли содержимое страницы с быстрой или objective-c особенно для очень сложной страницы со многими динамическими контентами.

Лучший способ сообщить вам код ios с веб-страницы javascript. Это делает его очень гибким и эффективным, вы можете уведомить ios-код, когда загружается dom или страница.

Здесь вы можете узнать мой пост.

Ответ 6

Поэтому ни один из этих ответов не работал у меня. Вот что работало.

Я использую webView как имя моего WKWebView. Переходите к тому, что вы использовали.

Я использовал Swift Spinner

Это создает великолепный полупрозрачный модальный надпись на всем экране. Загрузите его в viewDidLoad следующим образом:

SwiftSpinner.show("Doing something cool....")

Использование наблюдателя для остановки следующим образом:

Сначала добавьте это в свой метод webview... где вы загружаете свой URL-адрес:

 webView?.addObserver(self, forKeyPath: #keyPath(WKWebView.loading), options: .new, context: nil)

Это используется для наблюдателя:

// To handle spinner stop;  was testing other stuff, could simplify
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard let wv = object as? WKWebView else {
        return
    }
    guard let keyPath = keyPath else {
        return
    }
    guard let change = change else {
        return
    }

    print("in observer")
    switch keyPath {
    case "loading": // new:1 or 0
        if let val = change[.newKey] as? Bool {
            if val {
                print("starting animating")
                /// spinner.startAnimating()
            } else {
                if wv.estimatedProgress == 1 {
                   print("finished loading! stop animating spinner")
                   SwiftSpinner.hide()
                }
            }
        }

    default:
        break