什么时候UITableView的contentSize设置好了?

Dar*_*ren 45 uitableview uiscrollview ios

我有一个非滚动UITableViewUIScrollView.我将帧的大小设置UITableView为其内容大小.

当我添加一行时UITableView,我调用insertRowsAtIndexPaths:withRowAnimation:on UITableView.然后我调用一个方法来调整框架UITableView:

- (void)resizeTableViewFrameHeight
{
    // Table view does not scroll, so its frame height should be equal to its contentSize height
    CGRect frame = self.tableView.frame;
    frame.size = self.tableView.contentSize;
    self.tableView.frame = frame;
}
Run Code Online (Sandbox Code Playgroud)

看来虽然此时contentSize尚未更新.如果我根据行数和节数手动计算上述方法中的帧,则该方法可以正常工作.

我的问题是,我怎样才能UITableView更新它contentSize?我想我可以调用reloadData,这可能会这样做,但是当我只插入一个单元格时重新加载整个表似乎效率低下.

fel*_*ira 72

您可以通过调用UITableView强制UITableView计算内容的大小layoutIfNeeded.

应该位于具有可变大小的容器视图中的UITableViewController子类的示例:

- (CGSize)preferredContentSize
{
    // Force the table view to calculate its height
    [self.tableView layoutIfNeeded];
    return self.tableView.contentSize;
}
Run Code Online (Sandbox Code Playgroud)

更新2016-07-28 这是一个未经测试的Swift同样的例子.它应该工作,但如果没有,请发表评论.可能有更好或更惯用的方法来做到这一点.不幸的是我对Swift不太熟悉.

override var preferredContentSize: CGSize {
    get {
        self.tableView.layoutIfNeeded()
        return self.tableView.contentSize
    }
    set {}
}
Run Code Online (Sandbox Code Playgroud)

  • 不行!在调用insertRowAtIndexPath之后,我调用layoutIfNeeded,但是contentSize没有得到更新. (12认同)
  • 注意那些不能使用它的人 - tableView:estimatedHeightForRowAtIndexPath混淆了contentSize,所以你可以通过删除你的实现来获得运气. (9认同)

Bri*_*tta 26

虽然我不喜欢KVO(Key-Value Observing),但这是一个很好的方式来确切知道你的表何时contentSize发生了变化(而不是仅仅在一堆随机位置调用你的更新方法).它使用起来相当简单(尽管有点神秘和含蓄).要观察桌子上的变化contentSize,请执行以下操作:

1)成为你的表contentSize属性的观察者,如下所示:

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:NULL];
Run Code Online (Sandbox Code Playgroud)

这通常在保存tableView(viewDidLoad:例如)的视图控制器中完成.

2)实施KVO观察方法并进行所需的更改:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if(object == self.tableView && [keyPath isEqualToString:@"contentSize"]) {
        // perform your updates here
    }
}
Run Code Online (Sandbox Code Playgroud)

3)在某个逻辑点(我将其调用dealloc)中作为观察者移除视图控制器.你这样做:

- (void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentSize"];
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你提醒我KVO男人.我发现这是唯一适用于我相当复杂的布局的解决方案. (2认同)

rob*_*off 14

试试这个:

- (void)resizeTableViewFrameHeight
{
    UITableView *tableView = self.tableView;
    CGRect frame = tableView.frame;
    frame.size.height = [tableView sizeThatFits:CGSizeMake(frame.size.width, HUGE_VALF)].height;
    tableView.frame = frame;
}
Run Code Online (Sandbox Code Playgroud)


gbk*_*gbk 8

  1. 添加观察者(在我的样本中) viewDidLoad

    tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 观察价值

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let obj = object as? UITableView {
            if obj == self.tableView && keyPath == "contentSize" {
                if let newSize = change?[NSKeyValueChangeKey.newKey] as? CGSize {
                    //do stuff here
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 不需要时移除观察者

    deinit {
        self.tableView.removeObserver(self, forKeyPath: "contentSize")
    }
    
    Run Code Online (Sandbox Code Playgroud)


Ani*_*eja 6

正如@Farthen 建议的那样,您应该始终先调用 yourTableView.layoutIfNeeded() 来重新计算内容大小。

通过调用 layoutIfNeeded UITableView 将再次计算 contentSize,然后您可以更新框架或常量,无论它是什么。

简单的两行代码会省很多力。

tableView.layoutIfNeeded()
tableViewHeight.constant = tableView.contentSize.height
Run Code Online (Sandbox Code Playgroud)