When you invalidate layout on a UICollectionViewFlowLayout it creates a bunch of new layout attributes for each of your cells; it doesn't tell your cells to redraw however, which causes distortions in any Layer drawings.
I don't want to tell my collection to reload its data because this removes any nice transitions you have between flow attributes : I have a grid layout transitioning into a coverflow for example.
I need a way for the UICollectionViewController class to tell the cells to call their [setNeedsDisplay] method after being given the layout attributes.
When your cells change size, it's up to them how to handle that (stretching vs redrawing). This is controlled by the UIView contentMode property; try setting it to UIViewContentModeRedraw to cause resizing to invalidate your views contents like setNeedsDisplay: would.
If you're using CALayers directly as sub-layers of your cell, you can set their needsDisplayOnBoundsChange to YES in order to get the same effect.
Related
I have a UITableView with several columns of data. The reusable cells are loaded from an xib file which has the appropriate labels and autolayout constraints. Everything works perfectly; the table columns are laid out correctly on different devices and when the devices are rotated.
The problem I am having is trying to create a table footer to show the totals of the columns in the table.
I created an xib file with the same autolayout constraints as the cell xib file and am loading it in tableView.viewForFooterInSection the same way I did for the cells. As required, I am using a subclass of UITableViewHeaderFooterView instead of UITableViewCell.
The awakeFromNib method in the UITableViewHeaderFooterView subclass sets the background color, so I can see that it is the correct size on all devices/orientation, but the labels from the footer xib file are not getting laid out to match the table cells.
The autolayout constraints from the footer xib file are not being honored. When I set a different background color in the footer xib file, the table footer shows this background color for the length of the xib's view.
I'm new to all of this technology and would greatly appreciate help in resolving this incredibly frustrating issue.
Is there a way to use autolayout for UITableViewHeaderFooterViews loaded from nibs?
You should call setNeedsUpdateConstraints() to update your view.
From apple documentation
Controls whether the view’s constraints need updating.
When a property of your custom view changes in a way that would impact constraints, you can call this method to indicate that the
constraints need to be updated at some point in the future. The system
will then call updateConstraints as part of its normal layout pass.
Updating constraints all at once just before they are needed ensures
that you don’t needlessly recalculate constraints when multiple
changes are made to your view in between layout passes.
Also, you can update view throw layoutSubviews()
Lays out subviews. The default implementation of this method does
nothing on iOS 5.1 and earlier. Otherwise, the default implementation
uses any constraints you have set to determine the size and position
of any subviews. Subclasses can override this method as needed to
perform more precise layout of their subviews. You should override
this method only if the autoresizing and constraint-based behaviors of
the subviews do not offer the behavior you want. You can use your
implementation to set the frame rectangles of your subviews directly.
You should not call this method directly. If you want to force a
layout update, call the setNeedsLayout method instead to do so prior
to the next drawing update. If you want to update the layout of your
views immediately, call the layoutIfNeeded method.
AFAIK, the reason that cause UICollectionViewLayout restart layout process are:
UICollectionView's frame change
UICollectionView reload data
UICollectionView insert/delete items
UICollectionView's bound change (if shouldInvalidateLayoutForBoundsChange return YES)
In my custom layout implementation, I want to change the UICollectionViewLayoutAttributes of certain items as the bound changes (stick header to the top like UITableView, animate item in/out .etc).
Problem is that for whatever reason it is, the UICollectionView always call its layout object's prepareLayout method (which contains heavy layout computation) and that make jerky scrolling.
I'm going for the solution that opt out the layout computation if the reason is UICollectionView's bound change.
How to check what reason the prepareLayout call?
I set a flag in shouldInvalidateLayoutForBoundsChange: so I know not to recalculate everything when that happens (on every frame...).
You can set another flag when prepareForAnimatedBoundsChange: is called, which happens when new items are inserted or when the bounds of the collection view change. You can then know if the bounds were changed because of scrolling or because of resize thanks to the flag you set in shouldInvalidateLayoutForBoundsChange:.
However, you should not have to worry too much of why you have to prepare the layout, since you should recalculate it fully in most cases anyways. In my project the only case where I do something differently in prepareLayout is when the invalidation was caused by scrolling.
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?
Custom UITableViewCell's subviews added in code using auto layout works (verified). However the whole point of doing this was to not have to calculate the height of each tableview cell and the delegate method heightForRowAtIndexPath expects a height while drawing the tableview.
How can I figure out this height based on content using the auto-layout (visual format language based addition in code already added and working) and return it to this heightForRowAtIndexPath?
Also I'm I can't really do this on a background thread (or can I?) and therefore if I have a UITableView using this backed by a list of say 1000 records, this will take forever, is that right?
Autolayout in this case just means that you don't need to calculate the frame sizes of your subviews within each cell. It's got nothing to do with the heightForRowAtIndexPath method - this is used by the table view to define the cell's frame, which will then inform the layout of the subviews.
Using Autolayout to determine the heights would likely be pretty slow, and you can't do it on a background thread. If you have 1000 rows then I'd consider a hierarchical structure instead of a single table as that will be pretty tedious to scroll through. You could also consider calculating the heights at the point of setting or creating the data.
You may be able to set up an offscreen view using your constraints, populate it with your data for each item, then record and cache the height. However you'd have to do this at the data end rather than in the height method, as it would be far too slow.
I have a grouped table view with custom cells (created programmatically by subclassing, not with IB). To properly position custom cell's subviews (such as labels and text fields), I need to know the current width of the cell's contentView just before the cell displays (taking into account that real cell width in a table view can change (according to screen orientation, modal presentation style, etc.)).
if I override in custom cell class the layoutSubviews method, it works perfectly, but it can be called frequently, thus I have to reposition my subviews every time when it's called, even when there's no need to do that.
Please, recommend me more elegant solution.
The recommended way of doing this is by setting the autoresizingMask of the table cell. If you need to have more control over the layout, you can store the last used view width in a member variable, and only layout the subviews if this differs from the current view width.