2

I've come across an annoying situation. I've found similar questions, but the answers didn't help me, so I was hoping I could get some advice.

I have a custom UITableViewCell where I'm using a .xib file. I fetch some data when the app loads, and once it's retrieved I reload my tableView. The cells display a picture and some labels, and I need the labels to adjust their size based on the width of the text for the content in the individual cell. Say I have this for cellForRowAt indexPath:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard
        let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.reuseIdentifier, for: indexPath) as? MyCell,
        indexPath.row < myDataArray.count
    else { return UITableViewCell() }

    let dataForCell = myDataArray[indexPath.row]
    cell.configure(with: dataForCell)
    cell.layoutIfNeeded()
    return cell
}

Now in my MyCell.swift file, inside the configure(with:) method I set the label texts, then I also call a private method called setWidths() which uses an extension I added to UILabel to return to me the width of a label after the text is set - I do some basic calculations to adjust the constraints of the labels how I want.

The only problem I'm having is that when the table view initially loads, the label's widths aren't updating properly, but when I scroll down to the cells that were originally out of frame, they are displayed properly, and then when I scroll back up to the cells that were originally in frame and misaligned, they then also show properly.

I know I've come across issues like this before, but usually calling layoutIfNeeded() on the cell fixes the issue. It's not this time.

I know that this is connected the the idea of reusable cells and dequeuing, but why is the first group of cells laid out not following the same rules? These are the first cells the user sees, I need them to be correct!

Does anyone have any advice for me?

EDIT

I think I've targeted the issue, however I still don't quite know what to do. I have to adjust my calculated label widths based on the available with of the cell frame. I added some print statements to see if the cell width was changing, and it is. Inside the setWidths() method I added print(self.frame.width) and when the tableView first loads it prints out 325.0 for each cell, but then as I scroll and it lays out new cells, it changes to 414.0 (Using iPhone 8 Plus simulator). What's causing the change in the cell's frame width from initial layout to when the cell is reused?

Pierce
  • 3,148
  • 16
  • 38
  • Why don't you just set your constraints and use the UITableviewAutomaticDimension instead of calculate the size by your own ? – Luis Perez Mar 27 '19 at 23:44
  • @LuisPerez - I am using automatic dimension for the height of each cell, but I can't use it for the widths of these individual labels. I have a very specific mockup I have to follow where the labels need to shrink based on the available space for the adjacent labels. It's a little too complicated to explain here without just confusing everyone. I added an edit to my question that is leading me to discover what's going on. – Pierce Mar 27 '19 at 23:52
  • I think i understand now, in the moment that you are calculating the width of your cell, the contentView is not rendered yet, what you need to do is to change your configure method to take a width, and there you can send the width of the tableview frame, it will be something like this: `cell.configure(with: DataForCell, width: tableview.frame.width)` Edit: after that, use that width to calculate the labels size – Luis Perez Mar 27 '19 at 23:57
  • @LuisPerez - Yes you're right. I just tried adding print statements in `viewDidLoad` and then again in `cellForRowAt` - for some reason in `viewDidLoad` my `self.view.frame.width = 375.0`, but then in `cellForRowAt` the `self.view.frame.width = 414.0` which makes my math correct. If you would like to add an answer I will accept it. So does iOS just layout for a generic frame size? Why the change from `375.0` to `414.0`? That's what I just don't understand – Pierce Mar 28 '19 at 00:03

1 Answers1

2

The issue here is that you are calculating your sizes when the cell is not rendered yet, update your configure method to take the tableview width, like this cell.configure(with: DataForCell, width: tableview.frame.width), after that use that width to configure your sizes, and about why it changes from 375 to 414, the answer is that you are getting the width of the regular iphone, when the view is rendered it changes to the plus size.

Luis Perez
  • 111
  • 8
  • @LuizPerez - I was making a dumb mistake in my calculations also that I had just realized, in case anyone else has a similar problem. Before I calculated the change in width for one constraint, I had changed the width of another label based on it's text size, and in my calculation I used `otherLabel.frame.width` instead of my IBOutlet `otherLabelWidthConstraint.constant`. So my calculation was using the other label's frame (which hadn't changed yet, and was set to how it's constant was arranged in the XIB file), instead of using the width I had changed it to, but it hadn't updated yet. – Pierce Mar 28 '19 at 00:17