UIWebView — как определить последнее сообщение webViewDidFinishLoad?

Кажется, что сообщение webViewDidFinishLoad отправляется каждый раз при загрузке любого объекта на странице. Есть ли способ определить, что вся загрузка контента выполнена?


person chendral    schedule 25.05.2009    source источник
comment
Принятый ответ на этот вопрос и ответ с наибольшим количеством обновлений являются неполными. Посмотрите на эту тему, но игнорируйте принятый ответ, который просто перенаправляет на этот. stackoverflow.com/questions/10996028/   -  person Gruntcakes    schedule 23.04.2014


Ответы (10)


Интересно, не думал, что так получится. Хотя я уверен, что есть и другие способы сделать это (есть ли способ извлечь URL-адрес из сообщения webViewDidFinishLoad, чтобы вы могли видеть, какая из них завершает загрузку главной страницы?), главное, что я могу придумать, это использовать предполагаемый прогресс, чтобы проверить ход страницы и запустить все, что вы хотите сделать, когда она на 100% завершит загрузку, что я и делаю в своем приложении. Погуглите «iphone webview Assessmentprogress» и нажмите на первую ссылку, чтобы найти руководство, которое я написал о том, как это сделать.

Обновлять:

Пожалуйста, используйте ответ Фопкинса ниже вместо моего! Использование частных API в ваших приложениях — плохая идея, и вы, вероятно, получите отказ, а его решение — правильное.

person AriX    schedule 26.05.2009
comment
У меня есть аналогичная проблема в одном из моих приложений, и я реализовал решение AriX - однако мой вопрос заключается в том, честно ли Apple разрешает это в магазине приложений? Один из комментариев предполагает, что они не 't (по крайней мере, в 2009 году). Кто-нибудь имел более свежий опыт с этим? - person Jon; 11.03.2012
comment
Я считаю, что в какой-то момент Apple разрешила такие приложения в своем магазине, но я могу подтвердить, что в настоящее время они этого не делают. Я отправил приложение с помощью этого API в ноябре, и оно было отклонено. Почему Apple до сих пор не позволяет разработчикам получить доступ к прогрессу веб-представления, спустя четыре года после запуска iPhone SDK? Кто знает. - person AriX; 09.04.2012
comment
На самом деле, мое приложение просто отклонено из AppStore из-за использования закрытого _documentView. Будь осторожен. :-) - person Tsuneo Yoshioka; 08.01.2013
comment
Погуглив веб-просмотр iphone, вы попадете на эту страницу. Я не думаю, что вы действительно продумали эту стратегию связывания. :) - person Wayne; 25.07.2014

Я предполагаю, что iframes вызывают пару webViewDidStartLoad/webViewDidFinishLoad.

Проверка [webView isLoading], упомянутая в качестве ответа, у меня не сработала; он вернул false даже после первого из двух вызовов webViewDidFinishLoad. Вместо этого я отслеживаю загрузку следующим образом:

- (void)webViewDidStartLoad:(UIWebView *)webView {
  webViewLoads_++;
}


- (void)webViewDidFinishLoad:(UIWebView *)webView {
  webViewLoads_--;

  if (webViewLoads_ > 0) {
    return;
  }

  …
}

- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error {
  webViewLoads_--;
}

(Обратите внимание, что это будет работать только в том случае, если пары «начало/конец» не приходят последовательно, но по моему опыту этого не произошло.)

