Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No auto layout on first load tablewview #10

Open
Edig opened this issue Oct 10, 2014 · 28 comments
Open

No auto layout on first load tablewview #10

Edig opened this issue Oct 10, 2014 · 28 comments

Comments

@Edig
Copy link

Edig commented Oct 10, 2014

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,

@smileyborg
Copy link
Owner

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.

@Edig
Copy link
Author

Edig commented Oct 10, 2014

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
Copy link
Author

Edig commented Oct 10, 2014

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.

@smileyborg
Copy link
Owner

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.

@Edig
Copy link
Author

Edig commented Oct 10, 2014

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

@smileyborg
Copy link
Owner

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

@smileyborg
Copy link
Owner

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.

@Edig
Copy link
Author

Edig commented Oct 10, 2014

@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)

@smileyborg
Copy link
Owner

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.

@Edig
Copy link
Author

Edig commented Oct 10, 2014

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

@PetahChristian
Copy link

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.

@Edig
Copy link
Author

Edig commented Nov 16, 2014

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.

@smileyborg
Copy link
Owner

@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
Copy link
Owner

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
Copy link
Owner

Here's another workaround that may solve this issue: #22 (comment)

@8of
Copy link

8of commented May 7, 2015

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

@RishabhTayal
Copy link

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()

@smileyborg
Copy link
Owner

@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.

@RishabhTayal
Copy link

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

@nordringrayhide
Copy link

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

@JimVanG
Copy link

JimVanG commented Jun 29, 2015

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
Copy link

JimVanG commented Jun 30, 2015

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).

@gtsifrikas
Copy link

gtsifrikas commented Nov 24, 2016

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

@grangej
Copy link

grangej commented Dec 5, 2016

@gtsifrikas you sir, are a genius :-)

@thiagorossener
Copy link

@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.

@goneale
Copy link

goneale commented Nov 26, 2018

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.

@handlermyanmar
Copy link

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

@awilliams88
Copy link

awilliams88 commented Aug 9, 2019

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 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests