forked from LoopKit/LoopKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAddEditOverrideTableViewController.swift
More file actions
709 lines (620 loc) · 28.5 KB
/
AddEditOverrideTableViewController.swift
File metadata and controls
709 lines (620 loc) · 28.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
//
// AddEditOverrideTableViewController.swift
// Loop
//
// Created by Michael Pangburn on 1/2/19.
// Copyright © 2019 LoopKit Authors. All rights reserved.
//
import UIKit
import HealthKit
import LoopKit
public protocol AddEditOverrideTableViewControllerDelegate: AnyObject {
func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryScheduleOverridePreset)
func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSaveOverride override: TemporaryScheduleOverride)
func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didCancelOverride override: TemporaryScheduleOverride)
}
// MARK: - Default Implementations
extension AddEditOverrideTableViewControllerDelegate {
public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryScheduleOverridePreset) { }
public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSaveOverride override: TemporaryScheduleOverride) { }
public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didCancelOverride override: TemporaryScheduleOverride) { }
}
private extension TimeInterval {
static let defaultOverrideDuration: TimeInterval = .hours(1)
}
public final class AddEditOverrideTableViewController: UITableViewController {
// MARK: - Public configuration API
public enum InputMode {
case newPreset // Creating a new preset
case editPreset(TemporaryScheduleOverridePreset) // Editing an existing preset
case customizePresetOverride(TemporaryScheduleOverridePreset) // Defining an override relative to an existing preset
case customOverride // Defining a one-off custom override
case editOverride(TemporaryScheduleOverride) // Editing an active override
case viewOverride(TemporaryScheduleOverride) // Viewing an override
}
public enum DismissalMode {
case dismissModal
case popViewController
}
public var inputMode: InputMode = .newPreset {
didSet {
switch inputMode {
case .newPreset:
symbol = nil
name = nil
targetRange = nil
insulinNeedsScaleFactor = 1.0
duration = .finite(.defaultOverrideDuration)
case .editPreset(let preset), .customizePresetOverride(let preset):
symbol = preset.symbol
name = preset.name
configure(with: preset.settings)
duration = preset.duration
case .customOverride:
symbol = nil
name = nil
targetRange = nil
insulinNeedsScaleFactor = 1.0
startDate = Date()
duration = .finite(.defaultOverrideDuration)
case .editOverride(let override):
if case .preset(let preset) = override.context {
symbol = preset.symbol
name = preset.name
} else {
symbol = nil
name = nil
}
configure(with: override.settings)
startDate = override.startDate
duration = override.duration
enactTrigger = override.enactTrigger
syncIdentifier = override.syncIdentifier
case .viewOverride(let override):
if case .preset(let preset) = override.context {
symbol = preset.symbol
name = preset.name
} else {
symbol = nil
name = nil
}
configure(with: override.settings)
startDate = override.startDate
duration = override.duration
syncIdentifier = override.syncIdentifier
}
}
}
public var customDismissalMode: DismissalMode?
public weak var delegate: AddEditOverrideTableViewControllerDelegate?
// MARK: - Override properties
private let glucoseUnit: HKUnit
private var symbol: String? { didSet { updateSaveButtonEnabled() } }
private var name: String? { didSet { updateSaveButtonEnabled() } }
private var targetRange: DoubleRange? { didSet { updateSaveButtonEnabled() } }
private var insulinNeedsScaleFactor = 1.0 { didSet { updateSaveButtonEnabled() }}
private var startDate = Date()
private var duration: TemporaryScheduleOverride.Duration = .finite(.defaultOverrideDuration)
private var enactTrigger: TemporaryScheduleOverride.EnactTrigger = .local
private var syncIdentifier = UUID()
private var isConfiguringPreset: Bool {
switch inputMode {
case .newPreset, .editPreset:
return true
case .customizePresetOverride, .customOverride, .editOverride, .viewOverride:
return false
}
}
private func configure(with settings: TemporaryScheduleOverrideSettings) {
if let targetRange = settings.targetRange {
self.targetRange = DoubleRange(minValue: targetRange.lowerBound.doubleValue(for: glucoseUnit), maxValue: targetRange.upperBound.doubleValue(for: glucoseUnit))
} else {
self.targetRange = nil
}
insulinNeedsScaleFactor = settings.effectiveInsulinNeedsScaleFactor
}
// MARK: - Initialization & view life cycle
public init(glucoseUnit: HKUnit) {
self.glucoseUnit = glucoseUnit
super.init(style: .grouped)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func viewDidLoad() {
super.viewDidLoad()
setupTitle()
setupBarButtonItems()
tableView.register(LabeledTextFieldTableViewCell.nib(), forCellReuseIdentifier: LabeledTextFieldTableViewCell.className)
tableView.register(DoubleRangeTableViewCell.nib(), forCellReuseIdentifier: DoubleRangeTableViewCell.className)
tableView.register(DecimalTextFieldTableViewCell.nib(), forCellReuseIdentifier: DecimalTextFieldTableViewCell.className)
tableView.register(InsulinSensitivityScalingTableViewCell.nib(), forCellReuseIdentifier: InsulinSensitivityScalingTableViewCell.className)
tableView.register(DateAndDurationTableViewCell.nib(), forCellReuseIdentifier: DateAndDurationTableViewCell.className)
tableView.register(SwitchTableViewCell.self, forCellReuseIdentifier: SwitchTableViewCell.className)
tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className)
}
// MARK: - UITableViewDataSource
private enum Section: Int, CaseIterable {
case properties = 0
case cancel
}
private enum PropertyRow: Int, CaseIterable {
case symbol
case name
case insulinNeeds
case targetRange
case startDate
case endDate
case durationFiniteness
case duration
}
private var propertyRows: [PropertyRow] {
var rows: [PropertyRow] = {
if isConfiguringPreset {
return [.symbol, .name, .insulinNeeds, .targetRange, .durationFiniteness]
} else if case let .viewOverride(override) = inputMode, override.hasFinished() {
return [.insulinNeeds, .targetRange, .startDate, .endDate]
} else {
return [.insulinNeeds, .targetRange, .startDate, .durationFiniteness]
}
}()
if duration.isFinite {
rows.append(.duration)
}
rows.sort(by: { $0.rawValue < $1.rawValue })
return rows
}
private func propertyRow(for indexPath: IndexPath) -> PropertyRow {
return propertyRows[indexPath.row]
}
private func indexPath(for row: PropertyRow) -> IndexPath? {
guard let rowIndex = propertyRows.firstIndex(of: row) else {
return nil
}
return IndexPath(row: rowIndex, section: 0)
}
public override func numberOfSections(in tableView: UITableView) -> Int {
if case .editOverride = inputMode {
return Section.allCases.count
} else {
// No cancel button available unless override is already set
return Section.allCases.count - 1
}
}
public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch Section(rawValue: section)! {
case .properties:
return propertyRows.count
case .cancel:
return 1
}
}
private lazy var quantityFormatter: QuantityFormatter = {
let formatter = QuantityFormatter(for: glucoseUnit)
return formatter
}()
private lazy var overrideSymbolKeyboard: EmojiInputController = {
let keyboard = OverrideSymbolInputController()
keyboard.delegate = self
return keyboard
}()
public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch Section(rawValue: indexPath.section)! {
case .properties:
switch propertyRow(for: indexPath) {
case .symbol:
let cell = tableView.dequeueReusableCell(withIdentifier: LabeledTextFieldTableViewCell.className, for: indexPath) as! LabeledTextFieldTableViewCell
cell.titleLabel.text = LocalizedString("Symbol", comment: "The text for the custom preset symbol setting")
cell.textField.text = symbol
cell.textField.placeholder = SettingsTableViewCell.NoValueString
cell.maximumTextLength = 2
cell.customInput = overrideSymbolKeyboard
cell.delegate = self
return cell
case .name:
let cell = tableView.dequeueReusableCell(withIdentifier: LabeledTextFieldTableViewCell.className, for: indexPath) as! LabeledTextFieldTableViewCell
cell.titleLabel.text = LocalizedString("Name", comment: "The text for the custom preset name setting")
cell.textField.text = name
cell.textField.placeholder = LocalizedString("Running", comment: "The text for the custom preset name field placeholder")
cell.delegate = self
return cell
case .insulinNeeds:
let cell = tableView.dequeueReusableCell(withIdentifier: InsulinSensitivityScalingTableViewCell.className, for: indexPath) as! InsulinSensitivityScalingTableViewCell
cell.scaleFactor = insulinNeedsScaleFactor
cell.delegate = self
return cell
case .targetRange:
let cell = tableView.dequeueReusableCell(withIdentifier: DoubleRangeTableViewCell.className, for: indexPath) as! DoubleRangeTableViewCell
cell.numberFormatter = quantityFormatter.numberFormatter
cell.titleLabel.text = LocalizedString("Target Range", comment: "The text for the custom preset target range setting")
cell.range = targetRange
cell.unitLabel.text = quantityFormatter.localizedUnitStringWithPlurality()
cell.delegate = self
return cell
case .startDate:
let cell = tableView.dequeueReusableCell(withIdentifier: DateAndDurationTableViewCell.className, for: indexPath) as! DateAndDurationTableViewCell
cell.titleLabel.text = LocalizedString("Start Time", comment: "The text for the custom preset start time")
cell.datePicker.datePickerMode = .dateAndTime
cell.datePicker.preferredDatePickerStyle = .wheels
cell.datePicker.minimumDate = min(startDate, Date())
cell.date = startDate
cell.delegate = self
return cell
case .endDate:
guard case let .viewOverride(override) = inputMode else {
fatalError("endDate should only be used when viewing override history")
}
let cell = tableView.dequeueReusableCell(withIdentifier: DateAndDurationTableViewCell.className, for: indexPath) as! DateAndDurationTableViewCell
cell.titleLabel.text = LocalizedString("End Time", comment: "The text for the override start time")
cell.datePicker.datePickerMode = .dateAndTime
cell.date = override.actualEndDate
return cell
case .durationFiniteness:
let cell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell
cell.selectionStyle = .none
cell.textLabel?.text = LocalizedString("Enable Indefinitely", comment: "The text for the indefinite custom preset duration setting")
cell.switch?.isOn = !duration.isFinite
cell.switch?.addTarget(self, action: #selector(durationFinitenessChanged), for: .valueChanged)
return cell
case .duration:
let cell = tableView.dequeueReusableCell(withIdentifier: DateAndDurationTableViewCell.className, for: indexPath) as! DateAndDurationTableViewCell
cell.titleLabel.text = LocalizedString("Duration", comment: "The text for the custom preset duration setting")
cell.datePicker.datePickerMode = .countDownTimer
guard case .finite(let duration) = duration else {
preconditionFailure("Duration should only be selectable when duration is finite")
}
// Use the actual duration if we're retrospectively viewing overrides
if case let .viewOverride(override) = inputMode {
cell.titleLabel.text = LocalizedString("Active Duration", comment: "The text for the override history duration")
cell.datePicker.minuteInterval = 1
cell.duration = override.actualEndDate.timeIntervalSince(override.startDate)
} else {
cell.titleLabel.text = LocalizedString("Duration", comment: "The text for the override duration setting")
cell.datePicker.minuteInterval = 15
cell.duration = duration
}
cell.maximumDuration = .hours(24)
cell.delegate = self
return cell
}
case .cancel:
let cell = tableView.dequeueReusableCell(withIdentifier: TextButtonTableViewCell.className, for: indexPath) as! TextButtonTableViewCell
if startDate > Date() {
cell.textLabel?.text = LocalizedString("Cancel", comment: "The text for the scheduled custom preset cancel button")
} else {
cell.textLabel?.text = LocalizedString("Disable Preset", comment: "The text for the custom preset disable button")
}
cell.textLabel?.textAlignment = .center
cell.tintColor = .defaultButtonTextColor
return cell
}
}
@objc private func durationFinitenessChanged(_ sender: UISwitch) {
if sender.isOn {
setDurationIndefinite()
} else {
setDurationFinite()
}
}
private func setDurationIndefinite() {
guard let durationIndexPath = indexPath(for: .duration) else {
assertionFailure("Unable to locate duration row")
return
}
duration = .indefinite
tableView.deleteRows(at: [durationIndexPath], with: .automatic)
}
private func setDurationFinite() {
switch inputMode {
case .newPreset, .customOverride:
duration = .finite(.defaultOverrideDuration)
case .editPreset(let preset), .customizePresetOverride(let preset):
switch preset.duration {
case .finite(let interval):
duration = .finite(interval)
case .indefinite:
duration = .finite(.defaultOverrideDuration)
}
case .editOverride(let override), .viewOverride(let override):
if case .preset(let preset) = override.context,
case .finite(let interval) = preset.duration {
duration = .finite(interval)
} else {
switch override.duration {
case .finite(let interval):
duration = .finite(interval)
case .indefinite:
duration = .finite(.defaultOverrideDuration)
}
}
}
guard let durationIndexPath = indexPath(for: .duration) else {
assertionFailure("Unable to locate duration row")
return
}
tableView.insertRows(at: [durationIndexPath], with: .automatic)
}
public override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
guard section == 0 else {
return nil
}
switch inputMode {
case .customizePresetOverride(let preset):
return String(format: LocalizedString("Changes will only apply this time you enable the preset. The default settings of %@ will not be affected.", comment: "Footer text for customizing from a preset (1: preset name)"), preset.name)
case .editOverride(let override):
guard case .preset(let preset) = override.context else {
return nil
}
return String(format: LocalizedString("Edits persist only until the preset is disabled. The default settings of %@ will not be affected.", comment: "Footer text for editing an enabled custom preset (1: preset name)"), preset.name)
default:
return nil
}
}
// MARK: - UITableViewDelegate
public override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
switch Section(rawValue: indexPath.section)! {
case .properties:
tableView.endEditing(false)
tableView.beginUpdates()
collapseExpandableCells(excluding: indexPath)
case .cancel:
break
}
return indexPath
}
public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch Section(rawValue: indexPath.section)! {
case .properties:
tableView.endUpdates()
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) as? LabeledTextFieldTableViewCell, !cell.isFirstResponder {
cell.textField.becomeFirstResponder()
}
case .cancel:
guard case .editOverride(let override) = inputMode else {
assertionFailure("Only an already-set override can be canceled")
return
}
delegate?.addEditOverrideTableViewController(self, didCancelOverride: override)
dismiss()
}
}
private func collapseExpandableCells(excluding indexPath: IndexPath? = nil) {
tableView.beginUpdates()
hideDatePickerCells(excluding: indexPath)
collapseInsulinSensitivityScalingCells(excluding: indexPath)
tableView.endUpdates()
}
}
// MARK: - Navigation item configuration
extension AddEditOverrideTableViewController {
private func setupTitle() {
if let symbol = symbol, let name = name {
let format = LocalizedString("%1$@ %2$@", comment: "The format for a preset symbol and name (1: symbol)(2: name)")
title = String(format: format, symbol, name)
} else {
switch inputMode {
case .newPreset:
title = LocalizedString("New Preset", comment: "The title for the new custom preset entry screen")
case .editPreset, .customizePresetOverride:
assertionFailure("Editing or customizing a preset means we'll have a symbol and a name")
case .customOverride:
title = LocalizedString("Custom Preset", comment: "The title for the custom preset entry screen")
case .editOverride:
title = LocalizedString("Edit", comment: "The title for the enabled custom preset editing screen")
case .viewOverride:
title = LocalizedString("View Override", comment: "The title for the override editing screen")
}
}
}
private func setupBarButtonItems() {
switch inputMode {
case .newPreset, .editPreset, .editOverride:
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(save))
case .customizePresetOverride, .customOverride:
navigationItem.rightBarButtonItem = UIBarButtonItem(title: LocalizedString("Enable", comment: "The button text for enabling a temporary override"), style: .done, target: self, action: #selector(save))
case .viewOverride: break
}
updateSaveButtonEnabled()
switch inputMode {
case .newPreset:
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel))
default:
break
}
}
private var configuredSettings: TemporaryScheduleOverrideSettings? {
if let targetRange = targetRange {
guard targetRange.maxValue >= targetRange.minValue else {
return nil
}
} else {
guard insulinNeedsScaleFactor != 1.0 else {
return nil
}
}
return TemporaryScheduleOverrideSettings(
unit: glucoseUnit,
targetRange: targetRange,
insulinNeedsScaleFactor: insulinNeedsScaleFactor == 1.0 ? nil : insulinNeedsScaleFactor
)
}
private var configuredPreset: TemporaryScheduleOverridePreset? {
guard
let symbol = symbol, !symbol.isEmpty,
let name = name, !name.isEmpty,
let settings = configuredSettings
else {
return nil
}
let id: UUID
if case .editPreset(let preset) = inputMode {
id = preset.id
} else {
id = UUID()
}
return TemporaryScheduleOverridePreset(id: id, symbol: symbol, name: name, settings: settings, duration: duration)
}
private var configuredOverride: TemporaryScheduleOverride? {
guard let settings = configuredSettings else {
return nil
}
let context: TemporaryScheduleOverride.Context
switch inputMode {
case .customizePresetOverride(let preset):
let customizedPreset = TemporaryScheduleOverridePreset(
symbol: preset.symbol,
name: preset.name,
settings: settings,
duration: duration
)
context = .preset(customizedPreset)
case .editOverride(let override), .viewOverride(let override):
context = override.context
case .customOverride:
context = .custom
case .newPreset, .editPreset:
assertionFailure()
return nil
}
return TemporaryScheduleOverride(context: context, settings: settings, startDate: startDate, duration: duration, enactTrigger: enactTrigger, syncIdentifier: syncIdentifier)
}
private func updateSaveButtonEnabled() {
navigationItem.rightBarButtonItem?.isEnabled = {
switch inputMode {
case .newPreset, .editPreset:
return configuredPreset != nil
case .customizePresetOverride, .customOverride, .editOverride:
return configuredOverride != nil
case .viewOverride:
return false
}
}()
}
@objc private func save() {
switch inputMode {
case .newPreset, .editPreset:
guard let configuredPreset = configuredPreset else {
assertionFailure("Save button cannot be tapped when preset is invalid")
break
}
delegate?.addEditOverrideTableViewController(self, didSavePreset: configuredPreset)
case .customizePresetOverride, .customOverride, .editOverride:
guard let configuredOverride = configuredOverride else {
assertionFailure("Save button cannot be tapped when override is invalid")
break
}
delegate?.addEditOverrideTableViewController(self, didSaveOverride: configuredOverride)
case .viewOverride: break
}
dismiss()
}
@objc private func cancel() {
dismiss()
}
private func dismiss() {
if let customDismissalMode = customDismissalMode {
dismiss(with: customDismissalMode)
} else {
switch inputMode {
case .newPreset, .customizePresetOverride, .customOverride:
dismiss(with: .dismissModal)
case .editPreset, .editOverride, .viewOverride:
dismiss(with: .popViewController)
}
}
}
private func dismiss(with mode: DismissalMode) {
switch mode {
case .dismissModal:
dismiss(animated: true)
case .popViewController:
assert(navigationController != nil)
navigationController?.popViewController(animated: true)
}
}
}
// MARK: - Delegation
extension AddEditOverrideTableViewController: TextFieldTableViewCellDelegate {
public func textFieldTableViewCellDidBeginEditing(_ cell: TextFieldTableViewCell) {
collapseExpandableCells()
}
public func textFieldTableViewCellDidEndEditing(_ cell: TextFieldTableViewCell) {
updateWithText(from: cell)
}
public func textFieldTableViewCellDidChangeEditing(_ cell: TextFieldTableViewCell) {
updateWithText(from: cell)
}
private func updateWithText(from cell: TextFieldTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else {
return
}
switch propertyRow(for: indexPath) {
case .symbol:
symbol = cell.textField.text
case .name:
name = cell.textField.text
default:
assertionFailure()
}
}
}
extension AddEditOverrideTableViewController: EmojiInputControllerDelegate {
public func emojiInputControllerDidAdvanceToStandardInputMode(_ controller: EmojiInputController) {
guard
let indexPath = indexPath(for: .symbol),
let cell = tableView.cellForRow(at: indexPath) as? LabeledTextFieldTableViewCell,
let textField = cell.textField as? CustomInputTextField
else {
return
}
let customInput = textField.customInput
textField.customInput = nil
textField.resignFirstResponder()
textField.becomeFirstResponder()
textField.customInput = customInput
}
}
extension AddEditOverrideTableViewController: InsulinSensitivityScalingTableViewCellDelegate {
func insulinSensitivityScalingTableViewCellDidUpdateScaleFactor(_ cell: InsulinSensitivityScalingTableViewCell) {
insulinNeedsScaleFactor = cell.scaleFactor
}
}
extension AddEditOverrideTableViewController: DatePickerTableViewCellDelegate {
public func datePickerTableViewCellDidUpdateDate(_ cell: DatePickerTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
switch propertyRow(for: indexPath) {
case .startDate:
startDate = cell.date
case .duration:
duration = .finite(cell.duration)
default:
assertionFailure()
}
}
}
extension AddEditOverrideTableViewController: DoubleRangeTableViewCellDelegate {
func doubleRangeTableViewCellDidBeginEditing(_ cell: DoubleRangeTableViewCell) {
collapseExpandableCells()
}
func doubleRangeTableViewCellDidUpdateRange(_ cell: DoubleRangeTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
switch propertyRow(for: indexPath) {
case .targetRange:
targetRange = cell.range
default:
assertionFailure()
}
}
}
private extension UIColor {
static let defaultButtonTextColor = UIButton(type: .system).titleColor(for: .normal)
}
private extension UIFont {
func bold() -> UIFont? {
guard let descriptor = fontDescriptor.withSymbolicTraits(.traitBold) else {
return nil
}
return UIFont(descriptor: descriptor, size: pointSize)
}
}