Barra de progreso (y estado de actividad de la red) para UIWebView

Después de un largo descanso, comencé a hacer el desarrollo de iOS de nuevo. Con eso en mente, descargué la última versión de XCode (5.02) y comencé a codificar una aplicación con ios7 como objetivo. Todo iba bien hasta que agregué una "página webView" a mi aplicación. Esperaba ingenuamente que UIWebView funcionara como una página en Safari, pero no lo hace. Lo más obvio que falta es que no haya ningún comentario para el usuario mientras la página se está cargando. es decir, el indicador de actividad de la red no aparece en absoluto y no se muestra ninguna barra de progreso a medida que se carga la página. Así que pensé que solo tenías que agregarlos manualmente. Eso no debería ser demasiado difícil, ¿verdad?

Varios días después, funcionó bastante bien, pero no estoy 100% satisfecho con el resultado. Tomó un poco de depuración y experimentación para llegar a donde estoy ahora. Supongo que mi pregunta es "¿Se puede hacer algo para mejorar mi enfoque?" y, también, quizás, "¿Debo informar sobre la carga y solicitar problemas de propiedad a Apple y pedirles que los corrijan"?

Inicialmente busqué en Internet soluciones para este problema y hay bastantes preguntas sobre este tema, pero parece que nadie lo ha resquebrajado. Algunos de los enfoques sugeridos:

Use una API privada de UIWebView (no es bueno si quiere lanzar su aplicación a la tienda)Descargue la página utilizando NSURLConnection (da progreso solo para la descarga de la página inicial)Utilice una biblioteca de terceros para descargar y procesar la página (martillo para romper una tuerca?)Use un UIWebViewDelegate y realice un seguimiento de los inicios y finalizaciones de la carga.Use un UIWebViewDelegate para mostrar y ocultar manualmente el indicador de actividad de la red

Al final decidí usar 4. y 5. pero es complicado porque:

Las llamadas de inicio y final pueden venir en cualquier orden.Algunas solicitudes comienzan pero nunca terminanAlgunas solicitudes completan con un error

El primer problema se puede resolver contando los inicios y finales, asegurándose de que el porcentaje de progreso calculado solo aumenta y apagando el indicador de red solo cuando se completa la última solicitud.

El segundo problema es más difícil de resolver. El UIWebView tiene dos propiedades que, en principio, se pueden usar para hacerlo (cargar y solicitar), pero parecen funcionar de maneras misteriosas. Algunas respuestas y comentarios que encontré aquí sugirieron que la propiedad de carga nunca se actualiza, pero sí la solicitud. Inicialmente, esto tenía sentido para mí porque las páginas nunca dejan de cargarse (pueden tener contenido dinámico), por lo que tenía sentido que la propiedad de carga solo le dijera si la vista web estaba cargando contenido activamente (es decir, si se ha llamado a loadRequest y stopLoading no ha 't).

Sin embargo, al menos en ios7, en realidad funciona de manera opuesta. La propiedad de solicitud nunca se actualiza, por lo que no puede utilizar la propiedad de solicitud para determinar si la vista ha terminado de cargar todo (es decir, verificando si la solicitud que ha finalizado es la solicitud inicial). Sin embargo, la propiedad de carga se actualiza y se establece en NO cuando la página se ha cargado completamente. Esto ayuda mucho, pero he notado que el contenido dinámico (por ejemplo, los anuncios de página) hace que la carga se restablezca a SÍ, por lo que aún es un poco difícil de manejar.

El tercer problema no sería un problema, excepto que observé que dentro de webView: didFailLoadWithError, la propiedad de carga siempre se establece en NO. Sin embargo, esto parece ser un estado temporal y, si lo desea, puede ignorar los errores y esperar a que la carga se establezca en NO en webViewDidFinishLoad. Dado que la propiedad de solicitud no está actualizada, es imposible saber si es la solicitud inicial la que ha fallado o una de las solicitudes secundarias (que puede o no preocuparse). Así que he decidido elegir una solución que le permita al programador elegir el comportamiento que desea.

Las cosas serían más fáciles si:

la propiedad de solicitud se actualizó (o la solicitud actual se pasó, como en webView: shouldStartLoadWithRequest: navigationType)la propiedad de carga se configuró correctamente en webView: didFailLoadWithError

Aquí está mi código dentro del controlador de vista para la vista web:

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

Respuestas a la pregunta(0)

Su respuesta a la pregunta