Подтверждение поведения Javascript в UIWebView - PullRequest
6 голосов
/ 18 января 2011

Я пытаюсь выполнить Javascript для содержимого в UIWebView.Возможно, мои знания о среде выполнения Javascript отсутствуют, но я запутался в следующем примере. Подробности см. В исходном коде и комментариях:

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);

Подводя итог, можно ожидать, что глобальная переменная и функция JavascriptЯ создал, когда загружаю html, чтобы он был доступен для более поздних вызовов javascript. Удивительно, но это не так, и все элементы javascript должны быть добавлены позже. Это правильно?

спасибо за любую помощь заранее.

----- update ----- В этом случае я должен добавить, что self.view является WebView. Кроме того, я попытался поместить тег script в заголовок и текст документа HTML,без изменений в поведении.

Ответы [ 2 ]

3 голосов
/ 18 января 2011

mclin ответ верен в том, что UIWebView выполняет нагрузки в фоновом режиме.Если все, что вы загружаете, - это обычный html, и ни у одного тега нет содержимого типа src = "...", то вы можете быть вполне уверены, что webViewDidFinishLoad (метод протокола UIWebViewDelegate) будет вызываться только один раз, и что вы можете выполнить свой javascript в этом делегате.method.

Однако после загрузки исходного HTML-кода UIWebView начнет загружать любые изображения, javascript, аудио, видео и т. д., а webViewDidFinishLoad будет вызываться при завершении каждой из последующих загрузок.Я думаю, что если вы посмотрите на индикатор прогресса Safari на вашем iPhone, вы сможете увидеть это поведение.Индикатор выполнения идет примерно на полпути после начальной загрузки страницы, а затем идет постепенно по мере загрузки изображений или того, что вы загрузили.

Я попробовал несколько методов для запуска javascript послезагрузка страницы завершена.Проще всего сделать так, чтобы webViewDidFinishLoad через короткую задержку выполнил другой селектор и проверил свойство загрузки webView.Здесь вы рискуете тем, что мы объединяем «сроки» и «темы», но эй ... если вы хотите легко (то есть).

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

Более сложная техникаэто отслеживать нагрузки индивидуально и вести счетчик.Когда счетчик возвращается к 0, вы можете запустить свой JavaScript.В этом случае вы захотите реализовать метод делегата для

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

. Теперь вы можете иметь webViewDidStartLoad: increment outStandingLoads и иметь webViewDidFinishLoad: и webView: didFailLoadWithError: decment и checkSSignLoads.Я бы использовал атомарное свойство NSInteger для outStandingLoads по всему коду, чтобы учесть потенциальную возможность загрузки более одного потока.

Я думаю, что любой из методов будет работать достаточно хорошо.Я выбрал последний подход, но только потому, что мне нужно было решить другие проблемы в вызовах webViewDelegate.Я решил расширить UIWebView и сделать его своим собственным делегатом, где мое расширение реализовало методы делегата.Затем я использую модель NSNotification для публикации уведомлений, таких как «WebPageLoadBegan» и «WebPageLoadComplete», для связи с моим кодом пользовательского интерфейса.Мне показалось, что это более простая модель при работе с двумя различными наборами кода пользовательского интерфейса ... iPad и iPhone.

Надеюсь, это поможет.

1 голос
/ 18 января 2011

Ваш UIWebView не завершил загрузку.

Когда вы вызываете [self.webView loadHTMLString:html baseURL:nil];, он не загружается сразу.Он запускает поток, который выполняет это асинхронно, поэтому он не завершает загрузку при оценке вашего JS.

Сделайте ваш контроллер представления делегатом UIWebView, а затем оцените свой JS в -(void)webViewDidFinishLoad:(UIWebView *)webView

...