2011-01-18 11 views
6

Estoy intentando ejecutar algo de Javascript en el contenido dentro de un UIWebView. Tal vez mi conocimiento del tiempo de ejecución de Javascript es deficiente, pero yo "m confundido sobre el siguiente ejemplo Ver código fuente y comentarios para más detalles:.Confirmación del comportamiento de Javascript en un UIWebView

NSString *html = [NSString stringWithFormat:@"<html><head><script type='text/javascript'>var content='the initial content';function myFunc(){return 'value of content: ' + content;}myFunc();</script></head><body>Hello blank</body></html>"]; 
// I would expect after this call that the variable content exists, as well as the function myFunc() 
[self.webView loadHTMLString:html baseURL:nil]; 
// However, it appears not to. The following call returns nothing 
NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:@"myFunc();"]; 
NSLog(@"result: %@", result); 

// Inserting the Javascript at this point seems to work as expected 
result = [self.webView stringByEvaluatingJavaScriptFromString:@"var content='the new content';function myFunc(){return 'value of content: ' + content;}myFunc();"]; 
NSLog(@"result: %@", result); 

// And calling myFunc() at this point is successful 
result = [self.webView stringByEvaluatingJavaScriptFromString:@"myFunc();"]; 
NSLog(@"result: %@", result); 

En resumen, yo esperaría que la variable global Javascript y la función que crearon . cuando cargo el html esté disponible para más tarde llamadas JavaScript Sorprendentemente, parece que no, y todos Javascript cosas hay que añadir más tarde, ¿es así

gracias por cualquier ayuda de antemano

.?. - --- actualización ----- Debo añadir que self.view es una WebView en este caso. Además, tengo t ried poner la etiqueta del script en la cabeza y el cuerpo del documento HTML, sin ningún cambio en el comportamiento.

+0

Eso me sorprende también. Tengo que preguntar (sin relación con la pregunta), ¿por qué llamas myFunc() después de declararlo? – donkim

+0

Ahh, eso fue solo para probar si pasaba algo. No debería estar allí. Sacarlo no hace nada. –

Respuesta

3

de McLin es correcto en que UIWebView realiza las cargas en el fondo. Si todo lo que está cargando es html simple y ninguna etiqueta tiene contenido de tipo src = "...", entonces puede estar razonablemente seguro de que se invocará webViewDidFinishLoad (método de protocolo UIWebViewDelegate) una sola vez y que puede realizar su javascript en ese delegado método.

Sin embargo, después de cargar el código HTML inicial, se cargará cualquier imagen, javascript, audio, video, etc. y webViewDidFinishLoad cuando se complete cada una de esas cargas. Creo que si miras la barra de progreso en Safari en tu iPhone, puedes ver este comportamiento. La barra de progreso va aproximadamente a la mitad después de que se cargue la página inicial y luego avanza poco a poco como las imágenes o lo que tiene cargado.

He intentado un par de técnicas para ejecutar javascript después de que se complete la carga de la página. Lo más fácil es hacer que webViewDidFinishLoad realice otro selector después de un breve retraso y verificar la propiedad de carga de webView. Estás arriesgándote un poco aquí porque combinamos el "timing" y los "hilos", pero oye ... si quieres fácil (eso es).

-(void) checkIfLoadDone:(UIWebView *) webView { 
    if (webView.loading) { return; } 
    // run your javascript here 
} 

-(void) webViewDidFinishLoad:(UIWebView *) webView { 
    [NSObject cancelPreviousPerformRequestsWithTarget:self 
              selector:@selector(checkIfLoadDone:) 
              object:webView]; 
    [self performSelector:@selector(checkIfLoadDone:) 
      withObject:webView 
      afterDelay:0.5]; 
} 

-(void) webViewDidStartLoad:(UIWebView *)webView { 
    [NSObject cancelPreviousPerformRequestsWithTarget:self 
              selector:@selector(checkIfLoadDone:) 
              object:webView]; 
} 

- (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 
    // ooops ... probably should deal with the error, but lets just run checkIfLoadDone 
    [NSObject cancelPreviousPerformRequestsWithTarget:self 
              selector:@selector(checkIfLoadDone:) 
              object:webView]; 
    [self performSelector:@selector(checkIfLoadDone:) 
      withObject:webView 
      afterDelay:0.5]; 
} 

Una técnica más compleja es rastrear las cargas individualmente y mantener un contador. Cuando el contador vuelve a 0, puede ejecutar su javascript. En ese caso, usted querrá poner en práctica el método delegado de

- (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { 
    if (navigationType != UIWebViewNavigationTypeOther) { 
    self.outStandingLoads = 0; 
    } 
    return YES; 
} 

Ahora usted puede tener webViewDidStartLoad: outStandingLoads de incremento y tener webViewDidFinishLoad: y web View: didFailLoadWithError: disminución y comprobar outStandingLoads. Utilizaría una propiedad NSInteger atómica para outStandingLoads en todo el código para abordar el potencial de que haya más de un hilo cargando.

Creo que cualquier método funcionará razonablemente bien. Elegí este último enfoque, pero solo porque necesitaba abordar otros problemas en las llamadas webViewDelegate. Decidí extender UIWebView y convertirlo en su propio delegado, donde mi extensión implementó los métodos de delegado.Luego aprovecho el modelo NSNotification para publicar notificaciones como "WebPageLoadBegan" y "WebPageLoadComplete" para comunicarme con mi código de interfaz de usuario. Parecía un modelo más fácil para mí cuando trabajaba con dos juegos diferentes de códigos de UI ... el iPad y el iPhone.

Espero que esto ayude.

+0

Esta es probablemente la mejor descripción del a menudo misterioso proceso de carga de webView. La API es enorme, sí, pero la documentación de Apple es desagradablemente espartana/solo se enfoca en cosas que "quieren que hagas". Dicho esto ... En función de su respuesta, ¿no sería posible simplemente mirar la 'WebViewProgressFinishedNotification' para una respuesta "Sí, estoy seguro" de "¿está cargado este m-fer?" .. aka, ¿puedo? FINALMENTE, ejecute este script, ¿es confiable? –

1

Su UIWebView no se ha terminado de cargar.

Cuando llama al [self.webView loadHTMLString:html baseURL:nil]; no lo carga inmediatamente. Inicia un hilo para hacerlo de forma asíncrona, por lo que no se carga cuando está evaluando su JS.

Hacer delegado su controlador de vista de la UIWebView y luego evaluar sus JS en respuesta -(void)webViewDidFinishLoad:(UIWebView *)webView

+0

Interesante, eso tiene sentido. Lo intentaré mañana. –

Cuestiones relacionadas