Compositional Layout:將cell當積木組合排列
運用 UIKit 的 Compositional Layout 實作靈活排版與組合效果
Compositional Layout:將cell當積木組合排列
Compositional Layout:將cell當積木組合排列
運用 UIKit 的 Compositional Layout 實作靈活排版與組合效果
一般來說,在製作 CollectionView 時通常都是 Cell “一個個” 往右或往下堆疊,如果我要三個三個往右堆疊怎麼辦?又或者需求很奇葩,要一個大 Cell 後面接兩個小 Cell ?(巢狀結構)這樣怎麼辦
AppStore
總不能直接坐三行 CollectionView吧?這時就可用 Compositional Layout
基本的 CollectionView 的 layout架構
Compositional Layout 的架構
因為多了一個 group 物件,這個物件依照陣列方式塞入多個 Item,而 group 可以再依照排列方式:垂直 或 平行 的方式去排列,就可以達成很多排列效果 。
程式碼:Item 垂直相疊的橫向滾動
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
lazy var collectionViewLayout: UICollectionViewLayout = {
// items
let itemSize1 = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(30))
let item1 = NSCollectionLayoutItem(layoutSize: itemSize1)
let itemSize2 = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(60))
let item2 = NSCollectionLayoutItem(layoutSize: itemSize2)
let itemSize3 = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(90))
let item3 = NSCollectionLayoutItem(layoutSize: itemSize3)
// group
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(190))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item1, item2, item3])
group.interItemSpacing = .fixed(5)
group.edgeSpacing = NSCollectionLayoutEdgeSpacing(leading: nil, top: nil, trailing: .fixed(5), bottom: nil)
// section
let section = NSCollectionLayoutSection(group: group)
// fixed:指定固定的距離
// flexible:指定一個可伸長的最小距離
// header
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
heightDimension: .absolute(40))
let headerItem = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top,
absoluteOffset: CGPoint(x: 0, y: -5))
// footer
let footerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
heightDimension: .absolute(40))
let footerItem = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: footerSize,
elementKind: UICollectionView.elementKindSectionFooter,
alignment: .bottom,
absoluteOffset: CGPoint(x: 0, y: 5))
// 將header指定給section
section.boundarySupplementaryItems = [headerItem, footerItem]
// 畫面滾動方式
section.orthogonalScrollingBehavior = .continuous
// none:顧名思義,就是不會有垂直向的滾動(預設值)
// continuous:連續的滾動
// continuousGroupLeadingBoundary:連續的滾動,但會最後停在 group 的前緣
// paging:每次會滾動跟 CollectionView 一樣寬(或一樣高)的距離
// groupPaging:每次會滾動一個 group
// groupPagingCentered:每次會滾動一個 group,並且停在 group 置中的地方
section.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
// CompositionalLayout
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
override func viewDidLoad() {
super.viewDidLoad()
myCV.collectionViewLayout = collectionViewLayout
myCV.dataSource = self
myCV.delegate = self
myCV.register(myCollectionViewCell.self, forCellWithReuseIdentifier: myCollectionViewCell.identifier)
myCV.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView")
myCV.register(UICollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "FooterView")
}
// MARK: - UICollectionViewDelegate
extension ViewController: UICollectionViewDelegate {
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 30
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCollectionViewCell", for: indexPath) as? myCollectionViewCell else {
return UICollectionViewCell()
}
cell.backgroundColor = .systemBlue
return cell
}
// 當forSupplementaryViewOfKind不同時產生對應的CollectionReusableView
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var reusableView = UICollectionReusableView()
if kind == UICollectionView.elementKindSectionHeader {
// Header
reusableView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView", for: indexPath)
reusableView.backgroundColor = .gray
let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 40))
label.textAlignment = .center
label.text = "Header"
label.textColor = .black
reusableView.addSubview(label)
}else if kind == UICollectionView.elementKindSectionFooter {
// Footer
reusableView = collectionView.dequeueReusableSupplementaryView(
ofKind: UICollectionView.elementKindSectionFooter,
withReuseIdentifier: "FooterView",
for: indexPath)
reusableView.backgroundColor = .cyan
let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 40))
label.textAlignment = .center
label.text = "Footer"
label.textColor = UIColor.black
reusableView.addSubview(label)
}
return reusableView
}
}
- 4~11行是將每個 item 設置好大小,注意是用 NSCollectionLayoutSize
- 15行是敘述將每個 item 放進的排列方式
- 16行 裏面每個 item 互相的間隔, 記得這個要將這個間隔算近 group
效果:
Item 垂直相疊的橫向滾動(像AppStore)
程式碼:巢狀結構(就只列出不一樣的地方)
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
// items
let itemSize1 = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(30))
let item1 = NSCollectionLayoutItem(layoutSize: itemSize1)
let itemSize2 = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(60))
let item2 = NSCollectionLayoutItem(layoutSize: itemSize2)
let itemSize3 = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(95))
let item3 = NSCollectionLayoutItem(layoutSize: itemSize3)
// subGroup
let subGroupSize = NSCollectionLayoutSize(widthDimension: .absolute(110),heightDimension: .absolute(95))
let subGroup = NSCollectionLayoutGroup.vertical(layoutSize: subGroupSize, subitems: [item1, item2])
// item在group內的間距
subGroup.interItemSpacing = .fixed(5)
// group
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(225),heightDimension: .absolute(95))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item3, subGroup])
group.interItemSpacing = .fixed(5)
group.edgeSpacing = NSCollectionLayoutEdgeSpacing(leading: nil, top: nil, trailing: .fixed(5), bottom: nil)
效果:
巢狀Cell
這次學習紀錄參照:
關於 Header 跟 Footer 的設定是參照:
Post converted from Medium by ZMediumToMarkdown.
This post is licensed under CC BY 4.0 by the author.






