Skip to content

Commit b957b92

Browse files
committed
go on
1 parent 8f5e936 commit b957b92

36 files changed

+640
-265
lines changed

README.md

+99
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,102 @@ A collection view layout capable of laying out views in vertically and horizonta
55
## Introduction
66

77
`SwiftyCollectionViewFlowLayout` is a `UICollectionViewLayout` subclass for laying out vertically and horizontal scrolling grids and lists of items. Compared to `UICollectionViewFlowLayout`, `SwiftyCollectionViewFlowLayout` supports many additional features:
8+
9+
- Support `Vertical` and `Horizontal` scroll direction.
10+
- Support water-flow and grid list.
11+
- Section background decoration that can be hidden/visible on a per-section basis.
12+
- Hiding or showing headers and footers on a per-section basis.
13+
- Self-sizing headers and footers.
14+
- Headers and footers offset.
15+
- Headers and footer direction.
16+
- Per-item self-sizing preferences (self-size and statically-size items anywhere in your collection view).
17+
- Item width/height based on a fraction of the total available width/height.
18+
19+
## Preview
20+
21+
## Getting Start
22+
23+
### Requirements
24+
25+
- Deployment target iOS 11.0+
26+
- Swift 5+
27+
- Xcode 14+
28+
29+
### Installation
30+
31+
#### CocoaPods
32+
33+
```ruby
34+
pod 'SwiftyCollectionViewFlowLayout'
35+
```
36+
37+
### Usage
38+
39+
Once you've integrated the `SwiftyCollectionViewFlowLayout` into your project, using it with a collection view is easy.
40+
41+
#### Setting up cells and headers
42+
43+
`SwiftyCollectionViewFlowLayout` requires its own UICollectionViewCell and UICollectionReusableView subclasses:
44+
45+
- `SwiftyCollectionViewCell`
46+
- `SwiftyCollectionReusableView`
47+
48+
These two types enable cells and supplementary views to self-size correctly when using `SwiftyCollectionViewFlowLayout` .
49+
50+
#### Importing SwiftyCollectionViewFlowLayout
51+
52+
At the top of the file where you'd like to use `SwiftyCollectionViewFlowLayout`, import `SwiftyCollectionViewFlowLayout`.
53+
54+
```swift
55+
import SwiftyCollectionViewFlowLayout
56+
```
57+
58+
#### Setting up the collection view
59+
60+
Create your UICollectionView instance, passing in a `SwiftyCollectionViewFlowLayout` instance for the layout parameter.
61+
62+
```swift
63+
let layout = SwiftyCollectionViewFlowLayout()
64+
layout.scrollDirection = .vertical
65+
66+
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
67+
collectionView.dataSource = self
68+
collectionView.delegate = self
69+
70+
view.addSubview(collectionView)
71+
// Use SnapKit
72+
collectionView.snp.makeConstraints { make in
73+
make.edges.equalToSuperview()
74+
}
75+
```
76+
77+
#### Registering cells and supplementary views
78+
79+
```swift
80+
// Cell
81+
collectionView.register(MyCell.classForCoder(), forCellWithReuseIdentifier: "MyCellReuseIdentifier")
82+
83+
// Header
84+
collectionView.register(MyHeaderView.classForCoder(), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "MyHeaderReuseIdentifier")
85+
86+
//Footer
87+
collectionView.register(MyFooterView.classForCoder(), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "MyFooterReuseIdentifier")
88+
```
89+
90+
#### Decoration view
91+
92+
`SwiftyCollectionViewFlowLayout` support decoration view.
93+
94+
```swift
95+
layout.register(DecorationView.classForCoder(), forDecorationViewOfKind: SwiftyCollectionViewFlowLayout.DecorationElementKind)
96+
```
97+
98+
Make sure that the custom decoration view kind is **SwiftyCollectionViewFlowLayout.DecorationElementKind**.
99+
100+
#### Configuring the delegate
101+
102+
103+
104+
105+
106+

SwiftyCollectionViewFlowLayout/Sources/DecorationModel.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import Foundation
99
import UIKit
1010