person Fiona Hopkins    schedule 09.05.2010
comment
@phopkins добавь это :) (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error { -- webViewLoads_; } - person ; 05.04.2011
comment
Я ненавижу взламывать подобные глупые обходные пути, но это все же лучше, чем принятый ответ, который включает использование частных API Apple. Спасибо за ответ. - person nont; 15.02.2012
comment
@HyejuJung прав. Обязательно реализуйте didFailLoadWithError: иначе балансировка собьется и вам будет грустно :( - person Jarsen; 01.06.2012
comment
Не работает для меня. Ожидающий счетчик несколько раз обнулялся на пути к завершению. Я загружаю несколько разных UIWebViews одновременно. - person Bill Cheswick; 03.04.2013
comment
Это прекрасно работает (пока вы используете все три метода делегата); так много сайтов в настоящее время, похоже, имеют 10-20+ дополнительных вызовов для встроенных фреймов (кнопки общего доступа и т. д.), вызовы ajax и т. д., и этот метод отслеживания помогает моему коду webViewDidFinishLoad выполняться один или два раза вместо 10+ раз. Спасибо! - person geerlingguy; 08.11.2013
comment
Так же, как @BillCheswick выше меня, ожидающий счетчик несколько раз обнулялся на пути к завершению, однако я использую только один UIWebView. Также навигация может завершиться в webView:didFailLoadWithError: и код завершения должен быть написан в обоих методах. - person HyBRiD; 16.12.2013
comment
То же, что Чес. Это не сработало для меня вообще. Ле вздох. - person Joe D'Andrea; 24.12.2013
comment
ЭТО НЕ правильный способ сделать это. Я думаю, что люди должны смотреть на эту ссылку, НО НЕ на принятый ответ, посмотрите на другие ответы. stackoverflow.com/questions/10996028/ - person Gruntcakes; 23.04.2014

Проверьте это, это определенно сработает для вас:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if ([[webView stringByEvaluatingJavaScriptFromString:@"document.readyState"] isEqualToString:@"complete"]) {
        // UIWebView object has fully loaded.
    }
}
person Vinod Singh    schedule 23.04.2014
comment
Отлично! Не взламывает и проверяет именно то состояние, которое нам нужно. - person Jonathan Zhan; 06.09.2014
comment
К сожалению, это не без своих собственных проблем. Иногда метод webViewDidFinishLoad: срабатывает, потому что ресурс завершил загрузку, но ДО того, как средство визуализации завершило построение DOM/загрузку Javascript и т. д., и поэтому document.readyState еще НЕ равен завершенному. Решение, вероятно, состоит в том, чтобы запустить таймер повторения из первого вызова webViewDidFinishLoad:, который оценивает document.readyState каждые 0,5 секунды или около того. Однако едва ли элегантный. - person Oliver Pearmain; 03.11.2014
comment
Не работает для меня. Мое состояние всегда интерактивно и никогда не завершено. - person Alix; 07.02.2015
comment
идеально и единственное, что работает! это должен быть принятый ответ. - person Mohammad Zekrallah; 18.02.2016
comment
Использовали решение NSTimer (с интервалом 0,1). Это не приятно, но делает свою работу. Выложу код в отдельный ответ, для ленивых копипастов вроде меня :) - person codrut; 15.03.2016

Другой способ отслеживать ход загрузки с меньшим контролем — наблюдать за уведомлениями WebViewProgressEstimateChangedNotification, WebViewProgressFinishedNotification и WebViewProgressStartedNotification. Например, вы можете наблюдать за этими уведомлениями, чтобы реализовать в своем приложении простой индикатор хода выполнения. Вы обновляете индикатор выполнения, вызывая метод AssessmentProgress, чтобы получить оценку объема загруженного в данный момент контента.

с http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html

person KarenAnne    schedule 30.08.2012

Вы также можете использовать эту короткую категорию, которую я написал, которая добавляет блоки в UIWebView, а также позволяет вам выбирать между поведением UIWebView по умолчанию (получение уведомления после загрузки каждого объекта) или «ожидаемым» поведением (получение уведомления только тогда, когда страница полностью загружена).

https://github.com/freak4pc/UIWebView-Blocks

person Shai Mishali    schedule 02.01.2013
comment
У меня это не сработало, взорвав сигнал (я полагаю, SIGTRAP). Тем не менее, я прочитал код и использовал его логику на своем, и он работал нормально. - person Pedro Rolo; 11.01.2013
comment
Странно, у меня работает нормально, не уверен, что именно вы использовали, но не стесняйтесь писать здесь, если хотите. Ваше здоровье. - person Shai Mishali; 12.01.2013
comment
Если есть какие-либо проблемы, пожалуйста, не стесняйтесь вытащить свою копию и добавить исправление :) - person Shai Mishali; 14.01.2013

Мне нужно было захватить переменную со страницы, которая еще не была полностью загружена.

Это сработало для меня:

- (void) webViewDidFinishLoad:(UIWebView *)webView {
    NSString *valueID = [webView stringByEvaluatingJavaScriptFromString:@"document.valueID;"];

    if([valueID isEqualToString:@""]){
        [webView reload];
        return;
    }

    //page loaded
}
person Petr    schedule 30.01.2014

Все варианты действительно не работали для моего варианта использования. Я использовал слегка измененный пример Phopkins. Я обнаружил, что если HTML, загруженный в веб-представление, содержит изображение, это будет отдельный запрос, который выполняется последовательно, поэтому мы должны учитывать это, и я сделал это с помощью таймера. Не лучшее решение, но, похоже, оно работает.:

- (void)webViewActuallyFinished {
     _webViewLoads--;

    if (_webViewLoads > 0) {
        return;
    }

    //Actually done loading

}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    _webViewLoads++;
}


- (void)webViewDidFinishLoad:(UIWebView *)webView {


    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(webViewActuallyFinished) userInfo:nil repeats:NO];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
  _webViewLoads--;
}
person David Corrado    schedule 13.02.2014
comment
Это сработало для меня. Я попытался использовать javascript readyState, и это не сработало. Мне не нравится факт использования таймера для этого, но он работает. В этом не было бы необходимости, если бы didFinishLoad webView на самом деле был didFinishLoad. - person Jacob Boyd; 11.01.2017

привет это может быть уже немного далеко назад, но я надеюсь, что это поможет.

я просто использовал уведомление, когда оно входит в метод webViewDidFinishLoad, чтобы я мог захватить один экземпляр метода, а затем обнаружу уведомление и оттуда выполню свою логику.

надеюсь это поможет. он не захватывает последний вызванный экземпляр метода webViewDidFinishLoad, но позволяет вам делать что-то после его вызова и не повторять вызовы этого конкретного метода (например, показ кнопки).

*********ИЗМЕНИТЬ********

я сделал тест и удалось проверить его. это действительно работает, и метод, вызываемый из уведомления, будет вызываться после загрузки всей страницы.

в качестве альтернативы, я думаю, вы можете сделать счетчик для метода делегата webViewDidStartLoad, а также для webViewDidFinishLoad, чтобы убедиться, что они одинаковы, прежде чем запускать свои коды. это, тем не менее, уродливый хак, поскольку мы никогда не узнаем, сколько раз он будет вызываться, если, как и я, вы не загружаете html-канал и не можете добавить JavaScript, чтобы проверить, сколько элементов есть на странице, которую вы загружаете. я просто делюсь некоторыми методами, которые я пытался решить. Надеюсь, поможет.

обратная связь приветствуется. Благодарность!

person faterpig    schedule 09.05.2012
comment
В чем разница между размещением кода в webViewDidFinishLoad и созданием уведомления в webViewDidFinishLoad для вызова другого метода? - person Aaron Brager; 08.03.2013
comment
зависит от того, что ваш код делает на самом деле. уведомление запускает еще один набор кодов, поэтому, если у вас уже есть метод или что-то, что требуется для запуска отдельно или одновременно, то я думаю, что тогда было бы лучше вызвать другой метод. если нет, это действительно зависит от того, что делает ваш код, я думаю. - person faterpig; 13.08.2013

Вот что я использую, чтобы показать счетчик во время загрузки DOM, построенный на основе ответа @Vinod.

Обратите внимание, что между webViewDidStartLoad и webViewDidFinishLoad состояние readyState завершено из предыдущего запроса (если он был), поэтому опрос должен начаться после webViewDidFinishLoad.

Возможные значения readyState: loading, interactive или completed (а может быть, nil ?)

- (void)webViewDidStartLoad:(UIWebView *)webView {
    [self spinnerOn];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [self startDOMCompletionPolling];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    [self startDOMCompletionPolling];
}


- (void)startDOMCompletionPolling {
    if (self.domCompletionListener) {
        [self.domCompletionListener invalidate];
    }
    self.domCompletionListener = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkDOMCompletion) userInfo:nil repeats:YES];
}

- (void)checkDOMCompletion {
    NSString *readyState = [self.webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];

    if (readyState != nil) {
        if (readyState.length > 0) {
            if ([readyState isEqualToString:@"loading"]) { //keep polling
                return;
            }
        }
    }
    //'completed', 'interactive', nil, others -> hide spinner
    [self.domCompletionListener invalidate];
    [self spinnerOff];
}
person codrut    schedule 15.03.2016

Я делаю это следующим образом:

- (void)webViewDidFinishLoad:(UIWebView *)webview  {
    if (webview.loading)
    return;
    // now really done loading code goes next
    [logic]
}
person KeithF    schedule 21.06.2009
comment
Это определенно не работает. Это ложно каждый раз, когда вызывается webViewDidFinishLoad. Но тогда он все еще вызывается несколько раз. Да, 2 старта и ТРИ финиша. Пример: Начальная загрузка — статья 23, причины для выхода 1, Начальная загрузка — статья 23, причины для выхода 1, конечная загрузка — статья 23, причины для выхода 0, конечная загрузка — статья 23, причины для получения Out There 0, Finish Load -- статья 23 Причины выйти 0 - person christophercotton; 24.02.2011