Skip to content

Commit 65f706d

Browse files
authored
Bring in fixes from DIY for bolus entry, and also fix safe area layout issue (LoopKit#836)
1 parent 96f3bd1 commit 65f706d

4 files changed

Lines changed: 72 additions & 72 deletions

File tree

Loop/View Controllers/StatusTableViewController.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,11 @@ final class StatusTableViewController: LoopChartsTableViewController {
425425

426426
override func reloadData(animated: Bool = false) async {
427427
dispatchPrecondition(condition: .onQueue(.main))
428+
429+
guard view.window != nil else {
430+
return
431+
}
432+
428433
// This should be kept up to date immediately
429434
hudView?.loopCompletionHUD.lastLoopCompleted = loopManager.lastLoopCompleted
430435
hudView?.loopCompletionHUD.mostRecentGlucoseDataDate = loopManager.mostRecentGlucoseDataDate

Loop/Views/BolusEntryView.swift

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,21 @@ struct BolusEntryView: View {
1818
@EnvironmentObject private var displayGlucosePreference: DisplayGlucosePreference
1919
@Environment(\.dismissAction) var dismiss
2020
@Environment(\.appName) var appName
21-
21+
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
22+
2223
@ObservedObject var viewModel: BolusEntryViewModel
2324

2425
@State private var enteredBolusString = ""
25-
@State private var shouldBolusEntryBecomeFirstResponder = false
2626

2727
@State private var isInteractingWithChart = false
28-
@State private var isKeyboardVisible = false
29-
@State private var pickerShouldExpand = false
3028
@State private var editedBolusAmount = false
3129

30+
@FocusState private var bolusFieldFocused: Bool
31+
32+
private var accessoryClearance: CGFloat {
33+
dynamicTypeSize.isAccessibilitySize ? 72 : 52
34+
}
35+
3236
var body: some View {
3337
VStack(spacing: 0) {
3438
List {
@@ -37,21 +41,11 @@ struct BolusEntryView: View {
3741
}
3842
.padding(.top, -28)
3943
.insetGroupedListStyle()
40-
41-
self.actionArea
42-
.frame(height: self.isKeyboardVisible || shouldBolusEntryBecomeFirstResponder ? 0 : nil)
43-
.opacity(self.isKeyboardVisible || shouldBolusEntryBecomeFirstResponder ? 0 : 1)
44-
}
45-
.onKeyboardStateChange { state in
46-
self.isKeyboardVisible = state.height > 0
47-
48-
if state.height == 0 {
49-
// Ensure tapping 'Enter Bolus' can make the text field the first responder again
50-
self.shouldBolusEntryBecomeFirstResponder = false
44+
if !bolusFieldFocused {
45+
actionArea
5146
}
47+
5248
}
53-
.keyboardAware()
54-
.edgesIgnoringSafeArea(self.isKeyboardVisible ? [] : .bottom)
5549
.navigationBarTitle(self.title)
5650
.supportedInterfaceOrientations(.portrait)
5751
.alert(item: self.$viewModel.activeAlert, content: self.alert(for:))
@@ -71,6 +65,7 @@ struct BolusEntryView: View {
7165
enteredBolusStringBinding.wrappedValue = "0"
7266
}
7367
}
68+
.edgesIgnoringSafeArea(self.bolusFieldFocused ? [] : .bottom)
7469
.task {
7570
await self.viewModel.generateRecommendationAndStartObserving()
7671
}
@@ -283,18 +278,31 @@ struct BolusEntryView: View {
283278
Text("Bolus", comment: "Label for bolus entry row on bolus screen")
284279
Spacer()
285280
HStack(alignment: .firstTextBaseline) {
286-
DismissibleKeyboardTextField(
287-
text: enteredBolusStringBinding,
288-
placeholder: viewModel.formatBolusAmount(0.0),
289-
font: .preferredFont(forTextStyle: .title1),
290-
textColor: .loopAccent,
291-
textAlignment: .right,
292-
keyboardType: .decimalPad,
293-
shouldBecomeFirstResponder: shouldBolusEntryBecomeFirstResponder,
294-
maxLength: 5,
295-
doneButtonColor: .loopAccent,
296-
textFieldDidBeginEditing: didBeginEditing
297-
)
281+
TextField(viewModel.formatBolusAmount(0.0), text: enteredBolusStringBinding)
282+
.keyboardType(.decimalPad)
283+
.textInputAutocapitalization(.never)
284+
.disableAutocorrection(true)
285+
.font(.title)
286+
.multilineTextAlignment(.trailing)
287+
.foregroundColor(.loopAccent)
288+
.focused($bolusFieldFocused)
289+
.onChange(of: bolusFieldFocused) { oldValue, focused in
290+
if focused {
291+
didBeginEditing()
292+
}
293+
}
294+
.onChange(of: enteredBolusString) { oldValue, newValue in
295+
if newValue.count > 5 {
296+
enteredBolusString = String(newValue.prefix(5))
297+
viewModel.updateEnteredBolus(enteredBolusString)
298+
}
299+
}
300+
.toolbar {
301+
ToolbarItemGroup(placement: .keyboard) {
302+
Spacer()
303+
Button("Done") { bolusFieldFocused = false }
304+
}
305+
}
298306
bolusUnitsLabel
299307
}
300308
.accessibilityIdentifier("textField_Bolus")
@@ -328,7 +336,6 @@ struct BolusEntryView: View {
328336
enterManualGlucoseButton
329337
.transition(AnyTransition.opacity.combined(with: .move(edge: .bottom)))
330338
}
331-
332339
actionButton
333340
}
334341
.padding(.bottom) // FIXME: unnecessary on iPhone 8 size devices
@@ -385,7 +392,7 @@ struct BolusEntryView: View {
385392
Button<Text>(
386393
action: {
387394
if self.viewModel.actionButtonAction == .enterBolus {
388-
self.shouldBolusEntryBecomeFirstResponder = true
395+
self.bolusFieldFocused = true
389396
} else {
390397
Task {
391398
if await self.viewModel.didPressActionButton() {

Loop/Views/ManualEntryDoseView.swift

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,30 @@ import LoopUI
1515

1616

1717
struct ManualEntryDoseView: View {
18+
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
19+
1820
@ObservedObject var viewModel: ManualEntryDoseViewModel
1921

2022
@State private var enteredBolusString = ""
21-
@State private var shouldBolusEntryBecomeFirstResponder = false
22-
2323
@State private var isInteractingWithChart = false
24-
@State private var isKeyboardVisible = false
24+
@FocusState private var bolusFieldFocused: Bool
2525

2626
@Environment(\.dismissAction) var dismiss
2727

28+
private var accessoryClearance: CGFloat {
29+
dynamicTypeSize.isAccessibilitySize ? 72 : 52
30+
}
31+
2832
var body: some View {
2933
GeometryReader { geometry in
3034
VStack(spacing: 0) {
3135
List {
3236
self.chartSection
3337
self.summarySection
3438
}
35-
// As of iOS 13, we can't programmatically scroll to the Bolus entry text field. This ugly hack scoots the
36-
// list up instead, so the summarySection is visible and the keyboard shows when you tap "Enter Bolus".
37-
// Unfortunately, after entry, the field scoots back down and remains hidden. So this is not a great solution.
38-
// TODO: Fix this in Xcode 12 when we're building for iOS 14.
39-
.padding(.top, self.shouldAutoScroll(basedOn: geometry) ? -200 : -28)
4039
.insetGroupedListStyle()
4140

42-
self.actionArea
43-
.frame(height: self.isKeyboardVisible ? 0 : nil)
44-
.opacity(self.isKeyboardVisible ? 0 : 1)
4541
}
46-
.onKeyboardStateChange { state in
47-
self.isKeyboardVisible = state.height > 0
48-
49-
if state.height == 0 {
50-
// Ensure tapping 'Enter Bolus' can make the text field the first responder again
51-
self.shouldBolusEntryBecomeFirstResponder = false
52-
}
53-
}
54-
.keyboardAware()
55-
.edgesIgnoringSafeArea(self.isKeyboardVisible ? [] : .bottom)
5642
.navigationBarTitle(self.title)
5743
.supportedInterfaceOrientations(.portrait)
5844
}
@@ -62,12 +48,6 @@ struct ManualEntryDoseView: View {
6248
return Text("Log Dose", comment: "Title for dose logging screen")
6349
}
6450

65-
private func shouldAutoScroll(basedOn geometry: GeometryProxy) -> Bool {
66-
// Taking a guess of 640 to cover iPhone SE, iPod Touch, and other smaller devices.
67-
// Devices such as the iPhone 11 Pro Max do not need to auto-scroll.
68-
shouldBolusEntryBecomeFirstResponder && geometry.size.height < 640
69-
}
70-
7151
private var chartSection: some View {
7252
Section {
7353
VStack(spacing: 8) {
@@ -189,17 +169,25 @@ struct ManualEntryDoseView: View {
189169
Text("Bolus", comment: "Label for bolus entry row on bolus screen")
190170
Spacer()
191171
HStack(alignment: .firstTextBaseline) {
192-
DismissibleKeyboardTextField(
193-
text: typedBolusEntry,
194-
placeholder: Self.doseAmountFormatter.string(from: 0.0)!,
195-
font: .preferredFont(forTextStyle: .title1),
196-
textColor: .loopAccent,
197-
textAlignment: .right,
198-
keyboardType: .decimalPad,
199-
shouldBecomeFirstResponder: shouldBolusEntryBecomeFirstResponder,
200-
maxLength: 5,
201-
doneButtonColor: .loopAccent
202-
)
172+
TextField(Self.doseAmountFormatter.string(from: 0.0)!, text: typedBolusEntry)
173+
.keyboardType(.decimalPad)
174+
.textInputAutocapitalization(.never)
175+
.disableAutocorrection(true)
176+
.font(.title)
177+
.multilineTextAlignment(.trailing)
178+
.foregroundColor(.loopAccent)
179+
.focused($bolusFieldFocused)
180+
.onChange(of: enteredBolusString) { oldValue, newValue in
181+
if newValue.count > 5 {
182+
enteredBolusString = String(newValue.prefix(5))
183+
}
184+
}
185+
.toolbar {
186+
ToolbarItemGroup(placement: .keyboard) {
187+
Spacer()
188+
Button("Done") { bolusFieldFocused = false }
189+
}
190+
}
203191
bolusUnitsLabel
204192
}
205193
}
@@ -258,7 +246,7 @@ struct ManualEntryDoseView: View {
258246
}
259247
}
260248

261-
extension InsulinType: Labeled {
249+
extension InsulinType: @retroactive Labeled {
262250
public var label: String {
263251
return title
264252
}

LoopUI/Views/DeviceStatusHUDView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import LoopKitUI
3131
// round the edges of the progress view
3232
progressView.layer.cornerRadius = 2
3333
progressView.clipsToBounds = true
34-
progressView.layer.sublayers![1].cornerRadius = 2
35-
progressView.subviews[1].clipsToBounds = true
34+
progressView.layer.sublayers!.last!.cornerRadius = 2
35+
progressView.subviews.last!.clipsToBounds = true
3636
}
3737
}
3838

0 commit comments

Comments
 (0)