forked from airbnb/MagazineLayout
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MagazineLayoutCollectionViewCell.swift
88 lines (77 loc) · 3.99 KB
/
MagazineLayoutCollectionViewCell.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Created by bryankeller on 11/29/18.
// Copyright © 2018 Airbnb, Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import UIKit
/// A cell that coordinates with `MagazineLayoutCollectionViewLayoutAttributes` to determine how to
/// size itself: with self-sizing, or without self-sizing. Use this class (or subclasses) for
/// displaying cells with `MagazineLayout`.
///
/// Note that this class is very similar to `MagazineLayoutCollectionReusableView`.
///
/// `UIKit` invokes `preferredLayoutAttributesFitting(_:)` with an initial set of layout attributes,
/// giving this cell subclass a chance to modify the `size` property of the attributes based on
/// whether or not we want to self-size.
///
/// Subclassing and/or adding additional protocol conformances is encouraged, although modifying
/// the behavior of `preferredLayoutAttributesFitting(_:)` is not recommended.
///
/// This class exists because `MagazineLayout` supports self-sizing in just the vertical dimension -
/// a use case that `UICollectionViewCell` does not support out-of-the-box.
///
/// As of iOS 12, `UICollectionReusableView` is tightly coupled with `UICollectionViewFlowLayout`'s
/// private `_estimatesSizes` property, which is used to determine how to size cells displayed in a
/// `UICollectionViewFlowLayout`. If `_estimatesSizes` is `true`, then `UICollectionReusableView`
/// will self-size in both the horizontal and vertical dimensions. If it is `false`, no self-sizing
/// will occur. In short, `UICollectionReusableView` is only optimized to work correctly with
/// Apple's own layout.
open class MagazineLayoutCollectionViewCell: UICollectionViewCell {
override open func preferredLayoutAttributesFitting(
_ layoutAttributes: UICollectionViewLayoutAttributes)
-> UICollectionViewLayoutAttributes
{
guard let attributes = layoutAttributes as? MagazineLayoutCollectionViewLayoutAttributes else {
assertionFailure("`layoutAttributes` must be an instance of `MagazineLayoutCollectionViewLayoutAttributes`")
return super.preferredLayoutAttributesFitting(layoutAttributes)
}
// In some cases, `contentView`'s required width and height constraints
// (created from its auto-resizing mask) will not have the correct constants before invoking
// `systemLayoutSizeFitting(...)`, causing the cell to size incorrectly. This seems to be a
// UIKit bug.
// https://openradar.appspot.com/radar?id=5025850143539200
// The issue seems most common when the collection view's bounds change (on rotation).
// We correct for this by updating `contentView.bounds`, which updates the constants used by the
// width and height constraints created by the `contentView`'s auto-resizing mask.
if contentView.bounds.width != layoutAttributes.size.width {
contentView.bounds.size.width = layoutAttributes.size.width
}
if
!attributes.shouldVerticallySelfSize &&
contentView.bounds.height != layoutAttributes.size.height
{
contentView.bounds.size.height = layoutAttributes.size.height
}
let size: CGSize
if attributes.shouldVerticallySelfSize {
// Self-sizing is required in the vertical dimension.
size = super.systemLayoutSizeFitting(
layoutAttributes.size,
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel)
} else {
// No self-sizing is required; respect whatever size the layout determined.
size = layoutAttributes.size
}
layoutAttributes.size = size
return layoutAttributes
}
}