4

I have a view controller with UITableView. The table data is populated using RxSwift:

let observable = Observable.just(data)
observable.bindTo(tableView.rx.items(cellIdentifier: "CategoryCell", cellType: CategoryCell.self)) { (row, element, cell) in
    cell.setCategory(category: element)
}.disposed(by: disposeBag)

tableView.rx.setDelegate(self).disposed(by: disposeBag)

and I have the following delegate function:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // cell = nil. 
    let cell = tableView.cellForRow(at: indexPath)

    let screenWidth = UIScreen.main.bounds.size.width

    let itemWidth = (screenWidth / 3.0) - 20
    let itemHeight = (itemWidth) / 0.75 + 110

    return itemHeight
}

I want to access the cell object from inside heightForRowAt but it gives me nil. Is there a way to access the cell here? I have been looking at UITableView+Rx.swift in RxCocoa project but there is no function for this. What other options do I have?

EDIT: I am trying to achieve the following in my code:

class CategoryCell : UITableViewCell {
     func calculateHeight() {
        let screenWidth = UIScreen.main.bounds.size.width

        let itemWidth = (screenWidth / 3.0) - 20
        let itemHeight = (itemWidth) / 0.75 + 110

        return itemHeight
     }
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // cell = nil. 
    guard let cell : CategoryCell = tableView.cellForRow(at: indexPath) as? CategoryCell {
         return 0.0
    }
    return cell.calculateHeight()
}
Gasim
  • 7,615
  • 14
  • 64
  • 131
  • Add the necessary parameter to the model and get the information from the data source array. – vadian Mar 25 '17 at 11:15
  • I have a function in the cell class that calculates the height of the cell. I will be having multiple prototype cells, so I want to unify it. So, it would be unwise to put that function in model that is part of the business logic. – Gasim Mar 25 '17 at 12:28
  • please check my edit for clarification. – Gasim Mar 25 '17 at 14:15
  • 1
    Anyway `heightForRow` is called **before** `cellForRow` that's why you get `nil`. – vadian Mar 25 '17 at 14:23
  • Thank you! I will try to do it in some other way. Maybe add it to tableviewcontroller itself. – Gasim Mar 25 '17 at 14:39
  • I'm having the same problem/confusion — @gasim. Have you found a solution? – bleeckerj Apr 05 '17 at 23:57
  • @bleeckerj the problem here is that height is called before cellForRow. Since my layout was not that complicated (a cover on top and bunch of collection views; all inside tableview), I just did it based on indexPath. To make it more flexible, I think you need to store your cell types in ViewModel (or Model if you are using MVC). Anything else is going to be hard. – Gasim Apr 06 '17 at 07:13
  • 1
    @Gasim Thank you for that. I have confirmed the same behavior. I think it's weird that cell types would go in the ViewModel but I understand. – bleeckerj Apr 06 '17 at 14:03
  • Could you set the table view's rowHeight to UITableViewAutomaticDimension and let the cells size themselves? – Paul Nov 23 '17 at 09:36

1 Answers1

2

The call tableView.cellForRow(at: indexPath) returns nil in tableView(_: heightForRowAt:) because heightForRowAt is called on a particular indexPath before the cellForRowAt is called.

The best bet here would be to use self-sizing cells. (https://www.raywenderlich.com/129059/self-sizing-table-view-cells). Another option would be to keep the size of the cells as part of your model and access them here.

Daniel T.
  • 32,821
  • 6
  • 50
  • 72