单元格显示在部分标题的顶部

12

我有一个带有自定义单元格和自定义标题的 UITableView。当我移动一个单元格以进行编辑时,它会突出显示在标题视图上方。 如何使标题视图始终位于所有单元格的顶部?

应用程序使用故事板(storyboard),如果这有所不同。

这是它的样子?https://www.dropbox.com/s/wg8oiar0d9oytux/iOS%20SimulatorScreenSnapz003.mov

这是我的代码:

[...]
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ListCell";
    ListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    int handleSection = [self sectionToHandle:indexPath.section];

    switch (handleSection)
    {
        case PrivateLists:
        {
            if (tableView.isEditing && (indexPath.row == self.privateLists.count))
            {
                cell.textField.text = NSLocalizedString(@"Lägg till ny lista", nil);
                cell.textField.enabled = NO;
                cell.textField.userInteractionEnabled = NO;
                cell.accessoryType = UITableViewCellAccessoryNone;
                cell.editingAccessoryView.hidden = YES;
            }
            else
            {
                List *list = [self.privateLists objectAtIndex:indexPath.row];
                cell.textField.text = list.name;
                cell.textField.enabled = YES;
                cell.textField.userInteractionEnabled =YES;
                cell.backgroundColor = [UIColor clearColor];

                cell.onTextEntered = ^(NSString* enteredString){
                    list.name = enteredString;
                    UpdateListService *service = [[UpdateListService alloc]initServiceWithList:list];
                    [service updatelistOnCompletion:
                     ^(BOOL success){
                         DLog(@"Updated list");

                         NSIndexPath *newPath = [NSIndexPath indexPathForRow:0 inSection:indexPath.section];
                         [self.tableView moveRowAtIndexPath:indexPath toIndexPath:newPath];

                         [self moveListToTop:list.ListId newIndexPath:newPath];
                         justMovedWithoutSectionUpdate = YES;
                     }
                                                    onError:
                     ^(NSError *error){
                         [[ActivityIndicator sharedInstance] hide];
                         [[ErrorHandler sharedInstance]handleError:error fromSender:self];
                     }];
                };
            }
        }
            break;
        default:
            return 0;
            break;
    }

    return cell;
}

-(UIView *)tableView:(UITableView *)aTableView viewForHeaderInSection:(NSInteger)section
{
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 22)];

    UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 300, 21)];
    [textLabel setFont:[[AXThemeManager sharedTheme]headerFontWithSize:15.0]];
    [textLabel setTextColor:[[AXThemeManager sharedTheme]highlightColor]];
    [textLabel setText:@"SECTION TITLE"];
    [textLabel setBackgroundColor:[UIColor clearColor]];

    UIImageView *backgroundView = [[UIImageView alloc]initWithImage:[AXThemeManager sharedTheme].tableviewSectionHeaderBackgroundImage];
    [backgroundView setFrame:view.frame];
    [view addSubview:backgroundView];
    [view sendSubviewToBack:backgroundView];
    [view addSubview:textLabel];

    return view;
}

- (float)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 22;
}

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}
[...]

怎么办呢?表头是一个独立的视图,不算作单元格中的部分内容。如果我把它放在第一个单元格中,当我滚动时,它会被滚动掉。 - Paul Peelen
忘记我的上一个评论了...如果单元格没有文本框,这种情况会发生吗?不会吧? - BabyPanda
是的,我在另一个tableview中也遇到过这样的情况。我使用一个加载器并在执行reloadData时“锁定”表来解决它。然而,这是一种糟糕的解决方案,只是一个临时的权宜之计。因此,这个问题不仅仅是一个“特定的时间案例”...它很容易重新创建。 - Paul Peelen
在有确切而强有力的答案之前,我只能建议确保updatelist不会阻塞UI更新,或者在主线程上执行reloadData/moveRowAtIndexPath...抱歉... - BabyPanda
我理解,但不幸的是那不是问题所在。更新等操作是另一个线程,只是将新数据更新到API端,没有更多内容发布到UITableView。对我来说,这似乎是iOS的一个bug。 - Paul Peelen
5个回答

10

好消息!我已经用两种不同的方法修复/解决了你的问题(请参见下文)。

我认为这肯定是操作系统的 bug。你所做的会导致使用 moveRowAtIndexPath: 移动的单元格被放置在 z-order 中标题单元格的上方(前面)。

我能在操作系统版本 5 和 6 中重现这个问题,无论单元格是否有 UITextFields,并且无论 tableView 是否处于编辑模式中(在你的视频中,我注意到它处于编辑模式)。即使你正在使用标准的 section headers,它也会发生。

Paul,你在其中一条评论中说:

我使用加载程序和“锁定”表格来解决它进行 reloadData

我不确定你所说的“使用加载程序和锁定表格”是什么意思,但我确信在调用 moveRowAtIndexPath: 后调用 reloadData 可以解决这个问题。那不是你想要做的吗?

