Как получить высоту UITableView при динамическом размере ячеек?

У меня есть UITableView с ячейками, которые имеют динамический размер. Это означает, что я установил:

tableView.estimatedRowHeight = 50.0
tableView.rowHeight = UITableViewAutomaticDimension

Теперь я хочу получить высоту всего представления таблицы. Я пытался пройти через это tableView.contentSize.height но это только возвращает приблизительную высоту строки, а не фактическую динамическую высоту табличного представления.

Как получить динамическую высоту всего представления таблицы?

12 ответов

Решение

Я наконец взломал решение:

tableView.contentSize.height не будет работать для динамических ячеек, потому что они будут возвращать только количество ячеек * estimatedRowHeight,

Следовательно, чтобы получить динамическую высоту представления таблицы, вы ищите все видимые ячейки и суммируете их высоту. Обратите внимание, что это работает только для табличных представлений, которые короче вашего экрана.

Однако, прежде чем мы сделаем вышеупомянутое для поиска видимых ячеек, важно знать, что примечание нам нужно получить представление таблицы на экране, чтобы мы могли получить видимые ячейки. Для этого мы можем установить ограничение высоты для табличного представления на произвольно большое число, чтобы оно отображалось на экране:

  1. Установить высоту ограничения просмотра таблицы:

    // Class variable heightOfTableViewConstraint set to 1000
    heightOfTableViewConstraint = NSLayoutConstraint(item: self.tableView, attribute: .height, relatedBy: .equal, toItem: containerView, attribute: .height, multiplier: 0.0, constant: 1000)
    containerView.addConstraint(heightOfTableViewConstraint)
    
  2. Вызовите tableView.layoutIfNeeded() и по завершении найдите видимые ячейки, суммируйте их высоту и отредактируйте heightOfTableViewConstraint:

    UIView.animate(withDuration: 0, animations: {
        self.tableView.layoutIfNeeded()
        }) { (complete) in
            var heightOfTableView: CGFloat = 0.0
            // Get visible cells and sum up their heights
            let cells = self.tableView.visibleCells
            for cell in cells {
                heightOfTableView += cell.frame.height
            }
            // Edit heightOfTableViewConstraint's constant to update height of table view
            self.heightOfTableViewConstraint.constant = heightOfTableView
    }
    

Вышеупомянутые решения довольно хороши. Я пробовал другой подход, и его легко и ясно понять.

      override func viewDidLoad() {
    super.viewDidLoad()
    tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
}

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

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "contentSize"{
            if object is UITableView{
                if let newValue = change?[.newKey]{
                    let newSize = newValue as! CGSize
                    heightOfTableViewConstraint.constant = newSize.height
                }
            }
        }
    }

На него уже дан ответ, но я тоже хочу поделиться своим опытом.

У меня та же проблема. Я искал ответы во многих похожих вопросах, пробовал их ответы и не работал.

Наконец я нашел ответ Леонардлоо. Большое спасибо, но это еще не решило мою проблему. Я просто хочу завершить его ответ. Я положил этот код из его ответа:

UIView.animate(withDuration: 0, animations: {
self.tableView.layoutIfNeeded()
}) { (complete) in
    var heightOfTableView: CGFloat = 0.0
    // Get visible cells and sum up their heights
    let cells = self.tableView.visibleCells
    for cell in cells {
        heightOfTableView += cell.frame.height
    }
    // Edit heightOfTableViewConstraint's constant to update height of table view
    self.heightOfTableViewConstraint.constant = heightOfTableView
}

в этом блоке кода:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    ...
    if indexPath.row == data.count-1{ 
        // Insert the code here
    }
    return cell
}

Это означает, что после загрузки всех строк в таблице мы обновляем высоту строки, используя этот фрагмент кода. Оно работает!!!

Мы можем использовать расширение UITableView для получения высоты,

extension UITableView {

