Fortschrittsbalken (und Netzwerkaktivitätsstatus) für UIWebView

Nach einer langen Pause habe ich wieder angefangen, ios zu entwickeln. Aus diesem Grund habe ich die neueste Version von XCode (5.02) heruntergeladen und mich daran gemacht, eine App mit ios7 als Ziel zu codieren. Alles lief gut, bis ich meiner App eine "WebView-Seite" hinzufügte. Ich habe naiv erwartet, dass UIWebView wie eine Seite in Safari funktioniert, aber das ist nicht der Fall. Das offensichtlichste Fehlen ist, dass es während des Ladens der Seite keinerlei Feedback für den Benutzer gibt. Das heißt, die Netzwerkaktivitätsanzeige wird überhaupt nicht angezeigt, und beim Laden der Seite wird kein Fortschrittsbalken angezeigt. Also habe ich mir gedacht, dass Sie diese nur manuell hinzufügen müssen. Das sollte doch nicht zu schwer sein, oder?

Einige Tage später und ich habe es ganz gut funktioniert, aber ich bin nicht 100% zufrieden mit dem Ergebnis. Es dauerte einiges an Debuggen und Experimentieren, um dahin zu gelangen, wo ich jetzt bin. Ich schätze, meine Frage lautet: "Gibt es irgendetwas, das getan werden kann, um meinen Ansatz zu verbessern?" und vielleicht auch: "Soll ich das Laden melden und Eigenschaftsprobleme an Apple anfordern und sie bitten, sie zu beheben"?

Anfangs habe ich im Internet nach Lösungen für dieses Problem gesucht, und es gibt einige Fragen zum Stackoverflow, aber anscheinend hat es niemand wirklich geknackt. Einige der vorgeschlagenen Ansätze:

Verwenden Sie eine private UIWebView-API (nicht gut, wenn Sie Ihre App für den Store freigeben möchten)Laden Sie die Seite mit NSURLConnection herunter (zeigt den Fortschritt nur für den Download der ersten Seite an)Verwenden Sie eine Drittanbieter-Bibliothek zum Herunterladen und Verarbeiten der Seite (Vorschlaghammer, um eine Nuss zu knacken?)Verwenden Sie ein UIWebViewDelegate und verfolgen Sie die Start- und Endpunkte des LadevorgangsVerwenden Sie ein UIWebViewDelegate, um die Netzwerkaktivitätsanzeige manuell ein- und auszublenden

Am Ende habe ich mich für 4. und 5. entschieden, aber es ist schwierig, weil:

Die Start- und Zielanrufe können in beliebiger Reihenfolge erfolgenEinige Anfragen beginnen, enden aber nieEinige Anfragen werden mit einem Fehler abgeschlossen

Das erste Problem kann gelöst werden, indem die Start- und Endpunkte gezählt werden, sichergestellt wird, dass sich der berechnete Prozentsatz des Fortschritts nur erhöht und die Netzwerkanzeige erst deaktiviert wird, wenn die letzte Anforderung abgeschlossen ist.

Das zweite Problem ist schwieriger zu lösen. Das UIWebView verfügt über zwei Eigenschaften, die im Prinzip dazu verwendet werden können (Laden und Anfordern), die jedoch auf mysteriöse Weise zu funktionieren scheinen. Einige Antworten und Kommentare, die ich hier gefunden habe, deuten darauf hin, dass die Ladeeigenschaft nie aktualisiert wird, die angeforderte jedoch. Für mich war dies zunächst sinnvoll, da das Laden von Seiten nie wirklich aufgehört hat (sie haben möglicherweise dynamischen Inhalt). Daher war es sinnvoll, dass die loading-Eigenschaft nur angibt, ob die Webansicht aktiv Inhalte lädt (dh ob loadRequest aufgerufen wurde und stopLoading hasn 't).

Zumindest auf ios7 funktioniert dies jedoch genau umgekehrt. Die Anforderungseigenschaft wird nie aktualisiert, daher können Sie mit der Anforderungseigenschaft nicht feststellen, ob die Ansicht das Laden aller Elemente abgeschlossen hat (d. H. Indem Sie prüfen, ob die abgeschlossene Anforderung die ursprüngliche Anforderung ist). Die Ladeeigenschaft wird jedoch aktualisiert und auf NO gesetzt, wenn die Seite vollständig geladen wurde. Dies ist sehr hilfreich, aber ich habe festgestellt, dass bei dynamischen Inhalten (z. B. Seitenanzeigen) das Laden auf "Ja" zurückgesetzt wird. Daher ist es immer noch etwas schwierig, damit umzugehen.

Das dritte Problem wäre kein Problem, es sei denn, ich habe festgestellt, dass in webView: didFailLoadWithError die Eigenschaft loading immer auf NO gesetzt ist. Dies scheint jedoch ein vorübergehender Zustand zu sein. Wenn Sie möchten, können Sie Fehler ignorieren und warten, bis der Ladevorgang in webViewDidFinishLoad auf NO gesetzt wird. Da die Anforderungseigenschaft nicht aktualisiert wird, ist es unmöglich zu wissen, ob die erste Anforderung fehlgeschlagen ist oder eine der Unteranforderungen (die Sie interessieren könnten oder nicht). Deshalb habe ich mich für eine Lösung entschieden, mit der der Programmierer das gewünschte Verhalten auswählen kann.

Es wäre einfacher, wenn:

Die Anforderungseigenschaft wurde aktualisiert (oder die aktuelle Anforderung wurde wie in webView übergeben: shouldStartLoadWithRequest: navigationType).Die loading-Eigenschaft wurde in webView richtig eingestellt: didFailLoadWithError

Hier ist mein Code im View-Controller für das 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];
    }
}

Antworten auf die Frage(0)

Ihre Antwort auf die Frage