怎样计算带有UIWebView的Cell的高度?

2013-12-05 21:11:47 +08:00
 11
UIWebView在每个cell中的内容都不同,在 tableView:cellForRowAtIndexPath: 中 alloc 的,然后需要在 heightForRowAtIndexPath: 中计算高度。

可是问题是,在创建这个cell之前没法知道UIWebView的高度呀,可是 heightForRowAtIndexPath 是需要在这之前提供高度的。也就说调用 UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; 会出错。


所以应该怎么办?如果在 heightForRowAtIndexPath 中 alloc 一个 UIWebView 再读取 htmlString 得到 height 这样速度会太慢了吧,而且会做两边同样的工作。。

求解。
6098 次点击
所在节点    iDev
16 条回复
ultragtx
2013-12-05 21:34:43 +08:00
一般tableview用label都嫌拖动的时候卡 用webview估计你早晚要改实现方式
如果硬要这么整那只有你说这种 反正要先算出来 或者是你先给个大概的高度 然后等webview加载完了再更新
11
2013-12-05 21:51:41 +08:00
@ultragtx 但是如果做图文混排的那种,比如论坛客户端什么的,用UIWebView貌似是最理想的选择了吧。 用 CoreText 太折磨人了。。
xuan_lengyue
2013-12-05 22:15:11 +08:00
@11 为什么不整体用UIWebView实现呢,UITableView 里面嵌 UIWebView 这个会很有问题。
harrymoo
2013-12-05 22:38:02 +08:00
1. heightForRowAtIndexPath:中返回你的uiwebview的height;
2. uiwebview加载完后,在webViewDidFinishLoad:中计算出实际高度,调用:
[self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView beginUpdates];
[self.tableView endUpdates];
完毕。
11
2013-12-05 23:25:17 +08:00
@harrymoo 所以在 1 中,这里的 height 是随便设的一个值么?不是真正的 height。
chisj
2013-12-06 09:28:21 +08:00
@11 不是的,这个就是cell的height,只是你的webView高度在内容load前和load后会变化,所以需要重新设置并reload tableView。
so898
2013-12-06 13:55:41 +08:00
我曾经做过楼主你正在做的事情……

首先,UITableView的heightForRowAtIndexPath:函数是在主线程中运行的,而且必须在reload的时候提供一个可以使用的值
然后,UIWebView的高都计算需要在Web载入完成之后通过JS或者contentViewSize来计算,这个部分中Web载入是在独立的WebCore线程中进行的,载入完成之后通过Delegate回掉通知主线程

所以说,要做的话就是得在heightForRowAtIndexPath:提供一个大概的高度值,然后在UIWebView载入完成算完高度之后refresh指定的Cell
但这个实现有个问题,就是Cell不太好复用,而且后续Cell高度计算会导致所有内容全部被加载到UIWebView里面,在面对微博之类的内容的时候会导致内存占用过高
如果要复用的话,就得建立很好的NSURLCache来缓存主要页面内容,然后祈祷UIWebView快点加载……

对了,这里还要说一下注意的点,就是UIWebView的webViewDidFinishLoad:会被多次调用的,这里要做好处理
11
2013-12-06 17:03:11 +08:00
@so898 所以呢。。后来你还是用这个方案么?我昨天试了 @harrymoo 说的,然后果然就在 webViewDidFinishLoad: 这里卡死了。。这里应该怎么处理?我是用 superview 得到的 cell,然后得到 indexPath,再用的 [self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone] 。就卡死了。。求个 example。 @chisj
harrymoo
2013-12-06 19:06:03 +08:00
@11 怎么个卡死? 其实@so898也已经提到了,你不应直接在 webViewDidFinishLoad:里面去refresh tableview,因为是不同的线程,建议Post一个Notification到界面主线程处理。
chisj
2013-12-09 20:14:43 +08:00
@11 因为没有看到你的代码,我猜测下看能不能帮你提供一点思路:你的程序卡死的原因可能是死循环?在webViewDidFinishLoad: 你调用了 [self.tableView reloadRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone],然后在UITableView Datasource的 tableView:cellForRowAtIndexPath:里,你返回的cell里又重新init或者load了webView,导致了死循环?

是否可以试试在viewDidLoad里就初始化好UIWebView,然后tableView:cellForRowAtIndexPath:里add到cell的contentView里。

这样至少不会死循环导致你程序“卡死”。
11
2013-12-09 22:04:09 +08:00
@chisj Exactly! 如果在 viewDidLoad 里初始化 UIWebView 的话,也需要得到 cell 才能进行初始化?因为 UIWebView 是在每个 cell 中的。

我觉得我现在的思路有问题,麻烦请教一下那些图文混排的微博客户端什么的都是怎么做 cell?
chisj
2013-12-09 23:30:41 +08:00
@11 图文混排的微博现在好像都不用UIWebView了,毕竟性能上不好,应该都是使用CoreText了,参考唐巧的博客: http://blog.devtang.com/blog/2013/10/21/the-tech-detail-of-ape-client-3/ 。你说的“得到cell才能进行初始化”我没有理解是什么意思啊~~~
BB9z
2013-12-11 09:54:36 +08:00
如果 WebView 在顶部的话建议方到 table 的 header view 里。更新高度的话 headerView = headerView 就可以。
dazuiba
2013-12-24 21:41:36 +08:00
如果楼主你实在想这么干,我这里有个方案:

UIWebView load完毕后,执行一段JS,拿到document的Height.再通过WebView的Delegate传递给Objective-C Code.

然后再传递给TableViewController,告诉它这个Cell的高度已经拿到了,待所有Cell的高度都返回后,TableViewController开始显示TableView.

貌似没别的好办法。
wujichao
2014-03-01 00:10:30 +08:00
楼主啊
富文本两个办法

coretext
自己实现起来要求较高
开源库有 一些 比如说 rtlabel rclabel TTTAttributedLabel FTCoreText SECoreTextView

webview
注意哦 不是每个cell一个webview, 一个webview就够你受得了, 会卡死你的

这里有一个绝佳的tableview里面加了两个webview的实践
https://github.com/gaosboy/iOSSF/

或者最好使用一个webview用html5搞得像原生的一样, 在复杂页面时效果最好
比如说 我现在做的一个论坛的客户端中的帖子页面
wujichao
2014-03-01 00:12:11 +08:00
另外
https://github.com/gaosboy/iOSSF/
里面的检测contentSize获得高是我见过最好最"原生"的方法

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/91996

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX