طاب مسائك،
من الناحية العملية ، يواجه المطور غالبًا مهمة عرض كمية كبيرة من المعلومات في شكل قائمة أو كمجموعة ، كقاعدة عامة ، UITableView
أو تكون رائعة لهذا الغرض UICollectionView
. غالبًا ما تصادف أيضًا مهمة تحقيق الشاشة ، وهي مزيج من قائمة ومجموعة.
في هذه المقالة ، سننظر في الميزات الجديدة التي جلبها iOS 13 لتنفيذ هذه المهمة.
![](https://habrastorage.org/webt/v3/ko/id/v3koidieod2tld3tki2h0f2wc6k.png)
المقدمة
, , , , AppStore.
![](https://habrastorage.org/webt/wt/yb/dw/wtybdwnr4zslldx8gql3dukj7iy.gif)
?
iOS 13,
.
iOS 12.
Compositional Layout
iOS 13 Apple , — Compositional Layout, 3- — , .
— , , , , , , , .
![](https://habrastorage.org/webt/v3/ko/id/v3koidieod2tld3tki2h0f2wc6k.png)
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
.
, . , , , .
![](https://habrastorage.org/webt/7e/jk/r9/7ejkr9pjvxbr2i-z6ckqh3h2hu8.png)
.
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
![](https://habrastorage.org/webt/rj/tp/0e/rjtp0ecy-d-dp-1sjwsfrngzijy.png)
, ? , , , .
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0/4.0),
heightDimension: .fractionalHeight(1.0))
, 4 , .
![](https://habrastorage.org/webt/qb/r_/6n/qbr_6ndntgy1mbhirtidwcm_bhc.png)
Two Column Layout
, , , , , widthDimension
.
![](https://habrastorage.org/webt/mc/gj/vg/mcgjvgnlhj1p9e6b6nj9xf8xtvm.png)
.
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
.
![](https://habrastorage.org/webt/y7/gs/gj/y7gsgjqcyst2_2rlfwhd_3n77i8.png)
.
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- .
![](https://habrastorage.org/webt/km/dz/49/kmdz49rcuj9x8h4nlnyiy4xa8z4.png)
.
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
.
![](https://habrastorage.org/webt/gm/e5/n_/gme5n_kxueln1vmgtazm3u-g2gg.png)
.
![](https://habrastorage.org/webt/my/m5/6w/mym56wfknv-tek6_s9igi6ico3a.png)
.
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
, , , .
![](https://habrastorage.org/webt/eb/w2/0o/ebw20oxbfq-d_htnngu2dcyls_k.png)
.
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
![](https://habrastorage.org/webt/po/jm/ni/pojmnitfpx3rpmjfirtpi6er4gu.gif)
iOS 13 UICollectionView
, Compositional Layout .
. .
: