main
swift 417 lines 17.6 KB
Raw
1 //
2 // TVTabViewControllers.swift
3 // JomloCV tvOS Example
4 //
5 // Created by Seto Elkahfi on 2026-01-27.
6 // Copyright © 2026 Seto Elkahfi. All rights reserved.
7 //
8
9 import JomloCollectionView
10 import UIKit
11
12 // MARK: - Series Tab
13
14 /// The "Series" tab — shows popular and new series shelves.
15 @MainActor
16 final class TVSeriesViewController: JomloCollectionViewController {
17
18 init() {
19 super.init(sections: [])
20 }
21
22 override func viewDidLoad() {
23 super.viewDidLoad()
24 view.backgroundColor = UIColor(white: 0.05, alpha: 1.0)
25 registerCells()
26 loadContent()
27 }
28
29 private func registerCells() {
30 collectionView.registerJomloCell(TVPosterCell.self)
31 collectionView.registerJomloCell(TVSeeAllCell.self)
32 collectionView.register(
33 TVSectionHeaderView.self,
34 forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
35 withReuseIdentifier: TVSectionHeaderView.reuseIdentifier
36 )
37 collectionView.clipsToBounds = false
38 collectionView.remembersLastFocusedIndexPath = true
39 }
40
41 private func loadContent() {
42 setSections(
43 [
44 TVShelfSection(
45 id: "series-popular", title: "Popular Series",
46 items: [
47 TVContentItem(
48 id: "sp-1", title: "The Bear", subtitle: "2022\u{2013} \u{2022} Comedy",
49 colorHex: "#e65100", isFeatured: true),
50 TVContentItem(
51 id: "sp-2", title: "Slow Horses", subtitle: "2022\u{2013} \u{2022} Spy",
52 colorHex: "#1a237e", isFeatured: false),
53 TVContentItem(
54 id: "sp-3", title: "Severance",
55 subtitle: "2022\u{2013} \u{2022} Sci-Fi", colorHex: "#004d40",
56 isFeatured: false),
57 TVContentItem(
58 id: "sp-4", title: "Shogun", subtitle: "2024 \u{2022} Historical",
59 colorHex: "#b71c1c", isFeatured: true),
60 TVContentItem(
61 id: "sp-5", title: "True Detective", subtitle: "2024 \u{2022} Crime",
62 colorHex: "#311b92", isFeatured: false),
63 TVContentItem(
64 id: "sp-6", title: "Fallout", subtitle: "2024 \u{2022} Sci-Fi",
65 colorHex: "#33691e", isFeatured: false),
66 ],
67
68 presenter: self),
69 TVShelfSection(
70 id: "series-new", title: "New Episodes This Week",
71 items: [
72 TVContentItem(
73 id: "sn-1", title: "House of\nthe Dragon",
74 subtitle: "S2 \u{2022} Fantasy", colorHex: "#880e4f", isFeatured: true),
75 TVContentItem(
76 id: "sn-2", title: "The Diplomat", subtitle: "S2 \u{2022} Thriller",
77 colorHex: "#1565c0", isFeatured: false),
78 TVContentItem(
79 id: "sn-3", title: "Industry", subtitle: "S3 \u{2022} Drama",
80 colorHex: "#37474f", isFeatured: false),
81 TVContentItem(
82 id: "sn-4", title: "Bad Sisters", subtitle: "S2 \u{2022} Comedy",
83 colorHex: "#2e7d32", isFeatured: false),
84 TVContentItem(
85 id: "sn-5", title: "Reacher", subtitle: "S3 \u{2022} Action",
86 colorHex: "#4e342e", isFeatured: false),
87 ],
88
89 presenter: self),
90 TVShelfSection(
91 id: "series-scandi", title: "Scandinavian Originals",
92 items: [
93 TVContentItem(
94 id: "sc-1", title: "The Bridge", subtitle: "Crime \u{2022} Thriller",
95 colorHex: "#455a64", isFeatured: false),
96 TVContentItem(
97 id: "sc-2", title: "Borgen", subtitle: "Political \u{2022} Drama",
98 colorHex: "#00695c", isFeatured: false),
99 TVContentItem(
100 id: "sc-3", title: "Beforeigners", subtitle: "Sci-Fi \u{2022} Crime",
101 colorHex: "#283593", isFeatured: true),
102 TVContentItem(
103 id: "sc-4", title: "The Rain", subtitle: "Sci-Fi \u{2022} Drama",
104 colorHex: "#1b5e20", isFeatured: false),
105 TVContentItem(
106 id: "sc-5", title: "Kamikaze", subtitle: "Drama", colorHex: "#6a1b9a",
107 isFeatured: false),
108 ],
109
110 presenter: self),
111 ], animated: false)
112 }
113 }
114
115 // MARK: - Movies Tab
116
117 /// The "Movies" tab — shows featured films and categories.
118 @MainActor
119 final class TVMoviesViewController: JomloCollectionViewController {
120
121 init() {
122 super.init(sections: [])
123 }
124
125 override func viewDidLoad() {
126 super.viewDidLoad()
127 view.backgroundColor = UIColor(white: 0.05, alpha: 1.0)
128 registerCells()
129 loadContent()
130 }
131
132 private func registerCells() {
133 collectionView.registerJomloCell(TVPosterCell.self)
134 collectionView.registerJomloCell(TVSeeAllCell.self)
135 collectionView.register(
136 TVSectionHeaderView.self,
137 forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
138 withReuseIdentifier: TVSectionHeaderView.reuseIdentifier
139 )
140 collectionView.clipsToBounds = false
141 collectionView.remembersLastFocusedIndexPath = true
142 }
143
144 private func loadContent() {
145 setSections(
146 [
147 TVShelfSection(
148 id: "movies-new", title: "New Releases",
149 items: [
150 TVContentItem(
151 id: "mn-1", title: "Dune:\nPart Two", subtitle: "2024 \u{2022} Sci-Fi",
152 colorHex: "#ff8f00", isFeatured: true),
153 TVContentItem(
154 id: "mn-2", title: "Civil War", subtitle: "2024 \u{2022} Action",
155 colorHex: "#455a64", isFeatured: false),
156 TVContentItem(
157 id: "mn-3", title: "Challengers", subtitle: "2024 \u{2022} Drama",
158 colorHex: "#00695c", isFeatured: false),
159 TVContentItem(
160 id: "mn-4", title: "Furiosa", subtitle: "2024 \u{2022} Action",
161 colorHex: "#795548", isFeatured: false),
162 TVContentItem(
163 id: "mn-5", title: "Inside Out 2", subtitle: "2024 \u{2022} Animation",
164 colorHex: "#7b1fa2", isFeatured: true),
165 ],
166
167 presenter: self),
168 TVShelfSection(
169 id: "movies-acclaimed", title: "Critically Acclaimed",
170 items: [
171 TVContentItem(
172 id: "ma-1", title: "Oppenheimer", subtitle: "2023 \u{2022} Drama",
173 colorHex: "#ff6f00", isFeatured: true),
174 TVContentItem(
175 id: "ma-2", title: "Past Lives", subtitle: "2023 \u{2022} Romance",
176 colorHex: "#00838f", isFeatured: false),
177 TVContentItem(
178 id: "ma-3", title: "Anatomy\nof a Fall",
179 subtitle: "2023 \u{2022} Thriller", colorHex: "#283593",
180 isFeatured: false),
181 TVContentItem(
182 id: "ma-4", title: "The Holdovers", subtitle: "2023 \u{2022} Comedy",
183 colorHex: "#2e7d32", isFeatured: false),
184 TVContentItem(
185 id: "ma-5", title: "Poor Things", subtitle: "2023 \u{2022} Fantasy",
186 colorHex: "#ad1457", isFeatured: true),
187 TVContentItem(
188 id: "ma-6", title: "Zone of\nInterest", subtitle: "2023 \u{2022} Drama",
189 colorHex: "#37474f", isFeatured: false),
190 ],
191
192 presenter: self),
193 TVShelfSection(
194 id: "movies-family", title: "Family Movies",
195 items: [
196 TVContentItem(
197 id: "mf-1", title: "Wish", subtitle: "2023 \u{2022} Animation",
198 colorHex: "#4527a0", isFeatured: false),
199 TVContentItem(
200 id: "mf-2", title: "Migration", subtitle: "2023 \u{2022} Animation",
201 colorHex: "#00838f", isFeatured: false),
202 TVContentItem(
203 id: "mf-3", title: "Wonka", subtitle: "2023 \u{2022} Fantasy",
204 colorHex: "#6a1b9a", isFeatured: true),
205 TVContentItem(
206 id: "mf-4", title: "Elemental", subtitle: "2023 \u{2022} Animation",
207 colorHex: "#e65100", isFeatured: false),
208 ],
209
210 presenter: self),
211 ], animated: false)
212 }
213 }
214
215 // MARK: - Sports Tab
216
217 /// The "Sport" tab — shows live and upcoming sports content.
218 @MainActor
219 final class TVSportsViewController: JomloCollectionViewController {
220
221 init() {
222 super.init(sections: [])
223 }
224
225 override func viewDidLoad() {
226 super.viewDidLoad()
227 view.backgroundColor = UIColor(white: 0.05, alpha: 1.0)
228 registerCells()
229 loadContent()
230 }
231
232 private func registerCells() {
233 collectionView.registerJomloCell(TVPosterCell.self)
234 collectionView.registerJomloCell(TVSeeAllCell.self)
235 collectionView.register(
236 TVSectionHeaderView.self,
237 forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
238 withReuseIdentifier: TVSectionHeaderView.reuseIdentifier
239 )
240 collectionView.clipsToBounds = false
241 collectionView.remembersLastFocusedIndexPath = true
242 }
243
244 private func loadContent() {
245 setSections(
246 [
247 TVShelfSection(
248 id: "sports-live", title: "Live Now",
249 items: [
250 TVContentItem(
251 id: "sl-1", title: "Premier\nLeague",
252 subtitle: "Live \u{2022} Football", colorHex: "#1a237e",
253 isFeatured: true),
254 TVContentItem(
255 id: "sl-2", title: "Handball\nChampions",
256 subtitle: "Live \u{2022} Handball", colorHex: "#4a148c",
257 isFeatured: false),
258 ],
259
260 presenter: self),
261 TVShelfSection(
262 id: "sports-upcoming", title: "Upcoming",
263 items: [
264 TVContentItem(
265 id: "su-1", title: "Champions\nLeague",
266 subtitle: "Tomorrow \u{2022} Football", colorHex: "#01579b",
267 isFeatured: false),
268 TVContentItem(
269 id: "su-2", title: "Formula 1", subtitle: "Sunday \u{2022} Racing",
270 colorHex: "#b71c1c", isFeatured: true),
271 TVContentItem(
272 id: "su-3", title: "NHL", subtitle: "Tonight \u{2022} Ice Hockey",
273 colorHex: "#1b5e20", isFeatured: false),
274 TVContentItem(
275 id: "su-4", title: "UFC 310", subtitle: "Saturday \u{2022} MMA",
276 colorHex: "#880e4f", isFeatured: false),
277 ],
278
279 presenter: self),
280 TVShelfSection(
281 id: "sports-replays", title: "Replays & Highlights",
282 items: [
283 TVContentItem(
284 id: "sr-1", title: "Match of\nthe Day", subtitle: "Football",
285 colorHex: "#2e7d32", isFeatured: false),
286 TVContentItem(
287 id: "sr-2", title: "F1 Highlights", subtitle: "Racing",
288 colorHex: "#c62828", isFeatured: false),
289 TVContentItem(
290 id: "sr-3", title: "Golf\nMajors", subtitle: "Golf",
291 colorHex: "#33691e", isFeatured: false),
292 TVContentItem(
293 id: "sr-4", title: "Biathlon\nWorld Cup", subtitle: "Winter Sport",
294 colorHex: "#0d47a1", isFeatured: false),
295 ],
296
297 presenter: self),
298 ], animated: false)
299 }
300 }
301
302 // MARK: - Kids Tab
303
304 /// The "Kids" tab — shows children's content.
305 @MainActor
306 final class TVKidsViewController: JomloCollectionViewController {
307
308 init() {
309 super.init(sections: [])
310 }
311
312 override func viewDidLoad() {
313 super.viewDidLoad()
314 view.backgroundColor = UIColor(white: 0.08, alpha: 1.0)
315 registerCells()
316 loadContent()
317 }
318
319 private func registerCells() {
320 collectionView.registerJomloCell(TVPosterCell.self)
321 collectionView.registerJomloCell(TVSeeAllCell.self)
322 collectionView.register(
323 TVSectionHeaderView.self,
324 forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
325 withReuseIdentifier: TVSectionHeaderView.reuseIdentifier
326 )
327 collectionView.clipsToBounds = false
328 collectionView.remembersLastFocusedIndexPath = true
329 }
330
331 private func loadContent() {
332 setSections(
333 [
334 TVShelfSection(
335 id: "kids-popular", title: "Popular with Kids",
336 items: [
337 TVContentItem(
338 id: "kp-1", title: "Peppa Pig", subtitle: "Animation",
339 colorHex: "#e91e63", isFeatured: true),
340 TVContentItem(
341 id: "kp-2", title: "Paw Patrol", subtitle: "Animation",
342 colorHex: "#1565c0", isFeatured: false),
343 TVContentItem(
344 id: "kp-3", title: "Bluey", subtitle: "Animation", colorHex: "#0097a7",
345 isFeatured: true),
346 TVContentItem(
347 id: "kp-4", title: "CoComelon", subtitle: "Animation",
348 colorHex: "#f57f17", isFeatured: false),
349 TVContentItem(
350 id: "kp-5", title: "Lego Friends", subtitle: "Animation",
351 colorHex: "#7b1fa2", isFeatured: false),
352 ],
353
354 presenter: self),
355 TVShelfSection(
356 id: "kids-movies", title: "Kids Movies",
357 items: [
358 TVContentItem(
359 id: "km-1", title: "Frozen II", subtitle: "2019 \u{2022} Animation",
360 colorHex: "#4fc3f7", isFeatured: true),
361 TVContentItem(
362 id: "km-2", title: "Moana", subtitle: "2016 \u{2022} Animation",
363 colorHex: "#00897b", isFeatured: false),
364 TVContentItem(
365 id: "km-3", title: "Encanto", subtitle: "2021 \u{2022} Animation",
366 colorHex: "#ff8f00", isFeatured: false),
367 TVContentItem(
368 id: "km-4", title: "Luca", subtitle: "2021 \u{2022} Animation",
369 colorHex: "#1565c0", isFeatured: false),
370 ],
371
372 presenter: self),
373 ], animated: false)
374 }
375 }
376
377 // MARK: - Search Tab
378
379 /// The "Search" tab — placeholder for search functionality.
380 @MainActor
381 final class TVSearchViewController: UIViewController {
382
383 private let messageLabel: UILabel = {
384 let label = UILabel()
385 label.text = "Search"
386 label.font = .systemFont(ofSize: 42, weight: .medium)
387 label.textColor = UIColor.white.withAlphaComponent(0.6)
388 label.textAlignment = .center
389 return label
390 }()
391
392 private let subtitleLabel: UILabel = {
393 let label = UILabel()
394 label.text = "Type to search for movies, series, and sport"
395 label.font = .systemFont(ofSize: 28, weight: .regular)
396 label.textColor = UIColor.white.withAlphaComponent(0.4)
397 label.textAlignment = .center
398 return label
399 }()
400
401 override func viewDidLoad() {
402 super.viewDidLoad()
403 view.backgroundColor = UIColor(white: 0.05, alpha: 1.0)
404
405 let stack = UIStackView(arrangedSubviews: [messageLabel, subtitleLabel])
406 stack.axis = .vertical
407 stack.spacing = 16
408 stack.alignment = .center
409 stack.translatesAutoresizingMaskIntoConstraints = false
410
411 view.addSubview(stack)
412 NSLayoutConstraint.activate([
413 stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
414 stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
415 ])
416 }
417 }