insertRowsAtIndexPaths: with scrollToRowAtIndexPath: 导致 UITableView 部分被错误地隐藏
Posted
技术标签:
【中文标题】insertRowsAtIndexPaths: with scrollToRowAtIndexPath: 导致 UITableView 部分被错误地隐藏【英文标题】:insertRowsAtIndexPaths: with scrollToRowAtIndexPath: causes UITableView sections to be incorrectly hidden 【发布时间】:2012-07-17 02:48:51 【问题描述】:我创建了一个带有可点击部分的 UITableview。当你点击它们时,
-
它们“扩展”以显示其中的单元格
单击的部分滚动到视图的顶部。
我计算所有索引路径以插入/删除必要的单元格,然后使用以下代码插入它们:
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:pathsToOpen withRowAnimation:insertAnimation];
[self.tableView deleteRowsAtIndexPaths:pathsToClose withRowAnimation:deleteAnimation];
[self.tableView endUpdates];
[self.tableView scrollToRowAtIndexPath:[pathsToOpen objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
只有一个问题 - 所选部分下方的部分是隐藏的。第一个屏幕截图显示了 tableview 的外观。第二个屏幕截图显示了它的实际外观。
如果您向上滚动(因此隐藏部分在屏幕外)然后向下滚动,隐藏部分将被带回(再次可见)。我对为什么会发生这种情况的猜测如下:
插入/删除动画与scrollToRowAtIndexPath
同时发生,这使TableView 感到困惑。如果我没有完成scrollToRowAtIndexPath
第 3 和第 4 节将在屏幕外 - 所以 tableView 不知何故仍然认为它们在屏幕外。 UITableview 隐藏了屏幕外的单元格/部分作为优化。如果我在 2 秒内使用 dispatch_after
调用 scrollToRowAtIndexPath
,则第 3 和第 4 部分将正确显示。
所以我想我知道为什么会这样,但我不知道如何修复/覆盖这个 UITableview 优化。实际上,如果我实现 scrollViewDidEndScrollingAnimation
然后在此函数中添加断点,应用程序会正确显示第 3 和第 4 部分(这就是我获得第一个屏幕截图的方式)。但是一旦继续这个功能,细胞就会消失。
完整项目可以下载here
其他实现细节:节是合法的 UITableView 节。我添加了一个触发对 tableview 的委托回调的 tapGestureRecognizer。下面是打开这些部分的整个方法。
- (void)sectionHeaderView:(SectionHeaderView *)sectionHeaderView sectionOpened:(NSInteger)sectionOpened
// Open
sectionHeaderView.numRows = DefaultNumRows;
sectionHeaderView.selected = YES;
NSMutableArray *pathsToOpen = [[NSMutableArray alloc] init];
for (int i = 0; i < sectionHeaderView.numRows; i++)
NSIndexPath *pathToOpen = [NSIndexPath indexPathForRow:i inSection:sectionOpened];
[pathsToOpen addObject:pathToOpen];
// Close
NSMutableArray *pathsToClose = [[NSMutableArray alloc] init];
if (openSectionHeader)
for (int i = 0; i < openSectionHeader.numRows; i++)
NSIndexPath *pathToClose = [NSIndexPath indexPathForRow:i inSection:openSectionHeader.section];
[pathsToClose addObject:pathToClose];
// Set Correct Animation if section's already open
UITableViewRowAnimation insertAnimation = UITableViewRowAnimationBottom;
UITableViewRowAnimation deleteAnimation = UITableViewRowAnimationTop;
if (!openSectionHeader || sectionOpened < openSectionHeader.section)
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
openSectionHeader.numRows = 0;
openSectionHeader.selected = NO;
openSectionHeader = sectionHeaderView;
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:pathsToOpen withRowAnimation:insertAnimation];
[self.tableView deleteRowsAtIndexPaths:pathsToClose withRowAnimation:deleteAnimation];
[self.tableView endUpdates];
[self.tableView scrollToRowAtIndexPath:[pathsToOpen objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
【问题讨论】:
我认为你最好使用Apple提供的MVC。您所要做的就是在数据源中添加/删除数据,并且不要触摸 tableview 行 请在问题中包含更多您的项目,而不是期待下载 - 例如,您如何计算添加/删除的索引路径,以及您的“Section X”事物部分标题还是它们只是单元格?如果它不那么依赖于下载您的项目,它将成为一个更好的独立问题。 我认为你的猜测也是正确的。您可以尝试在滚动结束委托方法中重新加载行吗? @jrturton 感谢您的反馈。我在问题中包含了更多的实现细节。我还将项目的链接制作为更易于下载的 zip。我实际上已经尝试过重新加载 tableview 并且没有任何帮助。 【参考方案1】:据我所知,返回已使用的剖面视图时会出现问题。而不是:
- (UIView *)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section
return [self.sectionHeaderViews objectAtIndex:section];
如果我每次都创建一个新视图,我没有问题:
- (UIView *)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section
SectionHeaderView *sectionHeaderView = [self.tableView dequeueReusableCellWithIdentifier:SectionHeaderView_NibName];
sectionHeaderView.textLabel.text = [NSString stringWithFormat:@"Section %d", section];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:sectionHeaderView action:@selector(handleTap:)];
[sectionHeaderView addGestureRecognizer:tapRecognizer];
sectionHeaderView.section = section;
sectionHeaderView.delegate = self;
return sectionHeaderView;
这可能是因为您使用[self.tableView dequeueReusableCellWithIdentifier:SectionHeaderView_NibName];
创建节标题并将它们保存在一个数组中,我不认为 UITableViewCell 是为此创建的,但我不确定。您可能需要考虑前面的 UITableViewCell 用于剖面视图,而是使用其他东西(可能是带有 UILabel 的 UIImageView)。或者您可以不将剖面视图存储在数组中......您当前设置代码的方式,您不需要数组并且创建新视图很简单,您无需担心它。
【讨论】:
【参考方案2】:@AaronHayman 的回答有效(IMO 的接受和赏金应该归他所有 - 这不适合评论!),但我会更进一步 - 你应该'根本不使用单元格作为节标题,并且您不应该使用出队机制来实质上加载笔尖。
部分标题视图不应该是单元格,如果使用它们代替常规视图,您可能会获得无法预料的效果,特别是如果它们被取消队列 - 当您这样做时,表格会保留这些可重用单元格的列表,并在它们离开屏幕时回收它们,但您的部分标题不可重复使用,每个部分都有一个。
在您的示例项目中,我将 SectionHeaderView 的超类更改为普通 UIView,并将您的 createSectionHeaderViews
方法更改为直接从那里的 nib 加载:
NSMutableArray *sectionHeaderViews = [[NSMutableArray alloc] init];
UINib *headerNib = [UINib nibWithNibName:SectionHeaderView_NibName bundle:nil];
for (int i = 0; i < 5; i++)
SectionHeaderView *sectionHeaderView = [[headerNib instantiateWithOwner:nil options:nil] objectAtIndex:0];
sectionHeaderView.textLabel.text = [NSString stringWithFormat:@"Section %d", i];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:sectionHeaderView action:@selector(handleTap:)];
[sectionHeaderView addGestureRecognizer:tapRecognizer];
sectionHeaderView.section = i;
sectionHeaderView.delegate = self;
[sectionHeaderViews addObject:sectionHeaderView];
self.sectionHeaderViews = sectionHeaderViews;
我还从您的 viewDidLoad 中注释掉了 register for reuse 行。这可以防止节标题消失。
【讨论】:
向他展示如何从 Nib 实例化视图的好主意。我通常在代码中创建所有视图,所以我什至没有想到这一点。以上是关于insertRowsAtIndexPaths: with scrollToRowAtIndexPath: 导致 UITableView 部分被错误地隐藏的主要内容,如果未能解决你的问题,请参考以下文章
insertRowsAtIndexPaths 自定义动画 Swift
insertRowsAtIndexPaths:没有 reloadRowsAtIndexPaths 就不会动画:
带有 2 个动画的 insertRowsAtIndexPaths
insertRowsAtIndexPaths: with scrollToRowAtIndexPath: 导致 UITableView 部分被错误地隐藏