Calling layoutSubviews to dynamically resize a uilabel within a uicollectionviewcell - ios

I'm trying to resize a UILabel frame in a custom UICollectionViewCell. I added the label to the cell in Storyboards. The frame gets adjusted in -layoutSubviews. The frame is getting resized when layoutSubviews is called, but it isn't displaying the resize when the cell is displayed.
Here's what I'm assuming based on what I've seen...
It seems as though calling -layoutSubviews forces a re-layout on
every cell in the CollectionView, which hurts performance.
With auto-layout on, you simply can't resize the label.
If I delete the label in Storyboards, create the it
programmatically, and set the frame when the cell is called in
-cellForRowAtIndexPath, it works like a charm.
Are my assumptions above correct, and if so, is there an alternate to -layoutSubviews better suited for CollectionViews.

Related

Change frame of UIView in UICollectionViewCell in cellForItemAtIndexPath

My problem is very similar to this one, though the selected correct answer (which is just to use tags) didn't work for me (nor did any other answer): Changing label position in UICollectionViewCell.
I have a UICollectionView with a custom UICollectionViewCell. The cell has a UIView in it, which is referenced via IBOutlet property. I'm simply trying to change the frame within the cellForItemAtIndexPath method, and while the frame of the UIView is in fact changed, it is subsequently ignored, or not honored.
However, once the UICollectionView is scrolled a bit and it begins reusing cells, the reused cells do honor the previous change to the UIView's frame, and the UIView looks just like it should. So for some reason the first time cellForItemAtIndexPath is called for a particular cell, the frame change is ignored, but is subsequently honored once the cell is reused.
Actually, something more curious which I just noticed is that a reused cell has the size of the frame that had been previously changed, but keeps the original origin, which happens to be (0, 0), though I'm trying to update it to something like (0, 50).
Changing other properties of the UIView works fine, such as changing the background color. What might be happening to prevent the UIView's frame from initially changing?
UICollectionViewCell does not update frames for views that are defined in storyboard or xib files.
You have to create the view programmatically in your custom cells initWithFrame and/or awakeFromXib files.
Cheers!
hmmmm
I may be mistaken. I know this is true for UITableViewCell
Cannot update UI object's frame in UITableViewCell

Timing child view layout with UICollectionView layout changes

I have a UICollectionView that has a custom layout that changes the sizes of its cells when the device rotates. When the layout changes the size of a cell, layoutSubviews is called on each cell. This is what I want. However layoutSubviews does not execute in an animated manner but the change in size of the cell does. I would like to synchronize the two changes.
For example right now if the cell gets larger, the subviews resize instantly and then the cell size animates its change. I don't think I should put animation code in layoutSubviews. Is that correct? What is the best way to handle this?

UITableView Visible Cells Not Laying Out Subviews When View Appears