   func visibleCellsHeight() -> CGFloat {
      var height: CGFloat = 0
      for cell in self.visibleCells {
          height = height + cell.bounds.height
      }
      return height
   } 
}

Вы можете добавить это в расширениях.

Свифт 4:

extension UITableView {

    // sums the height of visible cells
    func heightOfVisibleCells() -> CGFloat {
        return self.visibleCells.map({ $0.frame.height }).reduce(0, +)
    }

}

Хотя ответ @leonardloo частично верен и может удовлетворить потребности многих людей, я хотел бы поделиться тем, как я решил свою проблему.

У меня был большой tableView с большими строками, и у меня была та же проблема. Когда я применил ответ @leonardloo, я увидел, что он рисует только количество видимых ячеек, которые он нашел в начале, и он рисует 1 строку, тогда как он должен нарисовать 5 (мой источник данных).

Чтобы исправить это, я нашел такое простое решение:

UIView.animate(withDuration: 0, animations: {
self.tableView.layoutIfNeeded()
}) { (complete) in
    var heightOfTableView: CGFloat = 0.0
    // Get visible cells and sum up their heights
    let cells = self.tableView.visibleCells
    for cell in cells {
        heightOfTableView += cell.frame.height
    }
    // Edit heightOfTableViewConstraint's constant to update height of table view
    // Set tableView's constraint to height + 1 so that 
    // it can still draw the next visible cell it's supposed to.
    self.heightOfTableViewConstraint.constant = heightOfTableView + 1
}

Смысл здесь в том, чтобы позволить ему нарисовать следующую ячейку, чтобы она также учитывалась в visibleCells, установив константу ограничения на высоту + 1. Я проверил, и теперь кажется, что это работает как шарм.

Ответы выше не работают, если у вас есть такие вещи, как верхние, нижние колонтитулы, заголовки разделов. Использование высоты только видимых ячеек — это путь к катастрофе. Вместо этого вы можете использовать высоту tableView contentSize.

      UIView.animate(withDuration: 0, animations: {
        self.tableView.layoutIfNeeded()
        }) { (complete) in
            self. heightOfTableViewConstraint.constant = self.tableView.contentSize.height
    }

Просто поддерживайте массив типа CGFLoat и добавить UITableViewAutomaticDimension в cellForRowAtindexPath делегат tableview

var cellHeight = [CGFloat]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cellHeight.append(UITableViewAutomaticDimension)
}

Вы должны вычислить высоту tableView в следующем методе делегата,

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    //Get the height required for the TableView to show all cells
    if indexPath.row() == (tableView.indexPathsForVisibleRows.last! as! NSIndexPath).row {
        //End of loading all Visible cells
        float heightRequiredForTable = tableView.contentSize.height

        //If cell's are more than 10 or so that it could not fit on the tableView's visible area then you have to go for other way to check for last cell loaded 
    }
}

Я добавлю свой ответ здесь, так как я боролся с этим немного.

В моем табличном представлении использовались разделы с заголовками, поэтому просто получить высоту видимых ячеек не получалось. То, что я в конечном итоге сделал, было сначала установить очень большую высоту для tableView так что все содержимое будет добавлено. Тогда на последнем cell из tableViewЯ изменяю размер tableView быть такой же высоты, как его content,

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  ... // do your normal cell processing

  // determine height and change it
  if indexPath.section == (self.sectionTitles.count - 1) && indexPath.row == (sectionData.count - 1) {

    UIView.animate(withDuration: 0, animations: {
      self.tableView.layoutIfNeeded()
    }) { complete in

      // Edit heightOfTableViewConstraint's constant to update height of table view
      self.tableViewHeightConstraint.constant = self.tableView.contentSize.height
    }
  }
}

Я столкнулся с этим и на самом деле нашел более простое решение.

Не устанавливайте приблизительную высоту строки.

Затем размер содержимого табличного представления вычисляется в соответствии с принятым ответом после вызова layoutIfNeeded.

Вы можете попробовать этот код

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}
Другие вопросы по тегам