main
md 255 lines 7.99 KB
Rendered Raw
1 # JomloCollectionView
2
3 [![Swift 6.0](https://img.shields.io/badge/Swift-6.0-orange.svg)](https://swift.org)
4 [![Platforms](https://img.shields.io/badge/Platforms-iOS%2017%20%7C%20tvOS%2017-blue.svg)](https://developer.apple.com)
5 [![License](https://img.shields.io/badge/License-MIT-lightgrey.svg)](LICENSE)
6
7 JomloCollectionView is a small UIKit package for building section based `UICollectionView` screens on iOS and tvOS.
8
9 It is meant for screens made from mixed sections: a hero banner, a few horizontal shelves, a grid, maybe a list. Each section owns its items, layout, cell configuration, and selection behavior. The view controller stitches those sections into one compositional layout and keeps the diffable data source out of your app code.
10
11 If you have used [JomloTableView](https://github.com/setoelkahfi/JomloTableView), this is the same idea moved to `UICollectionView`, with compositional layouts and Swift 6 concurrency rules.
12
13 ## Screenshots
14
15 <p>
16 <img src="assets/Simulator%20Screenshot%20-%20iPhone%2017%20Pro%20-%202026-05-18%20at%2019.41.17.png" alt="JomloCollectionView example app home screen" width="260">
17 <img src="assets/Simulator%20Screenshot%20-%20iPhone%2017%20Pro%20-%202026-05-18%20at%2019.41.25.png" alt="JomloCollectionView example app shelves and categories" width="260">
18 <img src="assets/Simulator%20Screenshot%20-%20iPhone%2017%20Pro%20-%202026-05-18%20at%2019.41.37.png" alt="JomloCollectionView example app see all screen" width="260">
19 </p>
20
21 ## What it gives you
22
23 - Section objects that define their own layout, items, cells, and selection handling
24 - `UICollectionViewCompositionalLayout` support without one large layout switch in the view controller
25 - A diffable data source managed by `JomloCollectionViewController`
26 - Layout helpers for hero, shelf, grid, list, and banner sections
27 - Self configuring cells through `JomloSelfConfiguringCell`
28 - Optional cell appearance animations
29 - Swift 6 language mode with `Sendable` and `@MainActor` annotations
30 - UIKit only, with no external package dependencies
31
32 ## Requirements
33
34 - iOS 17.0 or later
35 - tvOS 17.0 or later
36 - Swift 6.0 or later
37 - Xcode 16.0 or later
38
39 ## Installation
40
41 ### Swift Package Manager
42
43 Add the package to `Package.swift`:
44
45 ```swift
46 dependencies: [
47 .package(url: "https://github.com/setoelkahfi/JomloCollectionView", from: "1.0.0")
48 ]
49 ```
50
51 In Xcode, use `File > Add Package Dependencies` and paste the repository URL.
52
53 ### CocoaPods
54
55 Add this to your `Podfile`:
56
57 ```ruby
58 pod 'JomloCollectionView', '~> 1.0'
59 ```
60
61 Then run:
62
63 ```bash
64 pod install
65 ```
66
67 ## Quick start
68
69 Define an item:
70
71 ```swift
72 struct MovieItem: JomloItem {
73 let id: String
74 let title: String
75 let posterColor: String
76
77 nonisolated var itemIdentifier: String { id }
78 }
79 ```
80
81 Create a cell that knows how to render that item:
82
83 ```swift
84 final class MovieCell: UICollectionViewCell, JomloSelfConfiguringCell {
85 private let titleLabel = UILabel()
86
87 func configure(with item: MovieItem) {
88 titleLabel.text = item.title
89 }
90 }
91 ```
92
93 Put the layout and cell wiring in a section:
94
95 ```swift
96 @MainActor
97 final class MoviesShelfSection: JomloSection {
98 let identifier = JomloSectionID("movies")
99 var onReloadNeeded: (@MainActor @Sendable () -> Void)?
100
101 private let movies: [MovieItem]
102
103 init(movies: [MovieItem]) {
104 self.movies = movies
105 }
106
107 func items() -> [any JomloItem] {
108 movies
109 }
110
111 func layoutSection(
112 environment: NSCollectionLayoutEnvironment,
113 sectionIndex: Int
114 ) -> NSCollectionLayoutSection {
115 JomloLayoutFactory.horizontalShelf()
116 }
117
118 func cell(
119 for item: any JomloItem,
120 in collectionView: UICollectionView,
121 at indexPath: IndexPath
122 ) -> UICollectionViewCell {
123 guard let movie = item as? MovieItem else {
124 return UICollectionViewCell()
125 }
126
127 return collectionView.dequeueJomloCell(MovieCell.self, for: indexPath, item: movie)
128 }
129 }
130 ```
131
132 Then build the screen from sections:
133
134 ```swift
135 final class MoviesViewController: JomloCollectionViewController {
136 override func viewDidLoad() {
137 super.viewDidLoad()
138 collectionView.registerJomloCell(MovieCell.self)
139 }
140 }
141
142 let viewController = MoviesViewController(sections: [
143 heroSection,
144 continueWatchingSection,
145 moviesShelfSection,
146 categoriesGridSection,
147 ])
148 ```
149
150 `JomloCollectionViewController` owns the collection view, builds the compositional layout, applies snapshots, and forwards selection back to the section.
151
152 ## How the pieces fit
153
154 ```text
155 JomloCollectionViewController
156 - UICollectionViewCompositionalLayout
157 - UICollectionViewDiffableDataSource
158
159 Sections
160 - provide items
161 - build layout sections
162 - dequeue and configure cells
163 - handle selection
164
165 Items
166 - provide stable identifiers for diffing
167 - carry the data needed by cells
168 ```
169
170 ## Core types
171
172 | Type | What it does |
173 |------|--------------|
174 | `JomloSection` | Defines one section's items, layout, cells, supplementary views, selection, and reload callback |
175 | `JomloItem` | A `Hashable` and `Sendable` view model with a stable `itemIdentifier` |
176 | `JomloSelfConfiguringCell` | Lets a cell configure itself from one item type |
177 | `JomloCollectionViewController` | Hosts the collection view, compositional layout, and diffable data source |
178 | `JomloLayoutFactory` | Creates common compositional layout sections |
179 | `JomloAnimator` | Runs optional appearance animations as cells are displayed |
180 | `JomloSectionHeaderView` | A reusable section header with a title and action button |
181
182 ## Layout presets
183
184 | Preset | Use it for |
185 |--------|------------|
186 | `.hero()` | A wide featured item with optional paging |
187 | `.horizontalShelf()` | A horizontally scrolling row of cards |
188 | `.grid(columns:)` | A multi column grid |
189 | `.list()` | A single column vertical list |
190 | `.banner()` | A full width banner section |
191
192 ## Animations
193
194 | Animation | Effect |
195 |-----------|--------|
196 | `.fadeIn()` | Fades cells in with a staggered delay |
197 | `.moveUpWithFade()` | Moves cells up while fading them in |
198 | `.scaleUp()` | Scales cells from a smaller size while fading them in |
199
200 You can set an animation per section with `displayAnimation`, or set a shared `JomloAnimator` on the view controller.
201
202 ## Example app
203
204 The `Example/` directory contains an Xcode project with iOS and tvOS targets. It builds a streaming style home screen with:
205
206 - A featured hero section
207 - Continue watching shelves with progress indicators
208 - Poster shelves
209 - A categories grid
210 - Section headers with a "See All" action
211
212 To run it:
213
214 ```bash
215 cd Example
216 open JomloCollectionViewExample.xcodeproj
217 ```
218
219 Then choose one of these schemes:
220
221 - `JomloCV Example iOS` for an iPhone or iPad simulator
222 - `JomloCV Example tvOS` for an Apple TV simulator
223
224 Most cells, models, sections, and view controllers are shared between the targets. The platform specific app entry points live in their own target folders.
225
226 ## Moving from JomloTableView
227
228 | JomloTableView | JomloCollectionView |
229 |----------------|---------------------|
230 | `JomloTableViewSection` | `JomloSection` |
231 | `JomloTableViewRow` | `JomloItem` |
232 | `JomloTableViewCell` | `JomloSelfConfiguringCell` |
233 | `JomloTableView` | `JomloCollectionViewController` |
234 | `populateView(cell:)` | `configure(with:)` |
235 | `addSection(section:)` | `setSections([...])` or `appendSection(_:)` |
236 | Class inheritance | Protocol composition |
237 | `UITableView` | `UICollectionViewCompositionalLayout` |
238
239 ## Design notes
240
241 The package works best when each section is treated as a small, reusable unit. A section should know how to lay itself out and how to render its items. The screen should only decide which sections appear and in what order.
242
243 That keeps the view controller thin, and it makes common product screens easier to change. You can reuse a shelf section with different data, swap a grid from two columns to four, or map server returned section types into local section objects.
244
245 ## Author
246
247 Seto Elkahfi - [@setoelkahfi](https://github.com/setoelkahfi)
248
249 ## License
250
251 JomloCollectionView is available under the MIT license. See [LICENSE](LICENSE) for details.
252
253 ## Copyright
254
255 2026 Onde Inference (Splitfire AB)