[self.tableView moveRowAtIndexPath:indexPath toIndexPath:newPath];
//[self.tableView reloadData];
// per reply by Umka, below, reloadSections works and is lighter than reloadData:
[self reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationNone];
如果你不想那样做,这里有另一个解决方案,对我来说感觉有点hacky,但似乎很有效(iOS 5+):

如果您不想这样做,这里有另一种解决方案,对我来说感觉有点巧妙,但是似乎效果很好(iOS 5+):

__weak UITableViewCell* blockCell = cell;  // so we can refer to cell in the block below without a retain loop warning.
...
cell.onTextEntered = ^(NSString* sText)
{
    // move item in my model
    NSIndexPath *newPath = [NSIndexPath indexPathForRow:0 inSection:indexPath.section];
    [self.itemNames removeObjectAtIndex:indexPath.row];
    [self.itemNames insertObject:sText atIndex:0];

    // Then you can move cell to back
    [self.tableView moveRowAtIndexPath:indexPath toIndexPath:newPath];
    [self.tableView sendSubviewToBack:blockCell];  // a little hacky

    // OR per @Lombax, move header to front
    UIView *sectionView = [self.tableView headerViewForSection:indexPath.section];
    [self.tableView bringSubviewToFront:sectionView];

这听起来很棒,绝对值得一试。我周四回到工作岗位,第一时间尝试它!回答你的问题:不,那绝对不是我想做的事情。谢谢。 - Paul Peelen
我刚试了一下你的代码,它确实解决了问题的一部分...只要我离开编辑模式就没问题了...但是在编辑模式下滚动时会重叠。 - Paul Peelen
解决方法是在将单元格发送到后面之后,不要将sectionHeaderView设置为前面。只需不发送单元格即可正常工作。谢谢! - Paul Peelen

6

这是一个bug。 您可以在该行后快速解决它,只需添加:

[self.tableView moveRowAtIndexPath:indexPath toIndexPath:newPath];

这行文字:
UIView *sectionView = [self.tableView headerViewForSection:indexPath.section];
[self.tableView bringSubviewToFront:sectionView];

有没有可能知道为什么这个答案被踩了?出了什么问题? - LombaX

3

这不是一个解决方案,但你的代码存在许多问题。如果你修复它们会发生什么谁知道呢;)

(1) 在此行代码后,你的单元格可能为nil:

ListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

应该像这样:

ListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
                cell = [[[ListCell alloc] initWithStyle:style
                                                    reuseIdentifier:cellIdentifier] autorelease];
}

(2) 在-(UIView *)tableView:(UITableView *)aTableView viewForHeaderInSection:(NSInteger)section方法中存在两个内存泄漏问题。

->>解决方案(当您将标签添加为子视图时,它会增加+1的引用计数)。

UILabel *textLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10, 0, 300, 21)] autorelease];

->>修复(当你将视图添加为子视图时,它会增加1个引用)。

UIImageView *backgroundView = [[[UIImageView alloc]initWithImage:[AXThemeManager sharedTheme].tableviewSectionHeaderBackgroundImage] autorelease];

(3) 不是缺陷,但这可能会对您有所帮助。尝试使用这个而不是 [table reloadData]。它可以使事物动态地更新,并且不是一种非常强硬的更新表格的方式。我相信它更加轻量级。或者尝试查找其他“更新”方法。考虑到您的示例中没有删除行,类似于 [updateRowsFrom:idxFrom to:idxTo] 的东西会有所帮助。

[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0]
                              withRowAnimation:UITableViewRowAnimationAutomatic];

当UITableViewCells在Storyboard中定义时,似乎不需要检查nil。我不知道为什么,但事实就是如此。 - Morgan
  1. 如果Paul正在使用ARC,那么autorelease既不是必需的也不被允许。
- Morgan
我确认reloadSections和reloadData都能正常工作。然而,我认为UITableViewRowAnimationNone比UITableViewRowAnimationAutomatic更好看。 - Morgan
1
好的,我同意你说的所有话。至于(1),不确定story board会在内存压力情况下有所帮助。我们还是保险一点比较好。 - Cynichniy Bandera
没有内存压力的情况。从iOS 5开始,只要在viewDidLoad中注册nib,系统就会确保您获得一个有效的单元格。在iOS 6中,您可以注册一个类。 - jmstone617
@Morgan已经写了我要写的内容。虽然我没有写过最初的代码,但当我读到它时也有同样的疑问,并得出了与Morgan和JMStone相同的结论。 - Paul Peelen

0
我解决这个问题的方法是设置节标题视图的zPosition。
headerView.layer.zPosition = 1000 //just set a bigger number, it will show on top of all other cells.

0
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

  float  heightForHeader = 40.0;
    if (scrollView.contentOffset.y<=heightForHeader&&scrollView.contentOffset.y>=0) {
        scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
    } else if (scrollView.contentOffset.y>=heightForHeader) {
        scrollView.contentInset = UIEdgeInsetsMake(-heightForHeader, 0, 0, 0);
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接