【问题标题】:UITableView content size while using auto-layout使用自动布局时的 UITableView 内容大小
【发布时间】:2016-06-30 09:19:47
【问题描述】:

我想计算我的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;

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

编辑:

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

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

感谢您的帮助。

【问题讨论】:

  • 你能详细解释一下问题吗?
  • 我想要 tableview 的 contentsize。所以我可以根据 contentsize 更改 tableview 和 superview 框架。我正在使用自动布局和 UITableViewAutomaticDimension。所以它不能正常工作。
  • 如果您使用的是Autolayout,则无法更改框架。你需要玩NSLayoutContraints
  • tableView的containerView是什么?是滚动视图吗?
  • 什么时候需要更新 contentSize?是收到回复的时候吗?

标签: ios uitableview autolayout


【解决方案1】:

使用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)
}

【讨论】:

    【解决方案2】:

    基于块的 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)
        }
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • tableView.layoutIfNeeded() 为我修复了它,谢谢
      【解决方案4】:

      最后我通过一些调整解决了我的问题。在重新加载表格数据之前,我将表格视图高度更改为最大值(Objective-C:CGFLOAT_MAX,Swift:CGFloat.greatestFiniteMagnitude),因此表格视图有空间调整所有单元格的大小。

      Objective-C

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

      斯威夫特

      tableHeightConstraint.constant = CGFloat.greatestFiniteMagnitude
      tableView.reloadData()
      tableView.layoutIfNeeded()
      tableHeightConstraint.constant = contentTableView.contentSize.height
      

      发布此内容,以便对其他人有所帮助。

      【讨论】:

      • 你怎么知道 300 是最大(或足够)。我在滚动视图中有一个表格视图,并且表格视图应该具有其内容的高度。而且我发现我应该给tableview一个大于([self.tableView numberOfRowInSection:0] - 1) * estimatedHeight的初始高度。另外高度必须在viewWillLayoutSubviews 方法中设置,这只是工作,我不知道为什么。
      • 这确实有效。你知道为什么我们必须先将 constraint.constant 设置为 max 吗?没有它,这是行不通的。
      • 只需将表格视图的框架高度设置为Big_Enough_Value,然后删除行并在删除行后设置框架高度并将tableView contentOffset设置为初始值就足够了
      • 确实 工作,但因为我需要动画高度约束的变化 - 它看起来很恶心。它突然变得非常高,然后动画它缩小到正确的大小。虽然如果我删除第一行,它的动画效果很好,但尺寸不正确。我想我必须找到更好的解决方案。
      • 我发现(使用自动布局时,并且没有设置高度限制),我不需要添加第一行
      【解决方案5】:

      在我的例子中,我不使用 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)
      }
      

      【讨论】:

        【解决方案6】:

        快速使用

        tableView.invalidateIntrinsicContentSize()
        tableView.layoutIfNeeded()
        

        reloadData() 之后 和

        tableView.contentSize.height
        

        将完美运行。 Example on medium

        【讨论】:

        • 这对我也有用。非常感谢!
        【解决方案7】:

        最后,我明白了你的问题,这里是它的解决方案。

        我希望你已经这样做了。

        1. 首先将UITableView的固定高度设为。
        2. 然后采用约束IBOutletUITableView 高度。
        3. 然后在viewDidLayoutSubviews方法下填充数据后可以得到原来的UITableView高度。

        更新以下代码:

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

        更新:

        self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
        

        请检查一下。

        【讨论】:

        • 我也是这样做的。不工作。 tableView.contentSize 根据estimatedRowHeight 计算得出。调整所有单元格大小后我需要高度。
        • 感谢您的帮助。用我找到的解决方案编辑问题。
        【解决方案8】:

        您也可以将UITableView 子类化并从layoutSubviews() 调用您想要的任何人(即它的容器视图),您肯定有一个合适的contentSize

        【讨论】:

          【解决方案9】:

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

          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
                      }
                  }
              }
          }
          

          【讨论】:

          • 这是最好的方法!
          【解决方案10】:

          对我来说,解决方案是将estimatedRowHeight 设置为比我认为必须显示的数据更大的值。

          出于某种原因,这确保了estimatedRowHeight 属性不用于确定contentSize。我不显示滚动指示器,所以我认为estimatedRowHeight 已经过时并不重要。

          【讨论】:

            【解决方案11】:

            你可以尝试两件事,

            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;
              }
              

            试一试,它可以帮助你解决问题。

            【讨论】:

            • 感谢您的帮助。用我找到的解决方案编辑问题。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-07-25
            • 2012-09-27
            • 1970-01-01
            • 1970-01-01
            • 2014-04-08
            • 2016-03-31
            • 1970-01-01
            相关资源
            最近更新 更多