@@ -49,6 +49,8 @@ internal final class TextFieldWidget: WrapperWidget<UITextField>, UITextFieldDel
49
49
}
50
50
}
51
51
52
+ @available ( tvOS, unavailable)
53
+ @available ( macCatalyst, unavailable)
52
54
internal final class PickerWidget : WrapperWidget < UIPickerView > , UIPickerViewDataSource ,
53
55
UIPickerViewDelegate
54
56
{
@@ -76,50 +78,126 @@ internal final class PickerWidget: WrapperWidget<UIPickerView>, UIPickerViewData
76
78
options. count + 1
77
79
}
78
80
79
- func pickerView(
80
- _: UIPickerView ,
81
- titleForRow row: Int ,
82
- forComponent _: Int
83
- ) -> String ? {
84
- switch row {
85
- case 0 :
86
- " "
87
- case 1 ... options. count:
88
- options [ row - 1 ]
89
- default :
90
- nil
81
+ #if os(iOS)
82
+ func pickerView(
83
+ _: UIPickerView ,
84
+ titleForRow row: Int ,
85
+ forComponent _: Int
86
+ ) -> String ? {
87
+ switch row {
88
+ case 0 :
89
+ " "
90
+ case 1 ... options. count:
91
+ options [ row - 1 ]
92
+ default :
93
+ nil
94
+ }
91
95
}
92
- }
93
96
94
- func pickerView(
95
- _: UIPickerView ,
96
- didSelectRow row: Int ,
97
- inComponent _: Int
98
- ) {
99
- onSelect ? ( row > 0 ? row - 1 : nil )
100
- }
97
+ func pickerView(
98
+ _: UIPickerView ,
99
+ didSelectRow row: Int ,
100
+ inComponent _: Int
101
+ ) {
102
+ onSelect ? ( row > 0 ? row - 1 : nil )
103
+ }
104
+ #endif
101
105
}
102
106
103
- internal final class SwitchWidget : WrapperWidget < UISwitch > {
104
- var onChange : ( ( Bool ) -> Void ) ?
107
+ #if os(tvOS)
108
+ internal final class SwitchWidget : WrapperWidget < UISegmentedControl > {
109
+ var onChange : ( ( Bool ) -> Void ) ?
105
110
106
- @objc
107
- func switchFlipped( ) {
108
- onChange ? ( child. isOn)
111
+ @objc
112
+ func switchFlipped( ) {
113
+ onChange ? ( child. selectedSegmentIndex == 1 )
114
+ }
115
+
116
+ init ( ) {
117
+ // TODO: localization?
118
+ super. init (
119
+ child: UISegmentedControl ( items: [
120
+ " OFF " as NSString ,
121
+ " ON " as NSString ,
122
+ ] ) )
123
+
124
+ child. addTarget ( self , action: #selector( switchFlipped) , for: . valueChanged)
125
+ }
126
+
127
+ func setOn( _ on: Bool ) {
128
+ child. selectedSegmentIndex = on ? 1 : 0
129
+ }
109
130
}
131
+ #else
132
+ internal final class SwitchWidget : WrapperWidget < UISwitch > {
133
+ var onChange : ( ( Bool ) -> Void ) ?
110
134
111
- init ( ) {
112
- super. init ( child: UISwitch ( ) )
135
+ @objc
136
+ func switchFlipped( ) {
137
+ onChange ? ( child. isOn)
138
+ }
113
139
114
- // On iOS 14 and later, UISwitch can be either a switch or a checkbox. We have no
115
- // control over this on iOS 13 or any tvOS, but when possible, prefer a switch.
116
- #if os(iOS) || targetEnvironment(macCatalyst)
140
+ init ( ) {
141
+ super. init ( child: UISwitch ( ) )
142
+
143
+ // On iOS 14 and later, UISwitch can be either a switch or a checkbox (and I believe
144
+ // it's a checkbox by default on Mac Catalyst). We have no control over this on
145
+ // iOS 13, but when possible, prefer a switch.
117
146
if #available( iOS 14 , macCatalyst 14 , * ) {
118
147
child. preferredStyle = . sliding
119
148
}
120
- #endif
121
149
122
- child. addTarget ( self , action: #selector( switchFlipped) , for: . valueChanged)
150
+ child. addTarget ( self , action: #selector( switchFlipped) , for: . valueChanged)
151
+ }
152
+
153
+ func setOn( _ on: Bool ) {
154
+ child. setOn ( on, animated: true )
155
+ }
156
+ }
157
+ #endif
158
+
159
+ internal final class ClickableWidget : WrapperWidget < BaseWidget > {
160
+ private var gestureRecognizer : UITapGestureRecognizer !
161
+ var onClick : ( ( ) -> Void ) ?
162
+
163
+ override init ( child: BaseWidget ) {
164
+ super. init ( child: child)
165
+
166
+ gestureRecognizer = UITapGestureRecognizer ( target: self , action: #selector( viewTouched) )
167
+ gestureRecognizer. cancelsTouchesInView = true
168
+ child. addGestureRecognizer ( gestureRecognizer)
169
+ }
170
+
171
+ @objc
172
+ func viewTouched( ) {
173
+ onClick ? ( )
174
+ }
175
+ }
176
+
177
+ @available ( tvOS, unavailable)
178
+ internal final class SliderWidget : WrapperWidget < UISlider > {
179
+ var onChange : ( ( Double ) -> Void ) ?
180
+
181
+ private var _decimalPlaces = 17
182
+ var decimalPlaces : Int {
183
+ get { _decimalPlaces }
184
+ set {
185
+ _decimalPlaces = max ( 0 , min ( newValue, 17 ) )
186
+ }
187
+ }
188
+
189
+ @objc
190
+ func sliderMoved( ) {
191
+ onChange ? (
192
+ ( Double ( child. value) * pow( 10.0 , Double ( decimalPlaces) ) )
193
+ . rounded ( . toNearestOrEven)
194
+ / pow( 10.0 , Double ( decimalPlaces) )
195
+ )
196
+ }
197
+
198
+ init ( ) {
199
+ super. init ( child: UISlider ( ) )
200
+ child. addTarget ( self , action: #selector( sliderMoved) , for: . valueChanged)
123
201
}
124
202
}
125
203
@@ -174,25 +252,28 @@ extension UIKitBackend {
174
252
return textFieldWidget. child. text ?? " "
175
253
}
176
254
177
- public func createPicker( ) -> Widget {
178
- PickerWidget ( )
179
- }
255
+ #if os(iOS)
256
+ public func createPicker( ) -> Widget {
257
+ PickerWidget ( )
258
+ }
180
259
181
- public func updatePicker(
182
- _ picker: Widget ,
183
- options: [ String ] ,
184
- environment: EnvironmentValues ,
185
- onChange: @escaping ( Int ? ) -> Void
186
- ) {
187
- let pickerWidget = picker as! PickerWidget
188
- pickerWidget. onSelect = onChange
189
- pickerWidget. options = options
190
- }
260
+ public func updatePicker(
261
+ _ picker: Widget ,
262
+ options: [ String ] ,
263
+ environment: EnvironmentValues ,
264
+ onChange: @escaping ( Int ? ) -> Void
265
+ ) {
266
+ let pickerWidget = picker as! PickerWidget
267
+ pickerWidget. onSelect = onChange
268
+ pickerWidget. options = options
269
+ }
191
270
192
- public func setSelectedOption( ofPicker picker: Widget , to selectedOption: Int ? ) {
193
- let pickerWidget = picker as! PickerWidget
194
- pickerWidget. child. selectRow ( ( selectedOption ?? - 1 ) + 1 , inComponent: 0 , animated: false )
195
- }
271
+ public func setSelectedOption( ofPicker picker: Widget , to selectedOption: Int ? ) {
272
+ let pickerWidget = picker as! PickerWidget
273
+ pickerWidget. child. selectRow (
274
+ ( selectedOption ?? - 1 ) + 1 , inComponent: 0 , animated: false )
275
+ }
276
+ #endif
196
277
197
278
public func createSwitch( ) -> Widget {
198
279
SwitchWidget ( )
@@ -205,6 +286,43 @@ extension UIKitBackend {
205
286
206
287
public func setState( ofSwitch switchWidget: Widget , to state: Bool ) {
207
288
let wrapper = switchWidget as! SwitchWidget
208
- wrapper. child. setOn ( state, animated: true )
289
+ wrapper. setOn ( state)
290
+ }
291
+
292
+ public func createClickTarget( wrapping child: Widget ) -> Widget {
293
+ ClickableWidget ( child: child)
294
+ }
295
+
296
+ public func updateClickTarget(
297
+ _ clickTarget: Widget ,
298
+ clickHandler handleClick: @escaping ( ) -> Void
299
+ ) {
300
+ let wrapper = clickTarget as! ClickableWidget
301
+ wrapper. onClick = handleClick
209
302
}
303
+
304
+ #if os(iOS) || targetEnvironment(macCatalyst)
305
+ public func createSlider( ) -> Widget {
306
+ SliderWidget ( )
307
+ }
308
+
309
+ public func updateSlider(
310
+ _ slider: Widget ,
311
+ minimum: Double ,
312
+ maximum: Double ,
313
+ decimalPlaces: Int ,
314
+ onChange: @escaping ( Double ) -> Void
315
+ ) {
316
+ let sliderWidget = slider as! SliderWidget
317
+ sliderWidget. child. minimumValue = Float ( minimum)
318
+ sliderWidget. child. maximumValue = Float ( maximum)
319
+ sliderWidget. onChange = onChange
320
+ sliderWidget. decimalPlaces = decimalPlaces
321
+ }
322
+
323
+ public func setValue( ofSlider slider: Widget , to value: Double ) {
324
+ let sliderWidget = slider as! SliderWidget
325
+ sliderWidget. child. setValue ( Float ( value) , animated: true )
326
+ }
327
+ #endif
210
328
}
0 commit comments