Pasek postępu (i stan aktywności sieci) dla UIWebView

Po długiej przerwie zacząłem ponownie rozwijać program ios. Mając to na uwadze, pobrałem najnowszą wersję XCode (5.02) i przystąpiłem do kodowania aplikacji z ios7 jako celem. Wszystko szło dobrze, dopóki nie dodałem strony „webView” do mojej aplikacji. Naiwnie oczekiwałem, że UIWebView będzie działał tak samo jak strona w Safari, ale tak nie jest. Najbardziej oczywistą rzeczą, której brakuje, jest to, że użytkownik nie ma absolutnie żadnych informacji zwrotnych podczas ładowania strony. tj. wskaźnik aktywności sieci nie pojawia się w ogóle i nie ma paska postępu wyświetlanego podczas ładowania strony. Pomyślałem, że musisz je dodać ręcznie. To nie powinno być zbyt trudne, prawda?

Kilka dni później mam to całkiem dobrze, ale nie jestem w 100% zadowolony z wyniku. Trwało to trochę debugowania i eksperymentowania, aby uzyskać to, gdzie jestem teraz. Myślę, że moje pytanie brzmi: „Czy można coś zrobić, aby poprawić moje podejście?” a także, być może, „Czy powinienem zgłosić problemy z ładowaniem i żądaniem do Apple i poprosić ich o ich naprawienie”?

Początkowo szukałem w Internecie rozwiązań tego problemu i jest sporo pytań na temat stackoverflow na ten temat, ale wygląda na to, że nikt go naprawdę nie złamał. Niektóre z sugerowanych podejść:

Użyj prywatnego interfejsu API UIWebView (nie ma sensu, jeśli chcesz udostępnić swoją aplikację w sklepie)Pobierz stronę za pomocą NSURLConnection (daje postęp tylko w przypadku pobierania pierwszej strony)Użyj biblioteki innej firmy do pobierania i przetwarzania strony (młotek, aby złamać nakrętkę?)Użyj UIWebViewDelegate i śledź ładowanie i kończy ładowanieUżyj UIWebViewDelegate, aby ręcznie wyświetlić i ukryć wskaźnik aktywności sieci

W końcu zdecydowałem się użyć 4. i 5., ale to trudne, ponieważ:

Połączenia początkowe i końcowe mogą mieć dowolną kolejnośćNiektóre żądania zaczynają się, ale nigdy nie kończąNiektóre żądania zawierają błąd

Pierwszy problem można rozwiązać, licząc rozruchy i kończąc, upewniając się, że obliczony procent postępu wzrasta tylko i wyłączając wskaźnik sieci tylko po zakończeniu ostatniego żądania.

Drugi problem jest trudniejszy do rozwiązania. UIWebView ma dwie właściwości, które w zasadzie mogą być użyte do tego (ładowanie i żądanie), ale wydają się działać w tajemniczy sposób. Niektóre odpowiedzi i komentarze, które znalazłem tutaj, sugerują, że właściwość ładowania nigdy nie jest aktualizowana, ale żądanie jest jedno. To początkowo miało dla mnie sens, ponieważ strony nigdy tak naprawdę nie przestają się ładować (mogą mieć zawartość dynamiczną), więc sensowne było, że właściwość ładowania powie tylko, czy widok WWW aktywnie ładuje zawartość (tj. Czy wywołano loadRequest i zatrzymano ładowanie hasn t).

Jednak przynajmniej na ios7 działa w odwrotny sposób. Właściwość request nigdy nie jest aktualizowana, więc nie można użyć właściwości request do ustalenia, czy widok zakończył ładowanie wszystkiego (tj. Sprawdzając, czy zakończone żądanie jest początkowym żądaniem). Właściwość ładowania jest aktualizowana, ale zostaje ustawiona na NIE, gdy strona jest w pełni załadowana. To bardzo pomaga, ale zauważyłem, że dynamiczna treść (np. Reklamy na stronie) powoduje, że ładowanie jest resetowane z powrotem do TAK, więc nadal jest to trochę trudne.

Trzeci problem nie byłby problemem, ale zauważyłem, że wewnątrz webView: didFailLoadWithError właściwość ładowania jest zawsze ustawiona na NO. Jednak wydaje się, że jest to stan tymczasowy i jeśli chcesz, możesz zignorować błędy i poczekać, aż załadowanie zostanie ustawione na NO w webViewDidFinishLoad. Ponieważ właściwość żądania nie jest aktualizowana, nie można stwierdzić, czy jest to pierwotna prośba, która się nie powiodła, czy jedno z podprób ((na które lub nie możesz się zwrócić). Postanowiłem więc skorzystać z rozwiązania, które pozwala programiście wybrać pożądane zachowanie.

Byłoby łatwiej, gdyby:

właściwość request została zaktualizowana (lub bieżące żądanie zostało przekazane, tak jak w webView: shouldStartLoadWithRequest: navigationType)właściwość ładowania została poprawnie ustawiona w webView: didFailLoadWithError

Oto mój kod w kontrolerze widoku dla webView:

- (void)resetProgress {
    framesToLoad = 0;
    framesLoaded = 0;
    currentProgress = 0.0f;
}

- (void)resetForNewPage {
    // Reset progress
    [self resetProgress];

    // Monitor the page load
    monitorProgress = YES;

    // Keep going if errors occur
    // completeIfError = NO;

    // Stop updates if an error occurs
    completeIfError = YES;
}

- (void)webViewLoaded {
    [self resetProgress];
    [entryProgressView setProgress: 0.0f animated: YES];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Reset state for new page load
    [self resetForNewPage];

    // Load the page
    NSURLRequest *entryRequest = [NSURLRequest requestWithURL:entryURL];
    [entryWebView loadRequest:entryRequest];
}

-(void)viewWillDisappear:(BOOL)animated {
    [entryWebView stopLoading];

    entryWebView.delegate = nil;
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        // Reset state for new page load
        [self resetForNewPage];
    }

    return YES;
}

-(void)webViewDidStartLoad:(UIWebView *)webView {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    if (!monitorProgress) {
        return;
    }

    // Increment frames to load counter
    framesToLoad++;
}

-(void)webViewDidFinishLoad:(UIWebView *) webView {
    if (!monitorProgress) {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        return;
    }

    // Increment frames loaded counter
    framesLoaded++;

    // Update progress display
    float newProgress = ((float) framesLoaded) / framesToLoad;
    if (newProgress > currentProgress) {
        currentProgress = newProgress;
        [entryProgressView setProgress: newProgress animated: YES];
    }

    // Finish progress updates if loading is complete
    if (!webView.loading) {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        monitorProgress = NO;

        [entryProgressView setProgress: 1.0 animated: YES];
        [self performSelector:@selector(webViewLoaded) withObject: nil afterDelay: 1.0];
    }
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    if (!monitorProgress) {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        return;
    }

    // Increment frames loaded counter
    framesLoaded++;

    // Update progress display
    float newProgress = ((float) framesLoaded) / framesToLoad;
    if (newProgress > currentProgress) {
        currentProgress = newProgress;
        [entryProgressView setProgress: newProgress animated: YES];
    }

    // Finish progress updates if required
    if (completeIfError) {
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        monitorProgress = NO;

        [entryProgressView setProgress: 1.0 animated: YES];
        [self performSelector:@selector(webViewLoaded) withObject: nil afterDelay: 1.0];
    }
}

questionAnswers(0)

yourAnswerToTheQuestion