TableViewCellWithAutoLayoutiOS8 icon indicating copy to clipboard operation
TableViewCellWithAutoLayoutiOS8 copied to clipboard

No auto layout on first load tablewview

Open Edig opened this issue 10 years ago • 28 comments

I have a problem. I create a TableViewController with dynamic height on the cells. I follow this answer

http://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights/18746930#18746930

My problem its that (I copy the exact constraints) when I load information, like 2-3 lines it only appear the first or second line and the other line I need to scroll until the cell its outside the screen and bring it inside again and it comes with the other line. I hop eI explain my self.

I have my code, mostly the same as that post

var cell = tableView.dequeueReusableCellWithIdentifier("CellP", forIndexPath: indexPath) as CellPTableViewCell

        if (indexPath.row+1)%2 == 0 {
            cell.contentView.backgroundColor = UIColor(red: 244/255, green: 244/255, blue: 244/255, alpha: 1.0)
        }

        cell.label1.text = "\(userName) \(userLast)"
        cell.label1.text = userText

        cell.setNeedsUpdateConstraints()
        cell.updateConstraintsIfNeeded()
        return cell

Any ideas? the constraints are exactly the same as the post, I just modify the font (I try with the font he propose and its the same error)

Edit: I try it on iPhone 6 (Physically). and iPhone 4S (Simulator)

Best,

Edig avatar Oct 10 '14 16:10 Edig

Hi @Edig,

I don't think I have enough information to try and help. Did you compare your code to the code of this sample project on GitHub?

You'll probably need to share your entire project (or a sample project that illustrates the issues your seeing) if you would like more help in debugging.

90% of the issues people have with this is that their constraints are not correct, even though they think they are. I recommend double and triple checking that you have all the constraints you need -- it sounds like you could have an ambiguous layout issue.

smileyborg avatar Oct 10 '14 16:10 smileyborg

Hi @smileyborg , any email? also I copy the cell you have, exactly the same. And the cells resize perfectly but I need to reload the cell (put it off screen and put it again on screen)

The only thing I dones't have its

self.tableView.registerNib ()

because I have prototype cells

Edig avatar Oct 10 '14 16:10 Edig

Hi @smileyborg I really don't know why but If I add this

override func viewDidAppear(animated: Bool) {
    self.tableView.reloadData()
}

Works perfectly, it seems that in the first render of the cells, the system can't determine the exact height of the constraints.

Edig avatar Oct 10 '14 16:10 Edig

You can email tfox [at] smileyborg (dot) com. The issue could be related to the preferredMaxLayoutWidth of multi-line labels - not being set the first layout pass.

smileyborg avatar Oct 10 '14 16:10 smileyborg

Hi @smileyborg
cell.label2.preferredMaxLayoutWidth = CGRectGetWidth(cell.label2.frame)

work with the new cells, only I have a problem with the first 10 cells that appears without scrolling

Edig avatar Oct 10 '14 19:10 Edig

You'll have to share your project to help debug further. Which version of iOS are you testing on?

smileyborg avatar Oct 10 '14 19:10 smileyborg

This may be the same issue as #7. Can you try adding this code to your UITableViewCell subclass:

override func layoutSubviews() {
    super.layoutSubviews()

    placeName.preferredMaxLayoutWidth = CGRectGetWidth(placeName.frame)

    super.layoutSubviews()
}

If that doesn't work, try the following in your cellForRowAtIndexPath method:

cell.placeName.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

Basically, it seems like there is a bug with the Interface Builder "Automatic" preferredMaxLayoutWidth here.

smileyborg avatar Oct 10 '14 19:10 smileyborg

@smileyborg. With that modification it renders the height fine, but the distance between the label1 and label 2 (like your example) differ. When I hide and show the cell it appear with the exact position. Also the height of the cell changes

I try with

cell.placeName.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

The one its >=0 ( I change to = 0, and the same)

Edig avatar Oct 10 '14 20:10 Edig

Interesting, I'm not sure exactly what's going on. But it seems to suggest there are bugs with Apple's implementation of self sizing cells particularly when loading from a Storyboard/nib. You may wish to switch to implementing it in code (as in this example project), as it seems to be more reliable.

smileyborg avatar Oct 10 '14 20:10 smileyborg

@smileyborg Ok thanks! I will try it! and I let you know how it goes

Edig avatar Oct 10 '14 20:10 Edig

