mirror of
https://github.com/material-components/material-components-ios.git
synced 2026-02-20 08:27:32 +08:00
790 lines
26 KiB
Markdown
790 lines
26 KiB
Markdown
<!--docs:
|
|
title: "Lists"
|
|
layout: detail
|
|
section: components
|
|
excerpt: "Lists are continuous, vertical indexes of text or images."
|
|
iconId: <#icon_id#>
|
|
path: /catalog/list/
|
|
api_doc_root: true
|
|
-->
|
|
|
|
# Lists
|
|
|
|
[](https://github.com/material-components/material-components-ios/issues?q=is%3Aopen+is%3Aissue+label%3Atype%3ABug+label%3A%5BList%5D)
|
|
|
|
[Lists](https://material.io/components/lists/) are continuous, vertical indexes of text or images.
|
|
|
|
**Contents**
|
|
|
|
* [Using lists](#using-lists)
|
|
* [Single-line list](#single-line-list)
|
|
* [Two-line list](#two-line-list)
|
|
* [Three-line list](#three-line-list)
|
|
* [Theming lists](#theming)
|
|
* [Building your own list item](#building-your-own-list-item)
|
|
|
|
- - -
|
|
|
|
## Using lists
|
|
|
|
### Installing
|
|
|
|
In order to install lists with Cocoapods first add the List component subspec to your `Podfile`:
|
|
|
|
```bash
|
|
pod 'MaterialComponents/List'
|
|
```
|
|
<!--{: .code-renderer.code-renderer--install }-->
|
|
|
|
Then, run the following command:
|
|
|
|
```bash
|
|
pod install
|
|
```
|
|
|
|
From there, import the relevant target or file and use your list item like you would any other `UICollectionViewCell`.
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
import MaterialComponents.MaterialList
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
#import "MaterialList.h"
|
|
```
|
|
<!--</div>-->
|
|
|
|
### List classes
|
|
|
|
We currently offer two `UICollectionViewCell` subclasses that can be used to create Material Design lists: `MDCBaseCell` and `MDCSelfSizingStereoCell`.
|
|
|
|
#### `MDCBaseCell`
|
|
|
|
The `MDCBaseCell` is a list item in its simplest form, a `UICollectionViewCell` subclass with ripple and elevation. The `MDCBaseCell` provides a starting point to build anything demonstrated in the extensive [design guidelines](https://material.io/go/design-lists). To build a list using `MDCBaseCell` simply treat it like you would any other `UICollectionViewCell`.
|
|
|
|

|
|
|
|
#### `MDCSelfSizingStereoCell`
|
|
|
|
The `MDCSelfSizingStereoCell` is a subclass of `MDCBaseCell`. It exposes two image views (trailing and leading) and two labels (title and detail) that the user can configure however they like.
|
|
|
|

|
|
|
|
Because the list items we provide inherit from `UICollectionViewCell`, clients are not expected to instantiate them themselves. Rather, clients should register the cell classes with `UICollectionViews`, and then cast the cells to the correct class in their implementations of `-collectionView:cellForItemAtIndexPath:`.
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
// registering the cell
|
|
collectionView.register(MDCBaseCell.self, forCellWithReuseIdentifier: "baseCellIdentifier")
|
|
|
|
// casting the cell to the desired type within `-collectionView:cellForItemAtIndexPath:`
|
|
guard let cell = collectionView.cellForItem(at: indexPath) as? MDCBaseCell else { fatalError() }
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
// registering the cell
|
|
[self.collectionView registerClass:[MDCBaseCell class]
|
|
forCellWithReuseIdentifier:@"BaseCellIdentifier"];
|
|
|
|
// casting the cell to the desired type within `-collectionView:cellForItemAtIndexPath:`
|
|
MDCBaseCell *cell =
|
|
[collectionView dequeueReusableCellWithReuseIdentifier:@"BaseCellIdentifier"
|
|
forIndexPath:indexPath];
|
|
```
|
|
<!--</div>-->
|
|
|
|
### Making lists accessible
|
|
|
|
#### Setting `-isAccessibilityElement`
|
|
|
|
We recommend setting `UICollectionViewCell`s (and `UITableViewCell`s) as `accessibilityElements`. That way, VoiceOver doesn't traverse the entire cell and articulate an overwhelming amount of accessibility information for each of its subviews.
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
cell.isAccessibilityElement = true
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
cell.isAccessibilityElement = YES;
|
|
```
|
|
<!--</div>-->
|
|
|
|
## List anatomy
|
|
|
|
The following is an anatomy diagram of a typical list that applies to single-line, two-line, and three-line lists:
|
|
|
|

|
|
|
|
This list item consists of the following attributes:
|
|
1. Leading image view
|
|
1. Title label and detail label
|
|
1. Trailing label
|
|
|
|
_**NOTE: `MDCSelfSizingStereoCell` currently only supports leading and trailing _image views_, so the trailing label would be represented by a `UIImageView`.**_
|
|
|
|
An instance of `MDCSelfSizingStereoCell` can be configured to be a single-line, two-line, or three-line list item. The features above map to the following propertieis and methods:
|
|
|
|
#### Container attributes
|
|
|
|
| **Attribute** | **Related methods** | **Default value**
|
|
------------- | -------------------- | ------------------------ | -----------------
|
|
**Color** | `rippleColor` | `-setRippleColor:` <br/> `-setRippleColor` | On surface color at 0.12 opacity
|
|
**Elevation** | `elevation` | `-setElevation:` <br/> `-elevation` | 0
|
|
|
|
#### Icon attributes
|
|
|
|
| **Attribute** | **Related methods** | **Default value**
|
|
-------------------- | ------------------------------------- | ---------------------------------------------------------------- | -----------------
|
|
**Leading image** | `leadingImageView` | N/A | N/A
|
|
**Trailing image** | `trailingImageView` | N/A | N/A
|
|
|
|
#### Text label attributes
|
|
|
|
| **Attribute** | **Related methods** | **Default value**
|
|
------------------------- | -------------------------------------- | ------------------------------ | -----------------
|
|
**Title text** |`titleLabel` | N/A | N/A
|
|
**Detail text** |`titleLabel` | N/A | N/A
|
|
|
|
**Types**
|
|
|
|
There are three list types: 1\. [Single-line list](#single-line-list), 2\. [Two-line list](#two-line-list) 3\. [Three-line list](#three-line-list)
|
|
|
|

|
|
|
|
## Single-line list
|
|
|
|
Single-line list items contain a maximum of one line of text.
|
|
|
|
### Single-line list example
|
|
|
|

|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
func collectionView(_ collectionView: UICollectionView,
|
|
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
guard cell = collectionView.dequeueReusableCell(
|
|
withReuseIdentifier: kSelfSizingStereoCellIdentifier,
|
|
for: indexPath)
|
|
as? MDCCollectionViewTextCell
|
|
else { return }
|
|
cell.titleLabel.text = "This is a single-line list"
|
|
return cell
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
|
|
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
MDCSelfSizingStereoCell *cell =
|
|
(MDCSelfSizingStereoCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
|
|
forIndexPath:indexPath];
|
|
cell.titleLabel.text = @"This is a single-line list";
|
|
return cell;
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
## Two-line list
|
|
|
|
Two-line list items contain a maximum of two lines of text.
|
|
|
|
### Two-line list example
|
|
|
|

|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
func collectionView(_ collectionView: UICollectionView,
|
|
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
guard cell = collectionView.dequeueReusableCell(
|
|
withReuseIdentifier: kSelfSizingStereoCellIdentifier,
|
|
for: indexPath)
|
|
as? MDCCollectionViewTextCell
|
|
else { return }
|
|
cell.titleLabel.text = "This is a two-line list"
|
|
cell.detailLabel.text = "This is secondary text that occupies one line."
|
|
return cell
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
|
|
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
MDCSelfSizingStereoCell *cell =
|
|
[collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
|
|
forIndexPath:indexPath];
|
|
cell.titleLabel.text = @"This is a two-line list";
|
|
cell.detailLabel.text = @"This is secondary text that occupies one line.";
|
|
return cell;
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
## Three-line list
|
|
|
|
Three-line list items contains a maximum of three lines of text.
|
|
|
|
### Three-line list example
|
|
|
|

|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
func collectionView(_ collectionView: UICollectionView,
|
|
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
guard cell = collectionView.dequeueReusableCell(
|
|
withReuseIdentifier: kSelfSizingStereoCellIdentifier,
|
|
for: indexPath)
|
|
as? MDCCollectionViewTextCell
|
|
else { return }
|
|
cell.titleLabel.text = "This is a three-line list"
|
|
cell.detailLabel.text = "This is secondary text\nthat occupies two lines."
|
|
return cell
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
|
|
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
MDCSelfSizingStereoCell *cell =
|
|
[collectionView dequeueReusableCellWithReuseIdentifier:kSelfSizingStereoCellIdentifier
|
|
forIndexPath:indexPath];
|
|
cell.titleLabel.text = @"This is a three-line list";
|
|
cell.detailLabel.text = @"This is secondary text\nthat occupies two lines.";
|
|
return cell;
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
## Theming
|
|
|
|
This is an example of a two-line list with Shrine theming:
|
|
|
|

|
|
|
|
To theme a list item in your own app, use the Material Theming extension. To do that, first add the
|
|
theming extension to your `Podfile`:
|
|
|
|
```bash
|
|
pod `MaterialComponents/List+Theming`
|
|
```
|
|
|
|
Then run the installer:
|
|
|
|
```bash
|
|
pod install
|
|
```
|
|
|
|
From there, call the theming method from your `UICollectionViewDelegate` code.
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
// Step 1: Import the theming extension
|
|
import MaterialComponents.MaterialList_Theming
|
|
|
|
// Step 2: Create a shared container scheme. A shared scheme should be created once in your app and
|
|
// shared with all components.
|
|
let containerScheme = MDCContainerScheme()
|
|
|
|
// Step 3: Apply the scheme to each cell - from within `collectionView(_:cellForItemAt:)`
|
|
cell.applyTheme(withScheme:containerScheme)
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
// Step 1: Import the theming extension
|
|
#import "MaterialList+Theming.h"
|
|
|
|
// Step 2: Create a shared container scheme. A shared scheme should be created once in your app and
|
|
// shared with all components.
|
|
id<MDCContainerScheming> containerScheme = [[MDCContainerScheme alloc] init];
|
|
|
|
// Step 3: Apply the scheme to each cell - from within `-collectionView:cellForItemAtIndexPath:`
|
|
[cell applyThemeWithScheme:containerScheme];
|
|
```
|
|
<!--</div>-->
|
|
|
|
## Building your own list item
|
|
|
|
<!-- Extracted from docs/create-your-own.md -->
|
|
|
|
The example files can be found <a href="examples/">here</a>
|
|
|
|

|
|
|
|
Our example consists of a custom `UICollectionViewController`: <a
|
|
href="examples/CollectionListCellExampleTypicalUse.m">examples/CollectionListCellExampleTypicalUse.m</a>
|
|
and also of a custom `UICollectionViewCell`: <a
|
|
href="examples/supplemental/CollectionViewListCell.m">examples/supplemental/CollectionViewListCell.m</a>.
|
|
|
|
The main focus will be on the custom cell as that's where all the logic goes
|
|
in, whereas the collection view and its controller are using mostly
|
|
boilerplate code of setting up a simple example and collection view.
|
|
|
|
### Layout
|
|
|
|
For our example we will have a layout consisting of a left aligned
|
|
`UIImageView`, a title text `UILabel` and a details text `UILabel`. The title
|
|
text will have a max of 1 line whereas the details text can be up to 3 lines.
|
|
It is important to note that neither the image nor the labels need to be set.
|
|
To see more of the spec guidelines for Lists please see here: <a
|
|
href="https://material.io/go/design-lists">https://material.io/go/design-lists</a>
|
|
|
|
To create our layout we used auto layout constraints that are all set up in
|
|
the `(void)setupConstraints` method in our custom cell. It is important to
|
|
make sure we set `translatesAutoresizingMaskIntoConstraints` to `NO` for all
|
|
the views we are applying constraints on.
|
|
|
|
### Ink ripple
|
|
|
|
Interactable Material components and specifically List Cells have an ink
|
|
ripple when tapped on. To add ink to your cells there are a few steps you need
|
|
to take:
|
|
|
|
1. Add an `MDCInkView` property to your custom cell.
|
|
|
|
1. Initialize `MDCInkView` on init and add it as a subview:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
let inkView = MDCInkView(frame: bounds)
|
|
inkView.usesLegacyInkRipple = false
|
|
addSubview(inkView)
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
_inkView = [[MDCInkView alloc] initWithFrame:self.bounds];
|
|
_inkView.usesLegacyInkRipple = NO;
|
|
[self addSubview:_inkView];
|
|
```
|
|
<!--</div>-->
|
|
|
|
1. Initialize a `CGPoint` property in your cell (`CGPoint _lastTouch;`) to
|
|
indicate where the last tap was in the cell.
|
|
|
|
1. Override the `UIResponder`'s `touchesBegan` method in your cell to identify
|
|
and save where the touches were so we can then start the ripple animation from
|
|
that point:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
let touch = touches.first
|
|
let location = touch?.location(in: self)
|
|
lastTouch = location
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
|
UITouch *touch = [touches anyObject];
|
|
CGPoint location = [touch locationInView:self];
|
|
_lastTouch = location;
|
|
|
|
[super touchesBegan:touches withEvent:event];
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
1. Override the `setHighlighted` method for your cell and apply the start and
|
|
stop ripple animations:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
override var isHighlighted: Bool {
|
|
set {
|
|
super.isHighlighted = newValue
|
|
if (newValue) {
|
|
inkView.startTouchBeganAnimation(at: lastTouch, completion: nil)
|
|
} else {
|
|
inkView.startTouchEndedAnimation(at: lastTouch, completion: nil)
|
|
}
|
|
}
|
|
// get...
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
- (void)setHighlighted:(BOOL)highlighted {
|
|
[super setHighlighted:highlighted];
|
|
if (highlighted) {
|
|
[_inkView startTouchBeganAnimationAtPoint:_lastTouch completion:nil];
|
|
} else {
|
|
[_inkView startTouchEndedAnimationAtPoint:_lastTouch completion:nil];
|
|
}
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
1. When the cell is reused we must make sure no outstanding ripple animations
|
|
stay on the cell so we need to clear the ink before:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
|
|
```swift
|
|
override func prepareForReuse() {
|
|
inkView.cancelAllAnimations(animated: false)
|
|
super.prepareForReuse()
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
|
|
```objc
|
|
- (void)prepareForReuse {
|
|
[_inkView cancelAllAnimationsAnimated:NO];
|
|
[super prepareForReuse];
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
Now there is ink in our cells!
|
|
|
|
### Self sizing
|
|
|
|
In order to have cells self-size based on content and not rely on magic number
|
|
constants to decide how big they should be, we need to follow these steps:
|
|
|
|
1. Apply autolayout constraints of our added subviews relative to each other
|
|
and their superview (the cell's `contentView`).
|
|
|
|
We need to make sure our constraints don't define static heights or widths but
|
|
rather constraints that are relative or our cell won't calculate itself based
|
|
on the dynamically sized content. You can see how it is achieved in the
|
|
`setupConstraints` method in our example. If you'll notice there are some
|
|
constraints that are set up to be accessible throughout the file:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
var imageLeftPaddingConstraint: NSLayoutConstraint
|
|
var imageRightPaddingConstraint: NSLayoutConstraint
|
|
var imageWidthConstraint: NSLayoutConstraint
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
NSLayoutConstraint *_imageLeftPaddingConstraint;
|
|
NSLayoutConstraint *_imageRightPaddingConstraint;
|
|
NSLayoutConstraint *_imageWidthConstraint;
|
|
```
|
|
<!--</div>-->
|
|
|
|
This is in order to support the changing layout if an image is set or not.
|
|
|
|
1. Because our list cells need to fill the entire width of the collection
|
|
view, we want to expose the cell's width to be settable by the view controller
|
|
when the cell is set up. For that we expose a `setCellWidth` method that sets
|
|
the width constraint of the `contentView`:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
func set(cellWidth: CGFloat) {
|
|
cellWidthConstraint.constant = cellWidth
|
|
cellWidthConstraint.isActive = true
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
- (void)setCellWidth:(CGFloat)width {
|
|
_cellWidthConstraint.constant = width;
|
|
_cellWidthConstraint.active = YES;
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
and then in the collection view's `cellForItemAtIndexPath` delegate method we
|
|
set the width:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
var cellWidth = collectionView.bounds.width
|
|
if #available(iOS 11.0, *) {
|
|
cellWidth -= collectionView.adjustedContentInset.left + collectionView.adjustedContentInset.right
|
|
}
|
|
set(cellWidth: cellWidth)
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
CGFloat cellWidth = CGRectGetWidth(collectionView.bounds);
|
|
if (@available(iOS 11.0, *)) {
|
|
cellWidth -=
|
|
(collectionView.adjustedContentInset.left + collectionView.adjustedContentInset.right);
|
|
}
|
|
[cell setCellWidth:cellWidth];
|
|
```
|
|
<!--</div>-->
|
|
|
|
1. In our collection view's flow layout we must set an `estimatedItemSize` so
|
|
the collection view will defer the size calculations to its content.
|
|
|
|
Note: It is better to set the size smaller rather than larger or constraints
|
|
might break in runtime.
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
flowLayout.estimatedItemSize = CGSize(width: kSmallArbitraryCellWidth,
|
|
height: kSmallestCellHeight)
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
_flowLayout.estimatedItemSize = CGSizeMake(kSmallArbitraryCellWidth, kSmallestCellHeight);
|
|
```
|
|
<!--</div>-->
|
|
|
|
### Typography
|
|
|
|
For our example we use a typography scheme to apply the fonts to our cell's
|
|
`UILabel`s. Please see <a href="../schemes/Typography">Typography
|
|
Scheme</a> for more information.
|
|
|
|
### Dynamic Type
|
|
|
|
<a
|
|
href="https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/">Dynamic
|
|
Type</a> allows users to indicate a system-wide preferred text size. To
|
|
support it in our cells we need to follow these steps:
|
|
|
|
1. Set each of the label fonts to use the dynamically sized MDC fonts in their
|
|
set/update methods:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
func updateTitleFont() {
|
|
if (_titleFont == nil) {
|
|
_titleFont = defaultTitleFont
|
|
}
|
|
_titleLabel.font =
|
|
_titleFont.mdc_fontSized(forMaterialTextStyle: .subheadline,
|
|
scaledForDynamicType: mdc_adjustsFontForContentSizeCategory)
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
- (void)updateTitleFont {
|
|
if (!_titleFont) {
|
|
_titleFont = defaultTitleFont();
|
|
}
|
|
_titleLabel.font =
|
|
[_titleFont mdc_fontSizedForMaterialTextStyle:MDCFontTextStyleSubheadline
|
|
scaledForDynamicType:_mdc_adjustsFontForContentSizeCategory];
|
|
[self setNeedsLayout];
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
1. Add an observer in the cell to check for the
|
|
`UIContentSizeCategoryDidChangeNotification` which tells us the a system-wide
|
|
text size has been changed.
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
NotificationCenter.default.addObserver(self,
|
|
selector: #selector(contentSizeCategoryDidChange(notification:)),
|
|
name: UIContentSizeCategory.didChangeNotification,
|
|
object: nil)
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver:self
|
|
selector:@selector(contentSizeCategoryDidChange:)
|
|
name:UIContentSizeCategoryDidChangeNotification
|
|
object:nil];
|
|
```
|
|
<!--</div>-->
|
|
|
|
|
|
In the selector update the font sizes to reflect the change:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
func contentSizeCategoryDidChange(_: NSNotification) {
|
|
updateTitleFont()
|
|
updateDetailsFont()
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
- (void)contentSizeCategoryDidChange:(__unused NSNotification *)notification {
|
|
[self updateTitleFont];
|
|
[self updateDetailsFont];
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
1. Add an observer also in the `UIViewController` so we can reload the
|
|
collection view once there is a change:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
func contentSizeCategoryDidChange(_: NSNotification) {
|
|
collectionView.reloadData()
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
- (void)contentSizeCategoryDidChange:(__unused NSNotification *)notification {
|
|
[self.collectionView reloadData];
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
### iPhone X safe area support
|
|
|
|
Our collection view needs to be aware of the safe areas when being presented
|
|
on iPhone X. To do so need to set its `contentInsetAdjustmentBehavior` to be
|
|
aware of the safe area:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
if #available(iOS 11.0, *) {
|
|
collectionView.contentInsetAdjustmentBehavior = .always
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
|
|
if (@available(iOS 11.0, *)) {
|
|
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
|
|
}
|
|
#endif
|
|
```
|
|
<!--</div>-->
|
|
|
|
Lastly, as seen in the self-sizing section on step 2, when setting the width
|
|
of the cell we need to set it to be the width of the collection view bounds
|
|
minus the adjustedContentInset that now insets based on the safe area.
|
|
|
|
### Landscape support
|
|
|
|
In your view controller you need to invalidate the layout of your collection
|
|
view when there is an orientation change. Please see below for the desired
|
|
code changes to achieve that:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
super.traitCollectionDidChange(previousTraitCollection)
|
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
|
self.collectionView.reloadData()
|
|
}
|
|
|
|
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
|
super.viewWillTransition(to: size, with: coordinator)
|
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
|
coordinator.animate(alongsideTransition: nil) { (_) in
|
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
|
[super traitCollectionDidChange:previousTraitCollection];
|
|
[self.collectionView.collectionViewLayout invalidateLayout];
|
|
[self.collectionView reloadData];
|
|
}
|
|
|
|
- (void)viewWillTransitionToSize:(CGSize)size
|
|
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
|
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
|
|
[self.collectionView.collectionViewLayout invalidateLayout];
|
|
|
|
[coordinator animateAlongsideTransition:nil completion:^(__unused id context) {
|
|
[self.collectionView.collectionViewLayout invalidateLayout];
|
|
}];
|
|
}
|
|
```
|
|
<!--</div>-->
|
|
|
|
### Right to left text support
|
|
|
|
To support right to left text we need to import `MDFInternationalization`:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
import MDFInternationalization
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
#import <MDFInternationalization/MDFInternationalization.h>
|
|
```
|
|
<!--</div>-->
|
|
|
|
and for each of our cell's subviews me need to update the `autoResizingMask`:
|
|
|
|
<!--<div class="material-code-render" markdown="1">-->
|
|
#### Swift
|
|
```swift
|
|
_titleLabel.autoresizingMask =
|
|
MDFTrailingMarginAutoresizingMaskForLayoutDirection(effectiveUserInterfaceLayoutDirection)
|
|
```
|
|
|
|
#### Objective-C
|
|
```objc
|
|
_titleLabel.autoresizingMask =
|
|
MDFTrailingMarginAutoresizingMaskForLayoutDirection(self.effectiveUserInterfaceLayoutDirection);
|
|
```
|
|
<!--</div>-->
|