I have a UITableView that has multiple sections, with custom UITableViewCell subclasses populating the table. Within these UITableViewCell subclasses, I am implementing layoutSubviews to customize the position of labels, etc.
All of the cell's subviews are adjusting as they should, except the first batch of visible cells upon the view loading. They are identical to how they are laid out in storyboard, which is not what I want. For example:
http://i.stack.imgur.com/L5GJh.png
Note: The green and orange borders are a visual aid to see if the labels are resizing.
Upon scrolling, all of the new cells that appear have their subviews the way that I programmed them to be in layoutSubviews. As far as the first batch of visible cells, I can scroll them offscreen and then back on, and then the subviews are laid out perfectly. For example:
http://i.stack.imgur.com/jgjch.png
Within tableView: cellForRowAtIndexPath:, I call [cell layoutIfNeeded] before the method returns the cell. If I change this to [cell layoutSubviews], then the inverse happens, where the first batch of visible cells are laid out as they should be, but all of the cells that get loaded upon scrolling are not laid out properly.
I have tried to put [cell layoutIfNeeded] within [tableview: willDisplayCell: with no luck. Any ideas on how to fix this?
Thanks in advance!
This is the behavior you'd see if you were setting frames in your layout code with Auto Layout enabled. This won't work. The Auto Layout system is responsible for setting frames and will overwrite the values you set.
You should either specify your layout using constraints or turn off Auto Layout.

Custom iOS UITableViewCells not being re-laid out when recycled

I have a UITableView which is displaying update posts from various social networks. I have custom UITableViewCell subclasses, which have a custom UIView inside, which is responsible for drawing all the labels and images, similar to how Apple describes here: http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7-SW18
In this view's -layoutSubviews call, I find the size needed for the various labels, and set their frames accordingly. However, after scrolling for a little bit, it's obvious that -layoutSubviews is not being called when a cell is recycled, leading to some cells not having their label frame set big enough to display their content. When passing my data object through the cell to the custom view, I call [self setNeedsDisplay], which I believed would cause the cell to call -layoutSubviews.
How can I best ensure that the labels are properly resized for the content that gets sent to them?
If you want to force layout, send setNeedsLayout (not setNeedsDisplay) to the view. It sounds like you need to send [cell setNeedsLayout] in your tableView:cellForRowAtIndexPath: before returning the cell.

UITableViewCell frame height not matching tableView:heightForRowAtIndexPath:

I'm constructing a UITableView with variable height custom table cells, their height determined by the size of a contained multi-line UILabel. I've got the tableView:heightForRowAtIndexPath: delegate method wired up and calculating the final height correctly using sizeWithFont:constrainedToSize:.
I've run across strange issue: by the time the data source method tableView:cellForRowAtIndexPath: is called, the correct per-row height has already been determined as described above, but the frame of the cell does not match that height. Instead, the frame.size.height property of the cell is the default cell height of the table view (86 px, as I've set it in Interface Builder, the correct height when the contained UILabel has just one line of text), instead of being the height that tableView:heightForRowAtIndexPath: determined correct for that index path.
I'm producing the cells in cellForRowAtIndexPath: using dequeuing, that is,
// Using storyboards, this never returns nil, no need to check for it
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"SomeIdentifier"];
NSLog(#"%f", cell.frame.size.height); // 86, not correct if the cell contains a multi-line UILabel
It seems, then, that whatever iOS is doing behind the scenes, the dequeuing is not setting the frame property of the cell to match the calculated height. This in itself is not that surprising, dequeuing concerns itself with cell instances, not their geometry. The cells are rendered correctly, though, so the height property is being set somewhere, but it happens after cellForRowAtIndexPath:.
So: when I initially populate the table view, cell.frame.size.height is 86 for all the cells as they appear for the first time when I scroll the list downwards. Since the correct geometry is set sometime after the first cellForRowAtIndexPath: for each row before it's displayed, when I scroll back up, the height property is correct for each cell that comes back into view after being reused.
After this I can scroll the table view back and forth at will, and the height property remains correct for each cell from that point on.
What's the correct way of getting the correct cell height the first time around, before any dequeue-based reuse happens? I need this to do a bit of re-positioning of the subviews of the table cell. Do I need to manually call heightForRowAtIndexPath: in cellForRowAtIndexPath: and then manually set the frame of the freshly created CustomCell instance to match that height? This seems redundant, and I'd need to create a mechanism to detect when the cell is created for the first time with the wrong frame height vs. when it is dequeued with the correct frame height later to avoid this redundancy.
So, if someone can shed some light into what the logic is behind this, I'd appreciate it.
As suggested by Flexo, answering this myself is apparently better than adding an edit to the question. So, here's the previous edit as an answer:
Nevermind, I should read the docs better. I can get the correct frame in the tableView:willDisplayCell:forRowAtIndexPath: method of UITableViewDelegate, so that is the correct place to do subview customization that relies on the correct frame being set, not cellForRowAtIndexPath:.
Interesting that the docs say this, though:
After the delegate returns, the table view sets only the alpha and frame properties, and then only when animating rows as they slide in or out.
...since the correct frame is already there when this delegate method is called. But anyway, problem solved.
Don't forget that the cell is a UIView, so overriding layoutSubviews is also a valid way to get the correct frame and adjust size/position of subviews. Just don't forget to call [super layoutSubviews].
Easiest way I found was just to call cell.layoutIfNeeded() before you do any setup on the cell. This makes sure all the layout constraints are calculated and the frames are set.

Resources