The problem with cells being loaded from a storyboard is that the cell's width does not initially correspond to the tableView's width (which affects the label's preferredMaxLayoutWidth). Reloading simply reuses a cell which at that point has the proper frame size.

Cells instantiated in code are created with the right dimensions from the start, so they appear correct at the first load.

PetahChristian avatar Nov 16 '14 19:11 PetahChristian

Hi peter,

I had the same problem and I solve it by putting any-any in the class size instead of compact-any

And now works great

Enviado desde mi iPhone

El nov 16, 2014, a las 1:12 PM, Peter Jensen [email protected] escribió:

The problem with cells being loaded from a storyboard is that the cell's width does not initially correspond to the tableView's width (which affects the label's preferredMaxLayoutWidth). Reloading simply reuses a cell which at that point has the proper frame size.

Cells instantiated in code are created with the right dimensions from the start, so they appear correct at the first load.

— Reply to this email directly or view it on GitHub.

Edig avatar Nov 16 '14 19:11 Edig

@PetahChristian You're probably right. But if so, this is definitely a bug on Apple's side. If you can confirm your hypothesis and still see issues with the latest iOS version I would definitely file a radar.

smileyborg avatar Nov 16 '14 22:11 smileyborg

By the way, not sure if anyone here had made progress on this issue. But I ran into something (potentially) similar recently, and found a workaround that may be helpful...see my comment at the bottom of this article:

If you're having row height issues on iOS 8.0 or 8.1, where your cells are sized incorrectly, after you initially call [tableView reloadData] to populate the table with cells, add the following:

[tableView setNeedsLayout]; [tableView layoutIfNeeded]; [tableView reloadData];

Forcing a layout pass on the table view and then doing a 2nd reloadData caused the cells to be sized correctly in my case (some debugging clearly indicated that the issue was with the table view's internal height calculation, not with the cell). This workaround also is invisible to the user, since it all happens synchronously before a redraw.

smileyborg avatar Jan 13 '15 04:01 smileyborg

Here's another workaround that may solve this issue: https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/issues/22#issuecomment-89853337

smileyborg avatar Apr 05 '15 21:04 smileyborg

@smileyborg The comment before last one helped me a lot - thanks! :cool:

8of avatar May 07 '15 23:05 8of

Wrote a nice little extension to fix this issues based in @smileyborg 's answer.

extension UITableView {
    func reloadDataWithAutoSizingCellWorkAround() {
        self.reloadData()
        self.setNeedsLayout()
        self.layoutIfNeeded()
        self.reloadData()
    }
}

Just call self.tableView.reloadDataWithAutoSizingCellWorkAround() instead of self.tableView.reloadData()

RishabhTayal avatar May 15 '15 22:05 RishabhTayal

@rishabhtayal That's nifty, but I would caution against using this all the time instead of reloadData -- you should only need the workaround on first load of the table view.

Doing these extra reloads and layout passes for additional calls to reload the table view will just be wasted CPU processing. So I would recommend only doing it when necessary (the first time) and just using reloadData from then on.

smileyborg avatar May 16 '15 03:05 smileyborg

@smileyborg Yeah of-course. That extension is meant only for the first load.

RishabhTayal avatar May 16 '15 04:05 RishabhTayal

@rishabhtayal +1: you've just saved my mind, yeah it works for me, thanks :-)

nordringrayhide avatar Jun 06 '15 07:06 nordringrayhide

Alright, so I can get my UITableViewCells that are ON screen to initially load in the correct height. But I can't get my UITableViewCells that are off screen to load in the proper height. I have to scroll to them, then scroll up-and-down, to force them to become the proper height.

I'm doing this after my initial call to self.reloadData():

self.reloadData()
self.setNeedsLayout()
self.layoutIfNeeded()
self.reloadData()

I have this in my cell subclass:

override func layoutSubviews() {
    super.layoutSubviews()

    placeName.preferredMaxLayoutWidth = CGRectGetWidth(placeName.frame)

    super.layoutSubviews()
}

and this in my cellForRowAtIndexPath: cell.placeName.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

I'm also using the estimatedRowHeight, and overriding override public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath), to return the calculated cell height based upon the height of my UILabel in the cell.

Anyone have any information on getting the off-screen cells to initially load in the correct cell height?

thank you!

JimVanG avatar Jun 29 '15 17:06 JimVanG

So in order to get the dynamic cell heights to work properly I had to completely configure my cell inside of tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath), to the point where it pretty much looked like my cell configuration in cellForRowAtIndexPath, this is pretty annoying but it works. I'm not sure why I have to configure the entire cell just to get the correct height instead of just returning a number.

For a good example of how to set up your cell in tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) check out this link, http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout , specifically the part on tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath).

JimVanG avatar Jun 30 '15 21:06 JimVanG

If you do this:

tableView.reloadData()
UIView.setAnimationsEnabled(false)
tableView.beginUpdates()
tableView.endUpdates()
UIView.setAnimationsEnabled(true)

The first visible cells will get the correct height

gtsifrikas avatar Nov 24 '16 20:11 gtsifrikas

@gtsifrikas you sir, are a genius :-)

grangej avatar Dec 05 '16 05:12 grangej

@gtsifrikas I could not make this work :(

Here is my code:

private func addCampaign(campaign: Campaign) {
        let rowToInsert = self.campaignsList.insert(campaign)
        self.campaignsMap[campaign.key] = rowToInsert
        UIView.setAnimationsEnabled(false)
        self.tableView.beginUpdates()
        self.tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: rowToInsert + 1, inSection: 0)], withRowAnimation: .Top)
        self.tableView.endUpdates()
        UIView.setAnimationsEnabled(true)
    }

My problem is exactly the same problem of that guy: https://forums.developer.apple.com/thread/67560

Which I believe is the same problem everyone here is facing. So, I turned off the animation and turned on again, as you said, but that didn't work.

This issue looks like a common issue, but until now I couldn't find any good answer for that.

thiagorossener avatar Mar 20 '17 21:03 thiagorossener

Confirmed @gtsifrikas that hack works. Quicker I can move to React the better.... Crazy we have to do all this leg work for ios when it comes to dynamic data and images.

goneale avatar Nov 26 '18 00:11 goneale

2019 and Xcode still facing this problem. Thanks for the solution guys.

handlermyanmar avatar Jun 12 '19 09:06 handlermyanmar

This may be the same issue as #7. Can you try adding this code to your UITableViewCell subclass:

override func layoutSubviews() {
    super.layoutSubviews()

    placeName.preferredMaxLayoutWidth = CGRectGetWidth(placeName.frame)

    super.layoutSubviews()
}

If that doesn't work, try the following in your cellForRowAtIndexPath method:

cell.placeName.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

Basically, it seems like there is a bug with the Interface Builder "Automatic" preferredMaxLayoutWidth here.

@smileyborg U Rock Bro 🤘 - The auto layout bug for multi-line label took so much of my time in debugging 😔 - Thanks for your solution 🙂

awilliams88 avatar Aug 09 '19 02:08 awilliams88