From 5e8ebe72c9fd2af8cf7efdafffebd11f3bada5ab Mon Sep 17 00:00:00 2001 From: Mike Plante <82073483+MikePlante1@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:06:59 -0400 Subject: [PATCH 1/2] Add UIKit navigation title helper Introduce View.uikitNavigationTitle helper (View+UIKitNavigationTitle.swift) to set navigationItem.title and largeTitleDisplayMode when SwiftUI screens are hosted inside a UIKit UINavigationController. Replace .navigationTitle usages with .uikitNavigationTitle(displayMode: .inline) in BatteryTypeSelectionView, DataSourceSelectionView, UseMySentrySelectionView, and the InsulinTypeSetting destination; update the Xcode project to include the new source file. --- MinimedKit.xcodeproj/project.pbxproj | 4 + .../Views/BatteryTypeSelectionView.swift | 2 +- .../Views/DataSourceSelectionView.swift | 2 +- .../Views/MinimedPumpSettingsView.swift | 3 +- .../Views/UseMySentrySelectionView.swift | 2 +- .../Views/View+UIKitNavigationTitle.swift | 91 +++++++++++++++++++ 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 MinimedKitUI/Views/View+UIKitNavigationTitle.swift diff --git a/MinimedKit.xcodeproj/project.pbxproj b/MinimedKit.xcodeproj/project.pbxproj index 9f518b4..3ccd58d 100644 --- a/MinimedKit.xcodeproj/project.pbxproj +++ b/MinimedKit.xcodeproj/project.pbxproj @@ -265,6 +265,7 @@ C1E34B3129C7ABF3009A50A5 /* UseMySentrySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E34B1929C7ABF3009A50A5 /* UseMySentrySelectionView.swift */; }; C1E34B3229C7ABF3009A50A5 /* DataSourceSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E34B1A29C7ABF3009A50A5 /* DataSourceSelectionView.swift */; }; C1E34B3329C7ABF3009A50A5 /* BatteryTypeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E34B1B29C7ABF3009A50A5 /* BatteryTypeSelectionView.swift */; }; + B65C7B5E69AAD31D20C66C6A /* View+UIKitNavigationTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4918419DB490FB27EA2EC8FF /* View+UIKitNavigationTitle.swift */; }; C1E34B3429C7ABF3009A50A5 /* ReservoirHUDView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1E34B1C29C7ABF3009A50A5 /* ReservoirHUDView.xib */; }; C1E34B3529C7ABF3009A50A5 /* MinimedPumpSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E34B1D29C7ABF3009A50A5 /* MinimedPumpSettingsView.swift */; }; C1E34B3629C7ABF3009A50A5 /* ReservoirHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E34B1E29C7ABF3009A50A5 /* ReservoirHUDView.swift */; }; @@ -614,6 +615,7 @@ C1E34B1929C7ABF3009A50A5 /* UseMySentrySelectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UseMySentrySelectionView.swift; sourceTree = ""; }; C1E34B1A29C7ABF3009A50A5 /* DataSourceSelectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSourceSelectionView.swift; sourceTree = ""; }; C1E34B1B29C7ABF3009A50A5 /* BatteryTypeSelectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryTypeSelectionView.swift; sourceTree = ""; }; + 4918419DB490FB27EA2EC8FF /* View+UIKitNavigationTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+UIKitNavigationTitle.swift"; sourceTree = ""; }; C1E34B1C29C7ABF3009A50A5 /* ReservoirHUDView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReservoirHUDView.xib; sourceTree = ""; }; C1E34B1D29C7ABF3009A50A5 /* MinimedPumpSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimedPumpSettingsView.swift; sourceTree = ""; }; C1E34B1E29C7ABF3009A50A5 /* ReservoirHUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReservoirHUDView.swift; sourceTree = ""; }; @@ -1148,6 +1150,7 @@ C1E34B1929C7ABF3009A50A5 /* UseMySentrySelectionView.swift */, C1E34B1A29C7ABF3009A50A5 /* DataSourceSelectionView.swift */, C1E34B1B29C7ABF3009A50A5 /* BatteryTypeSelectionView.swift */, + 4918419DB490FB27EA2EC8FF /* View+UIKitNavigationTitle.swift */, C1E34B1C29C7ABF3009A50A5 /* ReservoirHUDView.xib */, C1E34B1D29C7ABF3009A50A5 /* MinimedPumpSettingsView.swift */, C1E34B1E29C7ABF3009A50A5 /* ReservoirHUDView.swift */, @@ -1681,6 +1684,7 @@ C1E34B2F29C7ABF3009A50A5 /* CommandResponseViewController.swift in Sources */, C1E34B3529C7ABF3009A50A5 /* MinimedPumpSettingsView.swift in Sources */, C1E34B3329C7ABF3009A50A5 /* BatteryTypeSelectionView.swift in Sources */, + B65C7B5E69AAD31D20C66C6A /* View+UIKitNavigationTitle.swift in Sources */, C1E34B2929C7ABF3009A50A5 /* MinimedPumpSentrySetupViewController.swift in Sources */, C1E34B9229C7B46C009A50A5 /* TimeZone.swift in Sources */, C1E34B8E29C7B34F009A50A5 /* NibLoadable.swift in Sources */, diff --git a/MinimedKitUI/Views/BatteryTypeSelectionView.swift b/MinimedKitUI/Views/BatteryTypeSelectionView.swift index 7bb0752..9d0b664 100644 --- a/MinimedKitUI/Views/BatteryTypeSelectionView.swift +++ b/MinimedKitUI/Views/BatteryTypeSelectionView.swift @@ -31,6 +31,6 @@ struct BatteryTypeSelectionView: View { } } .insetGroupedListStyle() - .navigationTitle(LocalizedString("Pump Battery Type", comment: "navigation title for pump battery type selection")) + .uikitNavigationTitle(LocalizedString("Pump Battery Type", comment: "navigation title for pump battery type selection"), displayMode: .inline) } } diff --git a/MinimedKitUI/Views/DataSourceSelectionView.swift b/MinimedKitUI/Views/DataSourceSelectionView.swift index 103c66a..79ba95c 100644 --- a/MinimedKitUI/Views/DataSourceSelectionView.swift +++ b/MinimedKitUI/Views/DataSourceSelectionView.swift @@ -30,6 +30,6 @@ struct DataSourceSelectionView: View { } } .insetGroupedListStyle() - .navigationTitle(LocalizedString("Preferred Data Source", comment: "navigation title for pump battery type selection")) + .uikitNavigationTitle(LocalizedString("Preferred Data Source", comment: "navigation title for pump battery type selection"), displayMode: .inline) } } diff --git a/MinimedKitUI/Views/MinimedPumpSettingsView.swift b/MinimedKitUI/Views/MinimedPumpSettingsView.swift index 69c2cc7..10bd30e 100644 --- a/MinimedKitUI/Views/MinimedPumpSettingsView.swift +++ b/MinimedKitUI/Views/MinimedPumpSettingsView.swift @@ -156,7 +156,8 @@ struct MinimedPumpSettingsView: View { Section(header: Text(LocalizedString("Configuration", comment: "The title of the configuration section in MinimedPumpManager settings"))) { - NavigationLink(destination: InsulinTypeSetting(initialValue: viewModel.pumpManager.state.insulinType, supportedInsulinTypes: supportedInsulinTypes, allowUnsetInsulinType: false, didChange: viewModel.didChangeInsulinType)) { + NavigationLink(destination: InsulinTypeSetting(initialValue: viewModel.pumpManager.state.insulinType, supportedInsulinTypes: supportedInsulinTypes, allowUnsetInsulinType: false, didChange: viewModel.didChangeInsulinType) + .uikitNavigationTitle(LocalizedString("Insulin Type", comment: "Text for confidence reminders navigation link"), displayMode: .inline)) { HStack { Text(LocalizedString("Insulin Type", comment: "Text for confidence reminders navigation link")).foregroundColor(Color.primary) if let currentTitle = viewModel.pumpManager.state.insulinType?.brandName { diff --git a/MinimedKitUI/Views/UseMySentrySelectionView.swift b/MinimedKitUI/Views/UseMySentrySelectionView.swift index e4b7d7e..c268439 100644 --- a/MinimedKitUI/Views/UseMySentrySelectionView.swift +++ b/MinimedKitUI/Views/UseMySentrySelectionView.swift @@ -29,6 +29,6 @@ struct UseMySentrySelectionView: View { } } .insetGroupedListStyle() - .navigationTitle(LocalizedString("Use MySentry", comment: "navigation title for pump battery type selection")) + .uikitNavigationTitle(LocalizedString("Use MySentry", comment: "navigation title for pump battery type selection"), displayMode: .inline) } } diff --git a/MinimedKitUI/Views/View+UIKitNavigationTitle.swift b/MinimedKitUI/Views/View+UIKitNavigationTitle.swift new file mode 100644 index 0000000..60f649f --- /dev/null +++ b/MinimedKitUI/Views/View+UIKitNavigationTitle.swift @@ -0,0 +1,91 @@ +// +// View+UIKitNavigationTitle.swift +// MinimedKitUI +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import SwiftUI +import UIKit + +extension View { + /// Sets the navigation bar title (and large-title display mode) for a SwiftUI screen pushed + /// via `NavigationLink` while the flow is hosted inside a UIKit `UINavigationController` + /// (as the Medtronic settings screens are, via `MinimedUICoordinator`). + func uikitNavigationTitle( + _ title: String, + displayMode: NavigationBarItem.TitleDisplayMode = .automatic + ) -> some View { + self + .navigationTitle(title) + .navigationBarTitleDisplayMode(displayMode) + .background(NavigationItemTitleSetter(title: title, largeTitleDisplayMode: displayMode.uiKitLargeTitleDisplayMode)) + } +} + +private extension NavigationBarItem.TitleDisplayMode { + var uiKitLargeTitleDisplayMode: UINavigationItem.LargeTitleDisplayMode { + switch self { + case .inline: return .never + case .large: return .always + case .automatic: return .automatic + @unknown default: return .automatic + } + } +} + +/// Sets `navigationItem.title` and `largeTitleDisplayMode` on the enclosing navigation +/// controller's top view controller. See `View.uikitNavigationTitle(_:displayMode:)`. +private struct NavigationItemTitleSetter: UIViewControllerRepresentable { + let title: String + let largeTitleDisplayMode: UINavigationItem.LargeTitleDisplayMode + + func makeUIViewController(context: Context) -> TitleProxyViewController { + TitleProxyViewController(title: title, largeTitleDisplayMode: largeTitleDisplayMode) + } + + func updateUIViewController(_ uiViewController: TitleProxyViewController, context: Context) { + uiViewController.proxyTitle = title + uiViewController.largeTitleDisplayMode = largeTitleDisplayMode + } + + final class TitleProxyViewController: UIViewController { + var proxyTitle: String { + didSet { applyTitle() } + } + + var largeTitleDisplayMode: UINavigationItem.LargeTitleDisplayMode { + didSet { applyTitle() } + } + + init(title: String, largeTitleDisplayMode: UINavigationItem.LargeTitleDisplayMode) { + self.proxyTitle = title + self.largeTitleDisplayMode = largeTitleDisplayMode + super.init(nibName: nil, bundle: nil) + view.isHidden = true + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + applyTitle() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + applyTitle() + } + + private func applyTitle() { + // The pushed SwiftUI screen is the navigation controller's top view controller; + // set the title and display mode the navigation bar actually uses. + guard let host = navigationController?.topViewController else { return } + host.navigationItem.title = proxyTitle + host.navigationItem.largeTitleDisplayMode = largeTitleDisplayMode + } + } +} From d71e4639566ef293d79c01e671e8c91bcd12c083 Mon Sep 17 00:00:00 2001 From: Mike Plante <82073483+MikePlante1@users.noreply.github.com> Date: Wed, 24 Jun 2026 20:45:30 -0400 Subject: [PATCH 2/2] Revert .inline navigation displayMode --- MinimedKitUI/Views/BatteryTypeSelectionView.swift | 2 +- MinimedKitUI/Views/DataSourceSelectionView.swift | 2 +- MinimedKitUI/Views/MinimedPumpSettingsView.swift | 2 +- MinimedKitUI/Views/UseMySentrySelectionView.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MinimedKitUI/Views/BatteryTypeSelectionView.swift b/MinimedKitUI/Views/BatteryTypeSelectionView.swift index 9d0b664..10e900c 100644 --- a/MinimedKitUI/Views/BatteryTypeSelectionView.swift +++ b/MinimedKitUI/Views/BatteryTypeSelectionView.swift @@ -31,6 +31,6 @@ struct BatteryTypeSelectionView: View { } } .insetGroupedListStyle() - .uikitNavigationTitle(LocalizedString("Pump Battery Type", comment: "navigation title for pump battery type selection"), displayMode: .inline) + .uikitNavigationTitle(LocalizedString("Pump Battery Type", comment: "navigation title for pump battery type selection")) } } diff --git a/MinimedKitUI/Views/DataSourceSelectionView.swift b/MinimedKitUI/Views/DataSourceSelectionView.swift index 79ba95c..fd1e67f 100644 --- a/MinimedKitUI/Views/DataSourceSelectionView.swift +++ b/MinimedKitUI/Views/DataSourceSelectionView.swift @@ -30,6 +30,6 @@ struct DataSourceSelectionView: View { } } .insetGroupedListStyle() - .uikitNavigationTitle(LocalizedString("Preferred Data Source", comment: "navigation title for pump battery type selection"), displayMode: .inline) + .uikitNavigationTitle(LocalizedString("Preferred Data Source", comment: "navigation title for pump battery type selection")) } } diff --git a/MinimedKitUI/Views/MinimedPumpSettingsView.swift b/MinimedKitUI/Views/MinimedPumpSettingsView.swift index 10bd30e..05f1d1d 100644 --- a/MinimedKitUI/Views/MinimedPumpSettingsView.swift +++ b/MinimedKitUI/Views/MinimedPumpSettingsView.swift @@ -157,7 +157,7 @@ struct MinimedPumpSettingsView: View { Section(header: Text(LocalizedString("Configuration", comment: "The title of the configuration section in MinimedPumpManager settings"))) { NavigationLink(destination: InsulinTypeSetting(initialValue: viewModel.pumpManager.state.insulinType, supportedInsulinTypes: supportedInsulinTypes, allowUnsetInsulinType: false, didChange: viewModel.didChangeInsulinType) - .uikitNavigationTitle(LocalizedString("Insulin Type", comment: "Text for confidence reminders navigation link"), displayMode: .inline)) { + .uikitNavigationTitle(LocalizedString("Insulin Type", comment: "Text for confidence reminders navigation link"))) { HStack { Text(LocalizedString("Insulin Type", comment: "Text for confidence reminders navigation link")).foregroundColor(Color.primary) if let currentTitle = viewModel.pumpManager.state.insulinType?.brandName { diff --git a/MinimedKitUI/Views/UseMySentrySelectionView.swift b/MinimedKitUI/Views/UseMySentrySelectionView.swift index c268439..8bb0b69 100644 --- a/MinimedKitUI/Views/UseMySentrySelectionView.swift +++ b/MinimedKitUI/Views/UseMySentrySelectionView.swift @@ -29,6 +29,6 @@ struct UseMySentrySelectionView: View { } } .insetGroupedListStyle() - .uikitNavigationTitle(LocalizedString("Use MySentry", comment: "navigation title for pump battery type selection"), displayMode: .inline) + .uikitNavigationTitle(LocalizedString("Use MySentry", comment: "navigation title for pump battery type selection")) } }