5

I've a tableView with custom cells. Each cell has different interface, elements. All they have different sizes. Each cell has different type of elements and their count. All these elements are created dynamically, so I'm creating them, making their frames and adding as subviews.

So the problem that heightForRowAtIndexPath executes before cellForRowAtIndexPath where I'm creating the row and constructing its interface and I don't know how to pass calculated height to heightForRowAtIndexPath

How can I count row height before and pass its value to heightForRowIndexPath for correct drawing of my tableView?

Hreno Hrenovich
  • 336
  • 6
  • 16

3 Answers3

3

If your cell view is really very complex and every component's height are depending on data source. You can try to create the view in heightForRowIndexPath method and then cache the created view to a dictionary in your view controller and use it directly in cellForRowAtIndexPath. In this way you only need to create the view once when user scrolling the table. If the datasource is not changing very frequently, you can reuse the cached view in heightForRowIndexPath as well.

And if the tableview has a lot of rows, you should return an approximate value for height in estimatedHeightForRowAtIndexPath to speed up the loading process of the view controller. Otherwise during loading tableview, it will try to calculate all row's height which may requires a lot of time.

But I really don't think your cell would be so complex. If only some UITextLabels that depends on datasource for the height, you can simply only calculate the height for the label, then add it to other components' height which is fixed.

Surely
  • 1,649
  • 16
  • 23
  • About its complex - it is. Its social network and when I'll do the wall with users post - their all can contain a text, pictures (up to 10), audio, video, documents and other attached to them. Also it can be a re-post, so all content will be shifted a bit and upper it will be the post-creator name. So there is no way to calculated row height in its uitableviewcell.swift-file and then pass it to the original heightForRowAtIndexPath, is it? – Hreno Hrenovich Jan 16 '16 at 12:28
  • @HrenoHrenovich no really, you can check the design carefully and extract some patterns out. For example, for images, audio, video or documents, the height for each single item usually would be fixed, you can get the total height by numberOfTheItem*eachItemHeight. – Surely Jan 16 '16 at 12:31
  • Thats it. So the question is about setting calculated height to rowHeight correctly. – Hreno Hrenovich Jan 16 '16 at 12:35
  • @HrenoHrenovich You can try my solution first, since you have the code to create view already, it should be very fast. If it works fine, your problem solved. But if you have performance issues, then we need to find other solution. – Surely Jan 16 '16 at 12:39
  • Oh, okay. I'll try. Thanks to you – Hreno Hrenovich Jan 16 '16 at 12:40
  • but the bad thing about such way to do is that I can not separate code of creating different rows, all of those must be in heightForRowAtIndexPath and it will be hard to read and understand it later – Hreno Hrenovich Jan 16 '16 at 13:00
  • @HrenoHrenovich How do you separate code in cellForRowAtIndexPath? you can do it same way in heightForRowAtIndexPath, because the two methods have the same parameter. – Surely Jan 16 '16 at 13:05
  • In cellForRowAtIndexPath I'm creating a cell depended to indexPath.row and calling function in them which construct their interface. All of cells has different type - custom UITableViewCell. – Hreno Hrenovich Jan 16 '16 at 13:10
  • @HrenoHrenovich I think your problem is the cell type. You can still use it in heightForRowAtIndexPath! For the cache dictionary, you use UITableViewCell as the type and in cellForRowAtIndexPath, you just directly return the cache view, no need to cast it to custom view. – Surely Jan 16 '16 at 13:13
  • @HrenoHrenovich If you still want to cast it to corresponding class, you can use the isKindOfClass method: http://stackoverflow.com/questions/1368124/iphone-how-to-check-the-type-of-an-object – Surely Jan 16 '16 at 13:29
2

You really should consider subclassing UITableViewCell and adding your custom logic inside your subclass.

Starting from there, you'll have such options:

  1. Create static method in your cell that will receive all data necessary to draw your cell (e.g heithtForCellWithFirstString:secondString:accessoryImage etc) and calculate height using string size computation methods. Use this method inside heightForRowAtIndexPath.

  2. Use autolayout for laying out subviews of your cell and then set table view's row height property to UITableViewAutomaticDimension. This way you won't need heightForRow delegate method at all. There are plenty of tutorials on this, for example: http://www.raywenderlich.com/87975/dynamic-table-view-cell-height-ios-8-swift

skorolkov
  • 229
  • 1
  • 4
  • Thanks. But if I calculate height inside heightForRowAtIndexPath and in the cell separatly - in will be twice executed the same operation. About autolayout - I thought about that, but elements have different positions all the time and also it can be that there is no one element so it will be nothing to connect to row top or bottom. That's why I need any advice about implementing some logic decision in my case – Hreno Hrenovich Jan 16 '16 at 12:10
  • Calculating height before cellForRow is another operation - as it doesn't involve view creation. It's just string.sizeWithAttributes and your custom margins arithmetics – skorolkov Jan 16 '16 at 12:22
0
  1. Try to create a subclass of UITableViewCell.
  2. All elements created by you put inside UIView, which is put in a cell.
  3. Create three NSLayoutConstraint, one from UIView.top to top of the cell, the second from UIView.bottom to the bottom of the cell and the third - the height of the UIView.
  4. In the viewDidLoad method of tableViewController set row height as the UITableViewAutomaticDimension

:

override func viewDidLoad() {
   super.viewDidLoad()
   self.tableView.rowHeight = UITableViewAutomaticDimension
}
  1. In a subclass of UITableViewCell create a method that will calculate the height of the cell and change NSLayoutConstraint, which sets the height of the cell. For example:

:

func adjustHeightOfInnerView() {
   let height = 100
   self.myInnerCellViewHeightConstraint.constant = height
   self.contentView.setNeedsUpdateConstraints()
}
  1. In the method cellForRowAtIndexPath call the adjustHeightOfTableview:

:

func tableView (tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
   let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! myTableViewCellSubclass
   cell.adjustHeightOfTableview()
   return cell
}
madmir
  • 151
  • 6