使用自动布局时的UITableView内容大小

时间:2016-03-15 04:33:45

标签: ios uitableview autolayout

我想计算UITableView的内容大小。我正在使用身高UITableViewAutomaticDimension的自动布局。

在重新加载tableview后尝试获取[UITableView contentsize],在这种情况下,根据estimatedRowHeight计算身高。

self.tableView.estimatedRowHeight = 50;
self.tableView.rowHeight = UITableViewAutomaticDimension;

代表 -

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

尝试获取内容大小并将其指定为高度约束。

[self.tableView reloadData];
[self.tableView layoutIfNeeded];
self.tableHeightConstraint.constant = self.tableView.contentSize.height;

有什么建议吗?提前谢谢。

修改

最后我通过一些调整解决了我的问题。在重新加载表格数据之前,我将tableview高度更改为max(在我的情况下300是最大值),因此tableview可以调整所有单元格的大小。

self.tableHeightConstraint.constant = 300;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
self.tableHeightConstraint.constant = self.tableView.contentSize.height;

感谢您的帮助。

11 个答案:

答案 0 :(得分:36)

最后我通过一些调整解决了我的问题。在重新加载表格数据之前,我将tableview高度更改为max(在我的情况下300是最大值),因此tableview可以调整所有单元格的大小。

self.tableHeightConstraint.constant = 300;
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
self.tableHeightConstraint.constant = self.tableView.contentSize.height;

发布此内容对其他人有帮助。

答案 1 :(得分:13)

最后,我理解你的问题,这是解决方案。

我希望你已经做到了。

  1. 首先考虑一些UITableView的修复高度。
  2. 然后采用IBOutlet高度的约束UITableView
  3. 然后在viewDidLayoutSubviews方法下,您可以在填充数据后获得原始UITableView高度。
  4. 更新以下代码:

    override func viewDidLayoutSubviews() {
    
       constTableViewHeight.constant = tableView.contentSize.height
    }
    
      

    更新

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    

    请检查一下。

答案 2 :(得分:7)

在表格视图初始化期间将estimatedRowHeight设置为,并将以下代码添加到viewDidLayoutSubviews中,为我工作!

tableView.reloadData()
tableView.layoutIfNeeded()
tableView.constant = tableView.contentSize.height

答案 3 :(得分:2)

您可以尝试两件事,

  1. 在tableView完全重新加载后,在延迟几秒后调用以下方法。

    - (void)adjustHeightOfTableview
    {
      dispatch_async(dispatch_get_main_queue(), ^{
    
      [self.tableView reloadData];
    
      //In my case i had to call this method after some delay, because (i think) it will allow tableView to reload completely and then calculate the height required for itself. (This might be a workaround, but it worked for me)
      [self performSelector:@selector(adjustHeightOfTableview) withObject:nil afterDelay:0.3];
      });
    }
    
  2. 当您调用上述方法时,请不要返回estimatedHeightForRow,

    /*
    -(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
       return 44;
    } 
    */
    
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
       return UITableViewAutomaticDimension;
    }
    
  3. 试一试,它可以帮助你解决问题。

答案 4 :(得分:2)

上述所有解决方案都不适用于我的案例。 我最终使用了这样的观察者:

self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

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 {
                self.tableView.frame.size.height = newSize.height
            }
        }
    }
}

答案 5 :(得分:1)

对我来说,解决方案是将preg_match()设置为比我认为可以显示的数据更大的值。

由于某种原因,这确保了estimatedRowHeight属性未用于确定estimatedRowHeight。我没有显示滚动指示器,因此contentSize离开时对我来说并不重要。

答案 6 :(得分:0)

您也可以将UITableView子类化,然后从您肯定拥有合适的layoutSubviews()的{​​{1}}处呼叫任何您想要的人(即,它是容器视图)。

答案 7 :(得分:0)

快速使用

tableView.invalidateIntrinsicContentSize()
tableView.layoutIfNeeded()

在reloadData()之后 和

tableView.contentSize.height

将完美工作。 Example on medium

答案 8 :(得分:0)

在我的情况下,我不使用tableView的高度约束-使用自动布局,设置了顶部和底部约束,并且只有底部约束进入/退出编辑模式时才会发生变化。

Tzegenos's answer的修改方式如下: 1.在viewDidLoad()中添加tableView.estimatedRowHeight = 0 2.添加以下内容:

override func viewDidLayoutSubviews() {

    // 60 is row's height as set by tableView( cellRowAt )
    // places is of type [String] to be used by tableView
    tableView.contentSize.height = 60 * CGFloat(places.count)
}

答案 9 :(得分:0)

使用 KVO 是解决此 contentSize 问题的最佳实践,您不需要使用 layoutIfNeeded 强制 TableView 更新其布局。如果 max_height (CGFloat.greatestFiniteMagnitude) 太大或者手动设置高度不是一个好主意,则使用 @OnkarK 答案(已接受的答案)会导致滞后。

使用 Swift KVO:

override  func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

}
override func viewWillDisappear(_ animated: Bool) {
    self.tableView.removeObserver(self, forKeyPath: "contentSize")
    super.viewWillDisappear(true)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?){
    if keyPath == "contentSize" {
        if let value = change?[.newKey]{
            let size  = value as! CGSize
            self.tableHeightConstraint.constant = size.height
        }
    }
} 

使用组合:

override func viewDidLoad() {
   super.viewDidLoad()

   tableView.publisher(for: \.contentSize)
      .receive(on: RunLoop.main)
      .sink { size in
         self.tableHeightConstraint.constant = size.height
      }.store(in: &cancellables)
}

使用 RxSwift:

override func viewDidLoad() {
   super.viewDidLoad()

   tableView.rx.observe(CGSize.self, "contentSize")
      .filter({$0 != nil})
      .subscribe(onNext: { size in
          self.tableHeightConstraint.constant = size!.height
      }).disposed(by: bag)
}

答案 10 :(得分:0)

基于块的 KVO 方法

var contentSizeObserver: NSKeyValueObservation?
@IBOutlet weak var myTableView: UITableView!
@IBOutlet weak var tableHeightConstraint: NSLayoutConstraint!

override func viewDidLoad() {
        contentSizeObserver = myTableView.observe(\UITableView.contentSize, options: [NSKeyValueObservingOptions.new], changeHandler: { _, change in
            if let contentSize = change.newValue {
                self.tableHeightConstraint.constant = contentSize.height
            }
        })
    }

override func viewWillDisappear(_ animated: Bool) {
        contentSizeObserver = nil
        super.viewWillDisappear(true)
    }