This article tells the
UITableView
、
UICollectionView
Knowledge of implementing the Self-sizing cell layout and how to optimize with Invalidationcontext
UICollectionView
Layout of the update. Background
iOS is becoming more user-friendly, and users can dynamically adjust the font size in the settings-general-accessibility feature. You'll find that the font size of all iOS-brought apps has changed, but the third-party app we've developed is still a previous font. After iOS7 we can use UIFont
the preferredFontForTextStyle:
class method to specify a style and let the font size match the font size set by the user. There are currently six styles to choose from:
123456 |
UIFontTextStyleHeadlineUIFontTextStyleBodyUIFontTextStyleSubheadlineUIFontTextStyleFootnoteUIFontTextStyleCaption1UIFontTextStyleCaption2
|
iOS adjusts the font appropriately to suit the purpose of the style.
The problem comes, such as the font size of this "dynamic type", we need to make dynamic UI adjustment, otherwise we always feel that our interface is strange:
We want to make the cell's height adjust with the font size:
In short, there are other dynamic factors that cause us to modify the layout.
Solution UITableView
There are three strategies to adjust the height of the cell (or header and footer):
- Adjust Height property
- Through the delegate method
tableView: heightForRowAtIndexPath:
- "Self-permutation" of the cell (self-sizing)
The first two strategies are familiar to us, and the third strategy is described later. UITableViewCell
and UICollectionViewCell
both support self-sizing
In IOS7, UITableViewDelegate
three new methods have been added to meet the user's method of setting the cell, header, and footer expected Heights:
123 |
- tableView:estimatedHeightForRowAtIndexPath:- tableView:estimatedHeightForHeaderInSection:- tableView:estimatedHeightForFooterInSection:
|
Of course, these three methods UITableView
estimatedRowHeight
, estimatedSectionHeaderHeight
and estimatedSectionFooterHeight
three attributes, are limited in that the height of all rows and sections can be defined uniformly.
In cell, for example, iOS creates a cell based on the projected height given, but when it is actually shown, IOS8 will contentSize
display the cell drawing after Self-sizing calculates the new size and adjusts the table. The key is how to derive the new size,ios of the cell provides two methods:
- Auto layout The artifact, which was launched two years ago, was poor at first, but with the increasing power of Xcode, the automatic layout in iOS7 became the default tick, by setting a series of constraints to make our UI adaptable to screens of all sizes. If you have experience using constraints, there must be a solution: add constraints to the cell
contentView
. iOS calls UIView
the method first systemLayoutSizeFittingSize:
to calculate the new size based on the constraint, and then calls the method if you do not implement the constraint systemLayoutSizeFittingSize:
sizeThatFits:
.
- Manual code we can rewrite the
sizeThatFits:
method to define the new size ourselves, so that we don't have to learn to constrain the relevant knowledge.
Below I give a demo-hardchoice written in Swift, using the automatic layout to adjust UITableViewCell
the height. By implementing a UITableViewCell
subclass DynamicCell
to automate the layout, you can download the source code on GitHub:
123456789101112 13141516 171819 20212223 242526 27282930 313233 34353637 |
Import UIKitClass Dynamiccell:uitableviewcell {Required Init (Coder:nscoder) {Super.init (Coder:coder)If Textlabel! = Nil {Textlabel.font = Uifont.preferredfontfortextstyle (uifonttextstyleheadline)Textlabel.numberoflines = 0}If Detailtextlabel! = Nil {Detailtextlabel.font = Uifont.preferredfontfortextstyle (uifonttextstylebody)Detailtextlabel.numberoflines = 0}} Override func constraints (), [Anyobject] {var constraints = [Anyobject] ()If Textlabel! = Nil {Constraints.extend (Constraintsforview (Textlabel))}If Detailtextlabel! = Nil {Constraints.extend (Constraintsforview (Detailtextlabel))} Constraints.append (Nslayoutconstraint (Item:contentview, Attribute:NSLayoutAttribute.Height, Relatedby: Nslayoutrelation.greaterthanorequal, Toitem:contentview, Attribute:NSLayoutAttribute.Height, multiplier:0, CONSTANT:44))contentview.addconstraints (constraints) return constraints } func constraintsforview (view: UIView), [anyobject]{ var constraints = [Nslayoutconstraint] () Constraints.append (Nslayoutconstraint ( Item:view, Attribute:NSLayoutAttribute.FirstBaseline, RelatedBy:NSLayoutRelation.Equal, Toitem:contentview, Attribute:NSLayoutAttribute.Top, multiplier:1.8, constant:30.0)) constraints.append (Nslayoutconstraint (item: Contentview, Attribute:NSLayoutAttribute.Bottom, RelatedBy:NSLayoutRelation.GreaterThanOrEqual, Toitem:view, Attribute:NSLayoutAttribute.Baseline, multiplier:1.3, Constant:8)) return constraints } }
|
The above code should note that classes in Objective-c can be considered in Swift AnyObject
, which works well for type compatibility issues.
Don't forget to add the Viewdidload method in the corresponding Uitableviewcontroller:
1 |
self.tableView.estimatedRowHeight = 44
|
The adaptive effect is as follows:
Uicollectionview
UITableView
And UICollectionView
are both Data-source and delegate driven. UICollectionView
further abstraction is made on top of this. It delegates control over the location, size, and appearance of its child views to a separate layout object. By providing a custom layout object, you can implement almost any layout you can imagine. Layouts inherit from the UICollectionViewLayout
abstract base class. In IOS6, UICollectionViewFlowLayout
a concrete layout implementation is presented in the form of class. In the UICollectionViewFlowLayout
self-sizing, the same applies:
After using self-sizing:
UICollectionView
The implementation of self-sizing can be done not only by contentView
adding constraints and rewriting methods on the cell sizeThatFits:
, but also at the cell level (previously contentSize
self-sizing): The rewritten UICollectionReusableView
preferredLayoutAttributesFittingAttributes:
method to modify the size after the self-sizing is computed, thus achieving full control of the cell layout properties ( UICollectionViewLayoutAttributes
).
PS: preferredLayoutAttributesFittingAttributes:
The method adjusts the Size property by default to accommodate the self-sizing Cell, so you need to call the parent class method before overriding it, and then UICollectionViewLayoutAttributes
do the changes you want to make on the returned object.
From this we have the most classic UICollectionViewLayout
mandatory computed properties (remember UICollectionViewLayoutAttributes
a series of factory methods?). To use self-sizing to adjust the size of the attribute according to our needs, and then to rewrite UICollectionReusableView
( UICollectionViewCell
and inherit from it) the preferredLayoutAttributesFittingAttributes:
method to modify all the properties from the cell level:
Here's how to UICollectionViewFlowLayout
implement Self-sizing:
First, the UICollectionViewFlowLayout
attribute is added, which is estimatedItemSize
UITableView
much like the "in estimated...Height
" (note I use the ellipsis to include the three attributes), but after all, the UICollectionView
item needs to constrain height and width, so it's a CGSIze
, except this one, with UITableView
There is no difference in the usage of " estimated...Height
".
Secondly... No second, in the UICollectionView
implementation of self-sizing, just give the estimatedItemSize
attribute (cannot be CGSizeZero
), a line of code is enough.
Invalidationcontext
If the device screen rotates, or needs to show some of its wonderful effects (such as coverflow), we need to invalidate the current layout and recalculate the layout. Of course, every calculation has a certain overhead, so we should be cautious about calling methods only when we need invalidateLayout
them to invalidate the layout.
In the iOS6 era, some people would "intelligently" do this:
12345678 |
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ CGRect oldBounds = self.collectionView.bounds; if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) { return YES; } return NO;}
|
The newly added class iOS7, however, UICollectionViewLayoutInvalidationContext
declares which parts of the layout will need to be updated when the layout fails. When the data source changes, invalidateEverything
and both invalidateDataSourceCounts
read-only bool attributes are marked with the UICollectionView
data source "expire all" and "section and item Count invalidated", UICollectionView
they are automatically set and provided to you.
You can invoke the invalidateLayoutWithContext:
method and pass UICollectionViewLayoutInvalidationContext
in an object, which optimizes the efficiency of the layout update.
When you customize a UICollectionViewLayout
subclass, you can call the invalidationContextClass
method to return a subclass of your definition UICollectionViewLayoutInvalidationContext
, so that your layout subclass will use your custom Invalidationcontext subclass to optimize the update layout when it fails.
You can also override invalidationContextForBoundsChange:
the method to return a Invalidationcontext object by overriding this method when implementing a custom layout.
All the above are new additions to iOS7 and can be applied in UICollectionViewFlowLayout
. In the iOS8, UICollectionViewLayoutInvalidationContext
It is also used on the Self-sizing cell.
UICollectionViewLayoutInvalidationContext
three new methods have been added to iOS8 to invalidate a particular section of item (Cell), supplementary view, or decoration view in more detail:
123 |
invalidateItemsAtIndexPaths:invalidateSupplementaryElementsOfKind:atIndexPaths:invalidateDecorationElementsOfKind:atIndexPaths:
|
corresponding to the addition of three read-only group attributes to mark the above three components:
123 |
invalidatedItemIndexPathsinvalidatedSupplementaryIndexPathsinvalidatedDecorationIndexPaths
|
The photo app that comes with iOS will stay at the top of each photo's information (time, place), and the ability to stick the header to the top is simply to invalidate the index's supplementary view.
UICollectionViewLayoutInvalidationContext
The newly added contentOffsetAdjustment
and contentSizeAdjustment
attributes allow us to update the displacement and size of the content of the CollectionView.
UICollectionViewLayout
a couple of ways to help us use self-sizing are also included:
12 |
shouldInvalidateLayoutForPreferredLayoutAttributes:withOriginalAttributes:invalidationContextForPreferredLayoutAttributes:withOriginalAttributes:
|
When a self-sizing cell property changes, the first method is called, it asks if the layout should be updated (that is, the original layout is invalidated), the default is no, and the second method more granular indicates which properties should be updated. You need to call the parent class's method to get a Invalidationcontext object, then make some changes you want, and finally return.
Imagine that if, in your custom layout, the size of a cell has changed for some reason (for example, because of a change in font size), other cells will change position due to self-sizing, you need to implement the above two methods to let the specified cell update some of the properties in the layout Do not forget the whole CollectionView contentSize
and contentOffset
therefore also will change, you need to give contentOffsetAdjustment
and contentSizeAdjustment
attribute assignment value.
IOS8 automatically adjusts UITableView and uicollectionview layouts