From a17efb1beab2a4ae3c31aefa2a5abf7b87b58c0e Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Thu, 16 Jul 2026 14:34:59 -0700 Subject: [PATCH 1/3] toolbar fix --- Loop/Info.plist | 2 - .../StatusTableViewController.swift | 130 ++++++++++++------ 2 files changed, 88 insertions(+), 44 deletions(-) diff --git a/Loop/Info.plist b/Loop/Info.plist index db76e6e846..d6ee292459 100644 --- a/Loop/Info.plist +++ b/Loop/Info.plist @@ -89,8 +89,6 @@ remote-notification audio - UIDesignRequiresCompatibility - UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 94df013244..0b1063fc63 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -273,41 +273,76 @@ final class StatusTableViewController: LoopChartsTableViewController { deviceManager.pumpManagerHUDProvider?.visible = active && onscreen } + private lazy var carbEntryButton = makeToolbarButton(imageNamed: "carbs", tintColor: .carbTintColor, action: #selector(userTappedAddCarbs)) + private lazy var bolusButton = makeToolbarButton(imageNamed: "bolus", tintColor: .insulinTintColor, action: #selector(presentBolusScreen)) + private lazy var settingsButton = makeToolbarButton(imageNamed: "settings", tintColor: .secondaryLabel, action: #selector(onSettingsTapped)) + + private lazy var workoutButton: UIButton = { + let button = UIButton(type: .system) + button.tintColor = .glucoseTintColor + button.addTarget(self, action: #selector(toggleWorkoutMode(_:)), for: .touchUpInside) + button.constrainToToolbarIconSize() + return button + }() + + private func makeToolbarButton(imageNamed name: String, tintColor: UIColor, action: Selector) -> UIButton { + let button = UIButton(type: .system) + button.setImage(UIImage(named: name), for: .normal) + button.tintColor = tintColor + button.addTarget(self, action: action, for: .touchUpInside) + button.constrainToToolbarIconSize() + return button + } + private func setupToolbarItems() { - let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil) - let carbs = UIBarButtonItem(image: UIImage(named: "carbs"), style: .plain, target: self, action: #selector(userTappedAddCarbs)) - let bolus = UIBarButtonItem(image: UIImage(named: "bolus"), style: .plain, target: self, action: #selector(presentBolusScreen)) - let settings = UIBarButtonItem(image: UIImage(named: "settings"), style: .plain, target: self, action: #selector(onSettingsTapped)) - + let carbs = UIBarButtonItem(customView: carbEntryButton) + let bolus = UIBarButtonItem(customView: bolusButton) + let settings = UIBarButtonItem(customView: settingsButton) + + carbs.title = "Add Meal" + let preMeal = createPreMealButtonItem(selected: false, isEnabled: true) - let workout = createWorkoutButtonItem(selected: false, isEnabled: true) - toolbarItems = [ - carbs, - space, - preMeal, - space, - bolus, - space, - workout, - space, - settings - ] - } + updateWorkoutButton(selected: false, isEnabled: true) + let workout = UIBarButtonItem(customView: workoutButton) + + func flexibleSpace() -> UIBarButtonItem { + UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + } + if #available(iOS 26, *) { + toolbarItems = [carbs, preMeal, bolus, workout, settings] + } else { + toolbarItems = [ + carbs, + flexibleSpace(), + preMeal, + flexibleSpace(), + bolus, + flexibleSpace(), + workout, + flexibleSpace(), + settings + ] + } + } + private func updateToolbarItems() { let isPumpOnboarded = onboardingManager.isComplete || deviceManager.pumpManager?.isOnboarded == true - toolbarItems![0].accessibilityLabel = NSLocalizedString("Add Meal", comment: "The label of the carb entry button") - toolbarItems![0].isEnabled = isPumpOnboarded - toolbarItems![0].tintColor = UIColor.carbTintColor - toolbarItems![4].accessibilityLabel = NSLocalizedString("Bolus", comment: "The label of the bolus entry button") - toolbarItems![4].isEnabled = isPumpOnboarded - toolbarItems![4].tintColor = UIColor.insulinTintColor - toolbarItems![8].accessibilityLabel = NSLocalizedString("Settings", comment: "The label of the settings button") - toolbarItems![8].tintColor = UIColor.secondaryLabel - - toolbarItems![2] = createPreMealButtonItem(selected: preMealMode == true && preMealModeAllowed, isEnabled: preMealModeAllowed) - toolbarItems![6] = createWorkoutButtonItem(selected: workoutMode == true && workoutModeAllowed, isEnabled: workoutModeAllowed) + carbEntryButton.accessibilityLabel = NSLocalizedString("Add Meal", comment: "The label of the carb entry button") + carbEntryButton.isEnabled = isPumpOnboarded + bolusButton.accessibilityLabel = NSLocalizedString("Bolus", comment: "The label of the bolus entry button") + bolusButton.isEnabled = isPumpOnboarded + settingsButton.accessibilityLabel = NSLocalizedString("Settings", comment: "The label of the settings button") + + let preMealIndex: Int + if #available(iOS 26, *) { + preMealIndex = 1 + } else { + preMealIndex = 2 + } + toolbarItems![preMealIndex] = createPreMealButtonItem(selected: preMealMode == true && preMealModeAllowed, isEnabled: preMealModeAllowed) + updateWorkoutButton(selected: workoutMode == true && workoutModeAllowed, isEnabled: workoutModeAllowed) } public var basalDeliveryState: PumpManagerStatus.BasalDeliveryState? = nil { @@ -1459,21 +1494,20 @@ final class StatusTableViewController: LoopChartsTableViewController { return item } - private func createWorkoutButtonItem(selected: Bool, isEnabled: Bool) -> UIBarButtonItem { - let item = UIBarButtonItem(image: UIImage.workoutImage(selected: selected), style: .plain, target: self, action: #selector(toggleWorkoutMode(_:))) - item.accessibilityLabel = NSLocalizedString("Workout Targets", comment: "The label of the workout mode toggle button") + private func updateWorkoutButton(selected: Bool, isEnabled: Bool) { + workoutButton.setImage(UIImage.workoutImage(selected: selected), for: .normal) + workoutButton.accessibilityLabel = NSLocalizedString("Workout Targets", comment: "The label of the workout mode toggle button") if selected { - item.accessibilityTraits.insert(.selected) - item.accessibilityHint = NSLocalizedString("Disables", comment: "The action hint of the workout mode toggle button when enabled") + workoutButton.accessibilityTraits.insert(.selected) + workoutButton.accessibilityHint = NSLocalizedString("Disables", comment: "The action hint of the workout mode toggle button when enabled") } else { - item.accessibilityHint = NSLocalizedString("Enables", comment: "The action hint of the workout mode toggle button when disabled") + workoutButton.accessibilityTraits.remove(.selected) + workoutButton.accessibilityHint = NSLocalizedString("Enables", comment: "The action hint of the workout mode toggle button when disabled") } - item.tintColor = UIColor.glucoseTintColor - item.isEnabled = isEnabled - - return item + workoutButton.isEnabled = isEnabled + workoutButton.sizeToFit() } @IBAction func premealButtonTapped(_ sender: UIBarButtonItem) { @@ -1781,15 +1815,15 @@ final class StatusTableViewController: LoopChartsTableViewController { @objc private func pumpStatusTapped(_ sender: UIGestureRecognizer) { if let pumpStatusView = sender.view as? PumpStatusHUDView { - executeHUDTapAction(deviceManager.didTapOnPumpStatus(pumpStatusView.pumpManagerProvidedHUD)) + executeHUDTapAction(deviceManager.didTapOnPumpStatus(pumpStatusView.pumpManagerProvidedHUD), zoomingFrom: pumpStatusView) } } @objc private func cgmStatusTapped( _ sender: UIGestureRecognizer) { - executeHUDTapAction(deviceManager.didTapOnCGMStatus()) + executeHUDTapAction(deviceManager.didTapOnCGMStatus(), zoomingFrom: sender.view) } - private func executeHUDTapAction(_ action: HUDTapAction?) { + private func executeHUDTapAction(_ action: HUDTapAction?, zoomingFrom sourceView: UIView? = nil) { guard let action = action else { return } @@ -2042,6 +2076,18 @@ final class StatusTableViewController: LoopChartsTableViewController { } } +private extension UIButton { + /// Keeps a custom-view toolbar button tight to its icon by refusing to stretch. Without + /// this the toolbar expands the button to fill its distributed slot, and the zoom + /// transition then originates from that full-width slot (the bar) rather than the icon. + func constrainToToolbarIconSize() { + setContentHuggingPriority(.required, for: .horizontal) + setContentHuggingPriority(.required, for: .vertical) + setContentCompressionResistancePriority(.required, for: .horizontal) + setContentCompressionResistancePriority(.required, for: .vertical) + } +} + extension UIAlertController { func addActivityIndicator() { let frame = CGRect(x: 0, y: 0, width: 40, height: 40) From fd07cefabe2a900944286e538ffb321c09e2ac42 Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Thu, 16 Jul 2026 14:39:31 -0700 Subject: [PATCH 2/3] toolbar fix --- Loop/View Controllers/StatusTableViewController.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 0b1063fc63..d3a7e61acc 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -1815,15 +1815,15 @@ final class StatusTableViewController: LoopChartsTableViewController { @objc private func pumpStatusTapped(_ sender: UIGestureRecognizer) { if let pumpStatusView = sender.view as? PumpStatusHUDView { - executeHUDTapAction(deviceManager.didTapOnPumpStatus(pumpStatusView.pumpManagerProvidedHUD), zoomingFrom: pumpStatusView) + executeHUDTapAction(deviceManager.didTapOnPumpStatus(pumpStatusView.pumpManagerProvidedHUD)) } } @objc private func cgmStatusTapped( _ sender: UIGestureRecognizer) { - executeHUDTapAction(deviceManager.didTapOnCGMStatus(), zoomingFrom: sender.view) + executeHUDTapAction(deviceManager.didTapOnCGMStatus()) } - private func executeHUDTapAction(_ action: HUDTapAction?, zoomingFrom sourceView: UIView? = nil) { + private func executeHUDTapAction(_ action: HUDTapAction?) { guard let action = action else { return } @@ -2077,9 +2077,6 @@ final class StatusTableViewController: LoopChartsTableViewController { } private extension UIButton { - /// Keeps a custom-view toolbar button tight to its icon by refusing to stretch. Without - /// this the toolbar expands the button to fill its distributed slot, and the zoom - /// transition then originates from that full-width slot (the bar) rather than the icon. func constrainToToolbarIconSize() { setContentHuggingPriority(.required, for: .horizontal) setContentHuggingPriority(.required, for: .vertical) From 007cd897057db3cd38b769ed5a9056d3f0ae6e02 Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Fri, 17 Jul 2026 15:03:58 -0700 Subject: [PATCH 3/3] toolbar fix --- Loop/View Controllers/StatusTableViewController.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index d3a7e61acc..84d278bac0 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -1578,7 +1578,13 @@ final class StatusTableViewController: LoopChartsTableViewController { } } else { if FeatureFlags.sensitivityOverridesEnabled { - performSegue(withIdentifier: OverrideSelectionViewController.className, sender: toolbarItems![6]) + let overridesIndex: Int + if #available(iOS 26, *) { + overridesIndex = 3 + } else { + overridesIndex = 6 + } + performSegue(withIdentifier: OverrideSelectionViewController.className, sender: toolbarItems![overridesIndex]) } else { presentWorkoutModeAlertController() }