1111
internal final class DecorationModel {
12-
internal let extraAttributes: SwiftyCollectionViewLayoutDecorationExtraAttributes?
12+
internal let extraAttributes: SwiftyCollectionViewDecorationExtraAttributes?
1313
internal let extraInset: UIEdgeInsets
1414

1515
internal var frame: CGRect = .zero
1616

17-
internal init(extraAttributes: SwiftyCollectionViewLayoutDecorationExtraAttributes?, extraInset: UIEdgeInsets) {
17+
internal init(extraAttributes: SwiftyCollectionViewDecorationExtraAttributes?, extraInset: UIEdgeInsets) {
1818
self.extraAttributes = extraAttributes
1919
self.extraInset = extraInset
2020
}

SwiftyCollectionViewFlowLayout/Sources/Default.swift

+9-4
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,24 @@
88
import Foundation
99
import UIKit
1010

11+
/// Default value.
1112
internal struct Default {
1213
static let sectionType: SwiftyCollectionViewSectionType = .waterFlow(numberOfColumns: 1)
1314
static let sectionInset: UIEdgeInsets = .zero
1415
static let sectionInsetContainHeader: Bool = false
1516
static let sectionInsetContainFooter: Bool = false
17+
static let headerOffet: UIOffset = .zero
18+
static let footerOffet: UIOffset = .zero
19+
static let headerDirection: SwiftyCollectionViewLayoutSupplementaryDirection = .left
20+
static let footerDirection: SwiftyCollectionViewLayoutSupplementaryDirection = .left
1621
static let lineSpacing: CGFloat = 15.0
1722
static let interitemSpacing: CGFloat = 15.0
18-
static let decorationVisibilityMode: SwiftyCollectionViewFlowLayoutDecorationVisibilityMode = .hidden
23+
static let decorationVisibilityMode: SwiftyCollectionViewLayoutDecorationVisibilityMode = .hidden
1924
static let decorationExtraInset: UIEdgeInsets = .zero
20-
static let sizeMode: SwiftyCollectionViewFlowLayoutSizeMode = .default
25+
static let sizeMode: SwiftyCollectionViewLayoutSizeMode = .default
2126
static let size: CGSize = CGSize(width: 50, height: 50)
22-
static let headerVisibilityMode: SwiftyCollectionViewFlowLayoutSupplementaryVisibilityMode = .hidden
23-
static let footerVisibilityMode: SwiftyCollectionViewFlowLayoutSupplementaryVisibilityMode = .hidden
27+
static let headerVisibilityMode: SwiftyCollectionViewLayoutSupplementaryVisibilityMode = .hidden
28+
static let footerVisibilityMode: SwiftyCollectionViewLayoutSupplementaryVisibilityMode = .hidden
2429
static let metrics: SectionMetrics = SectionMetrics.default
2530
}
2631

SwiftyCollectionViewFlowLayout/Sources/FooterModel.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import Foundation
99

1010
internal final class FooterModel {
1111

12-
internal var sizeMode: SwiftyCollectionViewFlowLayoutSizeMode
12+
internal var sizeMode: SwiftyCollectionViewLayoutSizeMode
1313

1414
internal var frame: CGRect = .zero
1515

16-
internal init(sizeMode: SwiftyCollectionViewFlowLayoutSizeMode) {
16+
internal init(sizeMode: SwiftyCollectionViewLayoutSizeMode) {
1717
self.sizeMode = sizeMode
1818

1919
switch sizeMode.width {

SwiftyCollectionViewFlowLayout/Sources/HeaderModel.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import Foundation
1010

1111
internal final class HeaderModel {
1212

13-
internal var sizeMode: SwiftyCollectionViewFlowLayoutSizeMode
13+
internal var sizeMode: SwiftyCollectionViewLayoutSizeMode
1414

1515
internal var frame: CGRect = .zero
1616

17-
internal init(sizeMode: SwiftyCollectionViewFlowLayoutSizeMode) {
17+
internal init(sizeMode: SwiftyCollectionViewLayoutSizeMode) {
1818
self.sizeMode = sizeMode
1919

2020
switch sizeMode.width {

SwiftyCollectionViewFlowLayout/Sources/ItemModel.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import Foundation
99

1010
internal final class ItemModel {
1111

12-
internal var sizeMode: SwiftyCollectionViewFlowLayoutSizeMode
12+
internal var sizeMode: SwiftyCollectionViewLayoutSizeMode
1313

1414
internal var frame: CGRect = .zero
1515

16-
internal init(sizeMode: SwiftyCollectionViewFlowLayoutSizeMode) {
16+
internal init(sizeMode: SwiftyCollectionViewLayoutSizeMode) {
1717
self.sizeMode = sizeMode
1818

1919
switch sizeMode.width {

SwiftyCollectionViewFlowLayout/Sources/ModeState+Decoration.swift

+89-18
Original file line numberDiff line numberDiff line change
@@ -13,54 +13,125 @@ extension ModeState {
1313
guard let sectionModel = sectionModel(at: section) else { return }
1414
guard let decorationModel = decorationModel(at: section) else { return }
1515

16-
var decorationFrame: CGRect = .zero
16+
guard let layout = layout else { return }
1717

18+
let scrollDirection = layout.scrollDirection
19+
20+
var standardDecorationFrame: CGRect = .zero
21+
switch scrollDirection {
22+
case .vertical:
23+
if let header = sectionModel.headerModel, sectionModel.metrics.sectionInsetContainHeader {
24+
standardDecorationFrame.origin.x = sectionModel.metrics.sectionInset.left
25+
26+
standardDecorationFrame.origin.y = previousSectionTotalLength(currentSection: section)
27+
standardDecorationFrame.origin.y += sectionModel.metrics.sectionInset.top
28+
29+
standardDecorationFrame.size.width = layout.mCollectionView.bounds.width - sectionModel.metrics.sectionInset.left - sectionModel.metrics.sectionInset.right
30+
31+
standardDecorationFrame.size.height = header.frame.height
32+
standardDecorationFrame.size.height += sectionModel.allItemsLength(scrollDirection: scrollDirection)
33+
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
34+
standardDecorationFrame.size.height += footer.frame.height
35+
}
36+
} else {
37+
standardDecorationFrame.origin.x = sectionModel.metrics.sectionInset.left
38+
39+
standardDecorationFrame.origin.y = previousSectionTotalLength(currentSection: section) + sectionModel.bodyBeforeLength(scrollDirection: scrollDirection)
40+
41+
standardDecorationFrame.size.width = layout.mCollectionView.bounds.width - sectionModel.metrics.sectionInset.left - sectionModel.metrics.sectionInset.right
42+
43+
standardDecorationFrame.size.height = sectionModel.allItemsLength(scrollDirection: scrollDirection)
44+
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
45+
standardDecorationFrame.size.height += footer.frame.height
46+
}
47+
}
48+
case .horizontal:
49+
if let header = sectionModel.headerModel, sectionModel.metrics.sectionInsetContainHeader {
50+
standardDecorationFrame.origin.x = previousSectionTotalLength(currentSection: section)
51+
standardDecorationFrame.origin.x += sectionModel.metrics.sectionInset.left
52+
53+
standardDecorationFrame.origin.y = sectionModel.metrics.sectionInset.top
54+
55+
standardDecorationFrame.size.height = layout.mCollectionView.bounds.height - sectionModel.metrics.sectionInset.top - sectionModel.metrics.sectionInset.bottom
56+
57+
standardDecorationFrame.size.width = header.frame.width
58+
standardDecorationFrame.size.width += sectionModel.allItemsLength(scrollDirection: scrollDirection)
59+
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
60+
standardDecorationFrame.size.width += footer.frame.width
61+
}
62+
} else {
63+
standardDecorationFrame.origin.x = previousSectionTotalLength(currentSection: section) + sectionModel.bodyBeforeLength(scrollDirection: scrollDirection)
64+
65+
standardDecorationFrame.origin.y = sectionModel.metrics.sectionInset.top
66+
67+
standardDecorationFrame.size.height = layout.mCollectionView.bounds.height - sectionModel.metrics.sectionInset.top - sectionModel.metrics.sectionInset.bottom
68+
69+
standardDecorationFrame.size.width = sectionModel.allItemsLength(scrollDirection: scrollDirection)
70+
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
71+
standardDecorationFrame.size.width += footer.frame.width
72+
}
73+
}
74+
default:
75+
break
76+
}
77+
78+
79+
var compactDecorationFrame: CGRect = .zero
1880
if let header = sectionModel.headerModel {
1981
if sectionModel.metrics.sectionInsetContainHeader {
20-
decorationFrame = header.frame
21-
decorationFrame = sectionModel.itemModels.reduce(decorationFrame, { $0.union($1.frame) })
82+
compactDecorationFrame = header.frame
83+
compactDecorationFrame = sectionModel.itemModels.reduce(compactDecorationFrame, { $0.union($1.frame) })
2284
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
23-
decorationFrame = decorationFrame.union(footer.frame)
85+
compactDecorationFrame = compactDecorationFrame.union(footer.frame)
2486
}
2587
} else {
2688
if sectionModel.itemModels.isEmpty {
2789
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
28-
decorationFrame = footer.frame
90+
compactDecorationFrame = footer.frame
2991
}
3092
} else {
31-
decorationFrame = sectionModel.itemModels.first!.frame
32-
decorationFrame = sectionModel.itemModels.reduce(decorationFrame, { $0.union($1.frame) })
93+
compactDecorationFrame = sectionModel.itemModels.first!.frame
94+
compactDecorationFrame = sectionModel.itemModels.reduce(compactDecorationFrame, { $0.union($1.frame) })
3395
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
34-
decorationFrame = decorationFrame.union(footer.frame)
96+
compactDecorationFrame = compactDecorationFrame.union(footer.frame)
3597
}
3698
}
3799
}
38100
} else {
39101
if sectionModel.itemModels.isEmpty {
40102
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
41-
decorationFrame = footer.frame
103+
compactDecorationFrame = footer.frame
42104
}
43105
} else {
44-
decorationFrame = sectionModel.itemModels.first!.frame
45-
decorationFrame = sectionModel.itemModels.reduce(decorationFrame, { $0.union($1.frame) })
106+
compactDecorationFrame = sectionModel.itemModels.first!.frame
107+
compactDecorationFrame = sectionModel.itemModels.reduce(compactDecorationFrame, { $0.union($1.frame) })
46108
if let footer = sectionModel.footerModel, sectionModel.metrics.sectionInsetContainFooter {
47-
decorationFrame = decorationFrame.union(footer.frame)
109+
compactDecorationFrame = compactDecorationFrame.union(footer.frame)
48110
}
49111
}
50112
}
51113

52-
decorationFrame.origin.x -= decorationModel.extraInset.left
53-
decorationFrame.origin.y -= decorationModel.extraInset.top
54-
decorationFrame.size.width += (decorationModel.extraInset.left + decorationModel.extraInset.right)
55-
decorationFrame.size.height += (decorationModel.extraInset.top + decorationModel.extraInset.bottom)
114+
var finalFrame: CGRect = .zero
115+
if standardDecorationFrame == .zero && compactDecorationFrame != .zero {
116+
finalFrame = compactDecorationFrame
117+
} else if standardDecorationFrame != .zero && compactDecorationFrame == .zero {
118+
finalFrame = standardDecorationFrame
119+
} else if standardDecorationFrame != .zero && compactDecorationFrame != .zero {
120+
finalFrame = standardDecorationFrame.union(compactDecorationFrame)
121+
}
122+
123+
finalFrame.origin.x -= decorationModel.extraInset.left
124+
finalFrame.origin.y -= decorationModel.extraInset.top
125+
finalFrame.size.width += (decorationModel.extraInset.left + decorationModel.extraInset.right)
126+
finalFrame.size.height += (decorationModel.extraInset.top + decorationModel.extraInset.bottom)
56127

57-
decorationModel.frame = decorationFrame
128+
decorationModel.frame = finalFrame
58129
}
59130

60131
internal func decorationLayoutAttributes(at section: Int, frame: CGRect) -> UICollectionViewLayoutAttributes {
61132
let extraAttributes = decorationModel(at: section)?.extraAttributes
62133
let indexPath = IndexPath(item: 0, section: section)
63-
let attr = SwiftyCollectionViewLayoutDecorationAttributes(forDecorationViewOfKind: SwiftyCollectionViewFlowLayout.DecorationElementKind, with: indexPath)
134+
let attr = SwiftyCollectionViewDecorationAttributes(forDecorationViewOfKind: SwiftyCollectionViewFlowLayout.DecorationElementKind, with: indexPath)
64135
attr.extraAttributes = extraAttributes
65136
attr.frame = frame
66137
attr.zIndex = -999

SwiftyCollectionViewFlowLayout/Sources/ModeState+Footer.swift

+34-5
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,55 @@ extension ModeState {
2323

2424
switch scrollDirection {
2525
case .vertical:
26-
frame.origin.x = .zero
2726
frame.origin.y = previousSectionTotalLength + sectionModel.footerBeforeLength(scrollDirection: scrollDirection)
2827
if sectionModel.metrics.sectionInsetContainFooter {
29-
frame.origin.x = sectionModel.metrics.sectionInset.left
3028
frame.origin.y -= sectionModel.metrics.sectionInset.bottom
3129
}
30+
switch sectionModel.metrics.footerDirection {
31+
case .left:
32+
frame.origin.x = .zero
33+
if sectionModel.metrics.sectionInsetContainFooter {
34+
frame.origin.x = sectionModel.metrics.sectionInset.left
35+
}
36+
case .center:
37+
frame.origin.x = (layout.mCollectionView.bounds.width - frame.width) / 2.0
38+
case .right:
39+
frame.origin.x = layout.mCollectionView.bounds.width - frame.width
40+
if sectionModel.metrics.sectionInsetContainFooter {
41+
frame.origin.x = layout.mCollectionView.bounds.width - frame.width - sectionModel.metrics.sectionInset.right
42+
}
43+
}
3244
case .horizontal:
3345
frame.origin.x = previousSectionTotalLength + sectionModel.footerBeforeLength(scrollDirection: scrollDirection)
34-
frame.origin.y = .zero
3546
if sectionModel.metrics.sectionInsetContainFooter {
3647
frame.origin.x -= sectionModel.metrics.sectionInset.right
37-
frame.origin.y = sectionModel.metrics.sectionInset.top
48+
}
49+
switch sectionModel.metrics.footerDirection {
50+
case .left:
51+
frame.origin.y = .zero
52+
if sectionModel.metrics.sectionInsetContainFooter {
53+
frame.origin.y = sectionModel.metrics.sectionInset.top
54+
}
55+
case .center:
56+
frame.origin.y = (layout.mCollectionView.bounds.height - frame.height) / 2.0
57+
case .right:
58+
frame.origin.y = layout.mCollectionView.bounds.height - frame.height
59+
if sectionModel.metrics.sectionInsetContainFooter {
60+
frame.origin.y = layout.mCollectionView.bounds.height - frame.height - sectionModel.metrics.sectionInset.bottom
61+
}
3862
}
3963
default:
4064
break
4165
}
66+
67+
// Offset
68+
frame.origin.x += sectionModel.metrics.footerOffset.horizontal
69+
frame.origin.y += sectionModel.metrics.footerOffset.vertical
70+
4271
footerModel.frame = frame
4372
}
4473

45-
internal func footerLayoutAttributes(at section: Int, frame: CGRect, sectionModel: SectionModel, sizeMode: SwiftyCollectionViewFlowLayoutSizeMode) -> UICollectionViewLayoutAttributes {
74+
internal func footerLayoutAttributes(at section: Int, frame: CGRect, sectionModel: SectionModel, sizeMode: SwiftyCollectionViewLayoutSizeMode) -> UICollectionViewLayoutAttributes {
4675
let indexPath = IndexPath(item: 0, section: section)
4776
let attr = SwiftyCollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, with: indexPath)
4877
attr.sizeMode = sizeMode

0 commit comments

Comments
 (0)