插入新 table 行后移动了 UITableView 部分页脚

UITableView section footer moved after inserting new table rows

我有一个显示项目列表的 UITableView。 table 视图控制器有一个项目数组,这些项目在响应对 Web 服务的调用时异步更新。这是我所拥有的示例(在 Swift 中):

class MyTableViewController : UITableViewController {
    var items: [ItemClass] = []

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("RootCell", forIndexPath: indexPath) as UITableViewCell
        if indexPath.section == 0 {
            let item = items[indexPath.row]
            cell.textLabel!.text = item.name
        }
        else if indexPath.section == 1 {
            // Another section not shown here
        }
        return cell
    }
}

我希望这个 table 的每个部分都有一个带按钮的页脚,所以我也包括这个:

    override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        let button = UIButton.buttonWithType(.System) as UIButton
        button.setTitle("Add", forState:UIControlState.Normal)
        if section == 0 {
            button.addTarget(self, action:Selector("itemAddPressed:"), forControlEvents:UIControlEvents.TouchUpInside)
        }
        else if section == 1 {
            // other section not shown here
        }
        return button
    }

通过在主 UI 线程之外调用的回调将项目添加到 items 数组。它看起来像这样:

private func itemWasAdded(item: ItemClass) {
    dispatch_async(dispatch_get_main_queue()) {
        self.items += [item]
        self.tableView!.reloadData()
    }
}

这一切都很好,但是当我知道一次只添加一个项目时,我对 table 的 reloadData 的使用对我来说似乎有点过分了。因此,我尝试更新它以执行以下操作:

private func itemWasAdded(item: ItemClass) {
    dispatch_async(dispatch_get_main_queue()) {
        self.items += [item]
        let indexPath = NSIndexPath(forRow:self.item.count - 1, inSection:0)
        self.tableView!.insertRowsAtIndexPaths([indexPath], withRowAnimation: .None)
    }
}

当我这样做时,table 继续工作,但页脚按钮有问题。我没有在每个部分的页脚中显示我创建的添加按钮,而是在 table 视图底部第 1 部分下方看到第 0 部分的添加按钮。

似乎可以通过强制刷新 table 来解决问题。这个 UITableViewController 是 UINavigationController 中的顶级控制器,如果我 select 一个 table 单元格,一个新的视图控制器将被推送到导航控制器上。导航回原来的 table 视图控制器,页脚按钮显示在正确的位置。

完成这项工作的最简单方法就是使用 reloadData 而不是 insertRowsAtIndexPaths。但是我想知道我在这里做错了什么,这样我就可以避免重新加载所有 table 数据。

我在这里使用 insertRowsAtIndexPaths 不正确吗?

我认为发生这种情况是因为缺少 beginUpdates() 和 endUpdates() 并且这是一个非常简单的错误。但是我在测试时遇到了完全相同的问题。

我只是分享一下我的观察。

当我尝试 tableView style Grouped 时,上面的问题没有发生。但是如果我使用 Plain 样式,页脚会下降到 table 视图的底部。我想这与不同的页脚视图行为有关,这取决于它的样式和更新数据后 table 查看布局其内容的方式。

如果必须使用 tablePlain 样式的视图,则必须处理其内容的高度总和(单元格和部分页脚视图)小于 table 的情况在插入行之前查看高度。 像,

let contentHeight = CGFloat(items.count * cellHeight + numberOfSection*footerHeight)
if contentHeight < tableViewHeight {
   tableView.frame = CGRectMake(0, 0, view.frame.size.width, numberOfSection*CGFloat(items.count * cellHeight + footerHeight))    
} else {
   tableView.frame = viewHeight 
}

为了让一切变得干净,您应该了解 table 视图的 footer/header 部分的不同样式和框架的行为。希望您能找到更符合您要求的解决方案。