考虑一个非常简单的 UITableView ,其中包含两种状态之一。
第一个州:
第二州:
在这两种情况下,每一行基本上都是四个可能的 UITableViewCell 对象中的一个,每个对象都包含自己的 UITextField 。我们甚至不需要重用或缓存,因为在这种情况下我们只处理四个已知单元。它们是在随附的XIB中创建的,因此我们已经将它们全部连接好并准备好了。
现在考虑我们要在两种状态之间切换。
听起来很容易。让我们假设我们的视图控制器的右侧栏按钮项提供切换支持。我们还将使用ivar和枚举来跟踪当前状态。
要明确一秒,这是一个人如何从状态1转到2.(假设我们也处理条形按钮项目的标题。)简而言之,我们要清除表格的页脚视图,然后插入第三和第四行。我们在更新块中批量处理,如下所示:
// Brute forced references to the third and fourth rows in section 0
NSUInteger row02[] = {0, 2};
NSUInteger row03[] = {0, 3};
[self.tableView beginUpdates];
state = tableStateTwo; // 'internal' iVar, not a property
self.tableView.tableFooterView = nil;
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:
[NSIndexPath indexPathWithIndexes:row02 length:2],
[NSIndexPath indexPathWithIndexes:row03 length:2], nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
反过来说,我们想要重新分配表格页脚视图(就像单元格一样,在XIB中准备就绪并等待),并删除最后两行:
// Use row02 and row03 from earlier snippet
[self.tableView beginUpdates];
state = tableStateOne;
self.tableView.tableFooterView = theTableFooterView;
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:
[NSIndexPath indexPathWithIndexes:row02 length:2],
[NSIndexPath indexPathWithIndexes:row03 length:2], nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
现在,当桌子要求行时,它非常干净。前两个细胞在两种情况下都是相同的。根据状态,只有最后两个出现/消失。当表视图询问诸如节中的行数,节中页眉/页脚的高度或节中页眉/页脚的视图之类的内容时,将查询状态ivar。
最后一点也是我遇到麻烦的地方。
使用上述逻辑,0部分的页眉/页脚不会消失。具体来说,页脚保持在插入行的下方,但标题现在覆盖最顶行。如果我们切换回状态1,则删除部分页脚,但部分标题仍然存在。
那么使用[self.tableView reloadData]
怎么样?当然,为什么不呢。根据Apple的建议,我们注意不要在更新块中使用它,只需在endUpdates
之后添加它。
这一次,好消息!第0节页眉/页脚消失。 :)
然而......
切换回状态导致最精致的混乱!第0节标题返回,只是再次覆盖第一行(而不是显示在它上面)。第0节的页脚位于最后一行的下方,但整个表格页脚 - 现在已恢复 - 覆盖了页面页脚。 Waaaaaah ......现在怎么样?
为了确保,让我们再次切换回状态二。是的,看起来很好。回到州一? Yecccch。
我也试过像其他一些特技一样使用reloadSections:withRowAnimation:
,但这只会让事情变得更糟。
NSRange range = {0, 1};
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
...
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
例证:如果我们在更新块结束之前调用reloadSections...
,则更改为状态2会隐藏视图中的前两行,即使它们原本占用的空间仍然存在。切换回状态1会将第0部分的页眉/页脚返回到正常状态,但前两行仍然不可见。
案例二:将reloadSections...
移至更新块之后但reloadData
之前导致所有行变为不可见! (我将该行称为不可见,因为在跟踪期间,tableView:cellForRowAtIndexPath:
为这些行返回真正的单元格对象。)
案例三:在reloadSections...
之后移动tableView:cellForRowAtIndexPath:
使我们更接近,但是当切换回状态1时,第0节页眉/页脚永远不会返回。
嗯。根据我在追踪时所看到的情况,使用reloadSections...
和 reloadData
可能会造成失礼,这会带来我们:
案例四:直接用reloadData
替换reloadSections...
。状态二中的所有细胞都消失了。状态1中的所有单元格也保持缺失(尽管保留空间)。
这个理论太多了。 :)
跟踪代码,单元格和视图对象以及区域高度,都是适合他们应该在适当的时间。他们只是没有表现得很清醒。 (更新:视图高度不一样,但我也没有更改它们!有关详细信息,请参阅我发布的答案。)
那么,如何破解这种情况呢?线索欢迎/赞赏!
答案 0 :(得分:1)
我今天遇到了同样的问题。
过了一会儿,我想到了一切都画得错误的想法。在表视图单元格方法的外观中,我删除了这样的条件子句,一切正常:
//if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//}
我将仔细研究这个问题,因为代码现在不再优化了。但它暂时有效。
答案 1 :(得分:0)
我把它放在答案部分,因为它有助于(部分?)解释我所看到的内容。它只是没有解释为什么。 :)
当我第一次将视图分配给表部分标题时(响应tableView:viewForHeaderInSection:
),它的设置就像我在XIB中定义它一样:
<UIView: 0x376620; frame = (0 0; 320 50); autoresize = RM+BM;
layer = <CALayer: 0x376720>>
在我们更改单元格并开始响应第0部分的nil为tableView:viewForHeaderInSection:
之后的某个时间,所述视图获得了一些 CABasicAnimation 的爱(毕竟表格即将动画) ...并且视图框架和autoresize参数更改!
<UIView: 0x376620; frame = (0 0; 320 10); autoresize = W;
animations = { position=<CABasicAnimation: 0x360b30>;
bounds=<CABasicAnimation: 0x360a30>; }; layer = <CALayer: 0x376720>>
如果我们再将BACK切换到第一个状态,并再次返回与节标题相同的视图,我们会在分配时看到一些碎片:
<UIView: 0x376620; frame = (0 0; 320 10); autoresize = W;
layer = <CALayer: 0x376720>>
是的。框架和自动调整大小不会重置!
因此,当从表的节头中有效删除视图时,我们似乎已经遇到了无意的副作用。
我的下意识反应:“如果你要弄乱我的UIView,请把东西放回你找到它们的方式!”在这一点上,我不知道这是否是一个现实的期望,但它是第一个想到的。
为了缓解,我想我每次都必须重置帧并自动调整大小。嗯......看起来有点乱,你想?也许有更好的方法,或者我在其他地方犯了一个失礼。
然后,此帧/自动调整大小调整似乎不会对整个表头/页脚视图造成问题,即使它们被删除并稍后重新添加。只是部分页眉/页脚视图。 (等等,更正:我无法判断它是否会影响表格页脚视图,因为它下方没有任何内容可以与之发生冲突。)
现在,我在每个页眉/页脚视图中嵌入了另一个视图。此视图在形式上与父视图相同,但在动画时不会更改。然后,对于受状态变化影响的每个页眉/页脚视图,只需调用类似于此的内容:
- (void)fixViewAnimationCruft:(UIView *)theView {
UIView *subview = [[theView subviews] objectAtIndex:0];
theView.frame = subview.frame;
theView.autoresizingMask = subview.autoresizingMask;
}
(不是最原始的方法名称,但现在可以使用。)