1import ExpoModulesCore
2import UIKit
3
4final class T3SegmentedControlView: ExpoView {
5 let onSelectionChange = EventDispatcher()
6 private let control = UISegmentedControl()
7 var labels: [String] = [] {
8 didSet {
9 guard labels != oldValue else { return }
10 control.removeAllSegments()
11 for (index, label) in labels.enumerated() {
12 control.insertSegment(withTitle: label, at: index, animated: false)
13 }
14 updateSelection()
15 }
16 }
17 var selectedIndex = -1 { didSet { updateSelection() } }
18 var trackColor: UIColor = .secondarySystemBackground { didSet { if trackColor != oldValue { control.backgroundColor = trackColor } } }
19 var selectedColor: UIColor = .secondarySystemFill { didSet { if selectedColor != oldValue { control.selectedSegmentTintColor = selectedColor } } }
20 var textColor: UIColor = .label { didSet { if textColor != oldValue { updateColors() } } }
21 var mutedColor: UIColor = .secondaryLabel { didSet { if mutedColor != oldValue { updateColors() } } }
22 var appearance = "light" {
23 didSet {
24 guard appearance != oldValue else { return }
25 overrideUserInterfaceStyle = appearance == "dark" ? .dark : .light
26 }
27 }
28
29 required init(appContext: AppContext? = nil) {
30 super.init(appContext: appContext)
31 addSubview(control)
32 control.addTarget(self, action: #selector(selectionChanged), for: .valueChanged)
33 }
34
35 override func layoutSubviews() {
36 super.layoutSubviews()
37 guard control.frame != bounds else { return }
38 UIView.performWithoutAnimation {
39 control.frame = bounds
40 }
41 }
42
43 private func updateSelection() {
44 let index = labels.indices.contains(selectedIndex) ? selectedIndex : UISegmentedControl.noSegment
45 // React echoes native selections while UIKit is still settling the glass lens.
46 guard control.selectedSegmentIndex != index else { return }
47 UIView.performWithoutAnimation {
48 control.selectedSegmentIndex = index
49 control.layoutIfNeeded()
50 }
51 }
52
53 private func updateColors() {
54 control.setTitleTextAttributes([.foregroundColor: mutedColor], for: .normal)
55 control.setTitleTextAttributes([.foregroundColor: textColor], for: .selected)
56 }
57
58 @objc private func selectionChanged() {
59 onSelectionChange(["index": control.selectedSegmentIndex])
60 }
61}