नमस्कार,
व्यवहार में, iOS, डेवलपर अक्सर एक सूची के रूप में या एक संग्रह के रूप में, एक नियम के रूप में, बड़ी मात्रा में जानकारी प्रदर्शित करने के कार्य का सामना करता है UITableView
या इसके लिए महान होता है UICollectionView
। अक्सर सामना किया गया एक स्क्रीन को साकार करने का कार्य भी है, जो एक सूची और संग्रह का एक संयोजन है।
इस लेख में, हम विचार करेंगे कि इस कार्य को कार्यान्वित करने के लिए iOS 13 कौन सी नई सुविधाएँ लाया है।

परिचय
, , , , AppStore.

?
iOS 13,
.
iOS 12.
Compositional Layout
iOS 13 Apple , — Compositional Layout, 3- — , .
— , , , , , , , .

Compositional Layout — , , .
— , , - . , .
— grid-, flow- , .
— , , .
Compositional Layout. 4 :
NSCollectionLayoutSize
— ;NSCollectionLayoutItem
— ;NSCollectionLayoutGroup
— , ;NSCollectionLayoutSection
— ;UICollectionViewCompositionalLayout
— .
UICollectionViewCompositionalLayout
.
class UICollectionViewCompositionalLayout : UICollectionViewLayout { ... }
UICollectionViewCompositionalLayout
UICollectionViewLayout
, UICollectionView
.
List Layout
. storyboard-, UIViewController
, viewDidLoad()
UICollectionView
init(frame:, layout:)
UICollectionViewCompositionalLayout
. , UICollectionViewCompositionalLayout
.
, . , , , .

.
private func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
private func configureHierarchy() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
}
itemSize
, , NSCollectionLayoutDimension
. .
fractionalWidth(_:)
, , 0 1 , , 0.5
— () ;fractionalHeight(_: )
, , 0 1 , , 0.5
— () ;absolute(_: )
, , , 44.0
;estimated(_:)
, , .
itemSize
widthDimension = .fractionalWidth(1.0)
, heightDimension = .fractionalHeight(1.0)
, , . .
, widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(44)
, , , 44.0
.
:
horizontal(layoutSize: , subitem: , count: )
, , ;horizontal(layoutSize:, subitems: )
, , , ;vertical(layoutSize:, subitem:, count:)
, , ;vertical(layoutSize:, subitems: )
, , , ;custom(layoutSize:, itemProvider: )
, , .
, .horizontal
.vertical
, , , .custom
, , .
NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
, , .
NSCollectionLayoutSection(group: group), . Compositional Layout
.
, , . ?
Grid Layout

, ? , , , .
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0/4.0),
heightDimension: .fractionalHeight(1.0))
, 4 , .

Two Column Layout
, , , , , widthDimension
.

.
private func createLayout() -> UICollectionViewLayout {
let spacing: CGFloat = 10
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 2)
group.interItemSpacing = .fixed(spacing)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = .init(top: spacing, leading: spacing, bottom: spacing, trailing: spacing)
section.interGroupSpacing = spacing
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
group.interItemSpacing = .fixed(spacing)
, section.interGroupSpacing = spacing
, contentInset
section.contentInsets = .init(top: spacing, leading: spacing, bottom: spacing, trailing: spacing)
. spacing
.
, interItemSpacing
, interGroupSpacing
, NSCollectionLayoutSpacing
.
class NSCollectionLayoutSpacing : NSObject, NSCopying {
class func fixed(_ fixedSpacing: CGFloat) -> Self // i.e. ==
class func flexible(_ flexibleSpacing: CGFloat) -> Self // i.e. >=
// ...
}
func fixed(_ fixedSpacing: )
, ;func flexible(_ flexibleSpacing:)
, , , ;
Inset Items Grid Layout
, , ? , , . , 5 , , 1.0/5.0 , . , 1.0/5.0 . , .
, .contentInsets
NSCollectionLayoutItem
.

.
private func createLayout() -> UICollectionViewLayout {
let spacing: CGFloat = 10
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.2),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: spacing, leading: spacing, bottom: spacing, trailing: spacing)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalWidth(0.2))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
Distinct Sections Layout
, .
UICollectionViewCompositionalLayout
, UICollectionViewCompositionalLayoutSectionProvider
.
typealias UICollectionViewCompositionalLayoutSectionProvider = (Int, NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection?
class UICollectionViewCompositionalLayout : UICollectionViewLayout {
init(section: NSCollectionLayoutSection)
init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider)
}
UICollectionViewCompositionalLayoutSectionProvider
2 , , , .
3 , , grid- 3- , grid-, 5- .

.
enum Section: Int, CaseIterable {
case list
case grid3
case grid5
var columnCount: Int {
switch self {
case .list:
return 1
case .grid3:
return 3
case .grid5:
return 5
}
}
}
private func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
guard let sectionKind = Section(rawValue: sectionIndex) else { return nil }
let columns = sectionKind.columnCount
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 2, leading: 2, bottom: 2, trailing: 2)
let groupHeight = columns == 1 ?
NSCollectionLayoutDimension.absolute(44) :
NSCollectionLayoutDimension.fractionalWidth(0.2)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: groupHeight)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = .init(top: 20, leading: 20, bottom: 20, trailing: 20)
return section
}
return layout
}
Adaptive Sections Layout
, NSCollectionLayoutEnvironment
, traitCollection
.
protocol NSCollectionLayoutEnvironment : NSObjectProtocol {
var container: NSCollectionLayoutContainer { get }
var traitCollection: UITraitCollection { get }
}
ompact size-class , , , , .
traitCollection.verticalSizeClass
layoutEnvironment
.
container.effectiveContentSize
layoutEnvironment
.

.

.
enum Section: Int, CaseIterable {
case list
case grid3
case grid5
func columnCount(for width: CGFloat) -> Int {
let wideMode = width > 800
switch self {
case .list:
return wideMode ? 2 : 1
case .grid3:
return wideMode ? 6 : 3
case .grid5:
return wideMode ? 10 : 5
}
}
}
private func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
guard let sectionKind = Section(rawValue: sectionIndex) else { return nil }
let columns = sectionKind.columnCount(for: layoutEnvironment.container.effectiveContentSize.width)
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 2, leading: 2, bottom: 2, trailing: 2)
let groupHeight = layoutEnvironment.traitCollection.verticalSizeClass == .compact ?
NSCollectionLayoutDimension.absolute(44) :
NSCollectionLayoutDimension.fractionalWidth(0.2)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: groupHeight)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = .init(top: 20, leading: 20, bottom: 20, trailing: 20)
return section
}
return layout
}
Nested Groups
class NSCollectionLayoutGroup : NSCollectionLayoutItem, NSCopying { ... }
NSCollectionLayoutGroup
, , NSCollectionLayoutItem
, , , .

.
func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout {
(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let leadingItem = NSCollectionLayoutItem(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.7),
heightDimension: .fractionalHeight(1.0)))
leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
let trailingItem = NSCollectionLayoutItem(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.3)))
trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
let trailingGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.3),
heightDimension: .fractionalHeight(1.0)),
subitem: trailingItem, count: 2)
let bottomNestedGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.6)),
subitems: [leadingItem, trailingGroup])
let topItem = NSCollectionLayoutItem(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.3)))
topItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
let nestedGroup = NSCollectionLayoutGroup.vertical(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.4)),
subitems: [topItem, bottomNestedGroup])
let section = NSCollectionLayoutSection(group: nestedGroup)
return section
}
return layout
}
? ! , , .
section.orthogonalScrollingBehavior = .continuous

iOS 13 UICollectionView
, Compositional Layout .
. .
: