feature/v1
swift 83 lines 2.73 KB
Raw
1 //
2 // TVSectionHeaderView.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 UIKit
10
11 /// A section header designed for TV — large, bold title with an optional focusable "See All" button.
12 ///
13 /// On tvOS, the "See All" button is a proper `UIButton` that participates in the
14 /// focus engine. It receives the standard tvOS focus styling automatically.
15 final class TVSectionHeaderView: UICollectionReusableView {
16
17 static let reuseIdentifier = "TVSectionHeaderView"
18
19 private let titleLabel: UILabel = {
20 let label = UILabel()
21 label.font = .systemFont(ofSize: 34, weight: .bold)
22 label.textColor = .white
23 return label
24 }()
25
26 private let seeAllButton: UIButton = {
27 let button = UIButton(type: .system)
28 button.setTitle("See All", for: .normal)
29 button.titleLabel?.font = .systemFont(ofSize: 26, weight: .medium)
30 button.setTitleColor(UIColor.white.withAlphaComponent(0.7), for: .normal)
31 button.isHidden = true
32 // tvOS focus engine will automatically apply a scale + shadow on focus
33 button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
34 return button
35 }()
36
37 private var onSeeAllTapped: (@MainActor @Sendable () -> Void)?
38
39 override init(frame: CGRect) {
40 super.init(frame: frame)
41 setupViews()
42 }
43
44 required init?(coder: NSCoder) {
45 fatalError("init(coder:) not supported")
46 }
47
48 /// Configures the header.
49 ///
50 /// - Parameters:
51 /// - title: The section title.
52 /// - action: Optional closure called when "See All" is selected. Pass `nil` to hide the button.
53 func configure(title: String, action: (@MainActor @Sendable () -> Void)? = nil) {
54 titleLabel.text = title
55 if let action {
56 onSeeAllTapped = action
57 seeAllButton.isHidden = false
58 } else {
59 onSeeAllTapped = nil
60 seeAllButton.isHidden = true
61 }
62 }
63
64 private func setupViews() {
65 let stack = UIStackView(arrangedSubviews: [titleLabel, UIView(), seeAllButton])
66 stack.axis = .horizontal
67 stack.alignment = .center
68 stack.translatesAutoresizingMaskIntoConstraints = false
69
70 addSubview(stack)
71 NSLayoutConstraint.activate([
72 stack.leadingAnchor.constraint(equalTo: leadingAnchor),
73 stack.trailingAnchor.constraint(equalTo: trailingAnchor),
74 stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
75 ])
76
77 seeAllButton.addTarget(self, action: #selector(seeAllPressed), for: .primaryActionTriggered)
78 }
79
80 @objc private func seeAllPressed() {
81 onSeeAllTapped?()
82 }
83 }