【问题标题】:Confirmation of Javascript behavior in a UIWebView确认 UIWebView 中的 Javascript 行为
【发布时间】:2011-01-18 05:19:53
【问题描述】:

我正在尝试对 UIWebView 中的内容执行一些 Javascript。也许我对 Javascript 运行时的了解不够,但是我对以下示例感到困惑。有关详细信息,请参阅源代码和 cmets:

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

总之,我希望我在加载 html 时创建的 Javascript 全局变量和函数可用于以后的 javascript 调用。令人惊讶的是,它似乎没有,所有的 javascript 东西都必须在以后添加。这是正确的吗?

提前感谢您的帮助。

----- 更新 ----- 在这种情况下,我应该添加 self.view 是一个 WebView。另外,我尝试将脚本标记放在 HTML 文档的头部和正文中,而行为没有改变。

【问题讨论】:

  • 这也让我感到惊讶。我不得不问(与问题无关),为什么在声明后调用myFunc()?
  • 啊,这只是为了测试是否发生了什么。它不应该在那里。把它拿出来没有任何作用。

标签: javascript iphone cocoa-touch uikit uiwebview


【解决方案1】:

mclin 的回答是正确的,因为 UIWebView 在后台执行加载。如果您加载的只是纯 html 并且没有标签具有 src="..." 类型内容,那么您可以合理地确定 webViewDidFinishLoad (UIWebViewDelegate 协议方法)只会被调用一次,并且您可以在该委托中执行您的 javascript方法。

但是,UIWebView 将在加载初始 HTML 后开始加载任何图像、javascript、音频、视频等,并且 webViewDidFinishLoad 将在每个后续加载完成时被调用。我认为如果您在 iPhone 上观看 Safari 上的进度条,您可能会看到这种行为。进度条在初始页面加载后大约一半移动,然后随着图像或加载的内容一次一点移动。

我尝试了几种技术来在页面加载完成后运行 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: 增加 outStandingLoads 并让 webViewDidFinishLoad: 和 webView:didFailLoadWithError: 减少并检查 outStandingLoads。我会在整个代码中为 outStandingLoads 使用原子 NSInteger 属性,以解决可能存在多个线程进行加载的问题。

我认为这两种方法都可以很好地工作。我选择了后一种方法,但这只是因为我需要解决 webViewDelegate 调用中的其他问题。我决定扩展 UIWebView 并使其成为自己的委托,我的扩展实现了委托方法。然后,我利用 NSNotification 模型发布诸如“WebPageLoadBegan”和“WebPageLoadComplete”之类的通知,以与我的用户界面代码进行通信。当使用两组不同的 UI 代码时,这对我来说似乎是一个更简单的模型……iPad 和 iPhone。

我希望这会有所帮助。

【讨论】:

  • 这可能是对经常神秘的 = webView 加载过程的最好描述。 API 是巨大的,是的,但 Apple 的文档非常简陋/只关注他们“希望你做的”事情。就是说...根据您的回答-不可能只看WebViewProgressFinishedNotification 来回答“是的,我确定”回答“这是“加载m-fer 吗?”.. aka,我可以吗?最后实际运行这个脚本 -- 可靠吗?
【解决方案2】:

你的 UIWebView 没有完成加载。

当您调用 [self.webView loadHTMLString:html baseURL:nil]; 时,它不会立即加载它。它会启动一个线程来异步执行此操作,因此在评估 JS 时它不会完成加载。

让您的视图控制器成为 UIWebView 的委托,然后在 -(void)webViewDidFinishLoad:(UIWebView *)webView 中评估您的 JS

【讨论】:

  • 有趣,说得通。明天试试。
猜你喜欢
  • 2013-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多