main
swift 91 lines 2.92 KB
Raw
1 //
2 // TVResumeSection.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 /// A "Continue Watching" shelf with wide landscape cards showing progress.
13 @MainActor
14 final class TVResumeSection: JomloSection {
15
16 let identifier = JomloSectionID("tv-resume")
17 var onReloadNeeded: (@MainActor @Sendable () -> Void)?
18
19 private let resumeItems: [TVResumeItem]
20
21 init(items: [TVResumeItem]) {
22 self.resumeItems = items
23 }
24
25 func items() -> [any JomloItem] {
26 resumeItems
27 }
28
29 func layoutSection(
30 environment: NSCollectionLayoutEnvironment,
31 sectionIndex: Int
32 ) -> NSCollectionLayoutSection {
33 // Wide landscape cards — 16:9 thumbnails
34 let itemSize = NSCollectionLayoutSize(
35 widthDimension: .fractionalWidth(1.0),
36 heightDimension: .fractionalHeight(1.0)
37 )
38 let item = NSCollectionLayoutItem(layoutSize: itemSize)
39
40 let groupSize = NSCollectionLayoutSize(
41 widthDimension: .absolute(380),
42 heightDimension: .absolute(320)
43 )
44 let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
45
46 let section = NSCollectionLayoutSection(group: group)
47 section.orthogonalScrollingBehavior = .continuous
48 section.interGroupSpacing = 40
49 section.contentInsets = NSDirectionalEdgeInsets(
50 top: 10, leading: 80, bottom: 50, trailing: 80)
51
52 let headerSize = NSCollectionLayoutSize(
53 widthDimension: .fractionalWidth(1.0),
54 heightDimension: .absolute(60)
55 )
56 let header = NSCollectionLayoutBoundarySupplementaryItem(
57 layoutSize: headerSize,
58 elementKind: UICollectionView.elementKindSectionHeader,
59 alignment: .top
60 )
61 section.boundarySupplementaryItems = [header]
62
63 return section
64 }
65
66 func cell(
67 for item: any JomloItem,
68 in collectionView: UICollectionView,
69 at indexPath: IndexPath
70 ) -> UICollectionViewCell {
71 guard let resumeItem = item as? TVResumeItem else { return UICollectionViewCell() }
72 return collectionView.dequeueJomloCell(TVResumeCell.self, for: indexPath, item: resumeItem)
73 }
74
75 func supplementaryView(
76 ofKind kind: String,
77 in collectionView: UICollectionView,
78 at indexPath: IndexPath
79 ) -> UICollectionReusableView? {
80 guard kind == UICollectionView.elementKindSectionHeader else { return nil }
81 guard
82 let header = collectionView.dequeueReusableSupplementaryView(
83 ofKind: kind,
84 withReuseIdentifier: TVSectionHeaderView.reuseIdentifier,
85 for: indexPath
86 ) as? TVSectionHeaderView
87 else { return nil }
88 header.configure(title: "Continue Watching")
89 return header
90 }
91 }