From 560c70a7ca22b345ce6956aa833f4e30ab97578b Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Tue, 18 Aug 2026 10:51:04 -0700 Subject: [PATCH 1/4] Use GeometryReader instead of pulling UIApplication.shared.connectedScenes --- .../StatusTableViewController.swift | 13 ++++- Loop/Views/StatusTableView.swift | 53 ++++++------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 9144a7690..19cf257b9 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -643,7 +643,7 @@ final class StatusTableViewController: LoopChartsTableViewController { let statusRowMode = self.determineStatusRowMode() updateBannerAndHUDandStatusRows(statusRowMode: statusRowMode, newSize: currentContext.newSize, animated: animated) - tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: ActionTabBarMetrics.tableContentInset, right: 0) + updateTableBottomContentInset() redrawCharts() @@ -777,6 +777,17 @@ final class StatusTableViewController: LoopChartsTableViewController { override func viewDidLayoutSubviews() { updateStatusBar() + updateTableBottomContentInset() + } + + private func updateTableBottomContentInset() { + let bottomInset = ActionTabBarMetrics.tableContentInset( + isLandscape: view.bounds.width > view.bounds.height, + bottomSafeAreaInset: view.safeAreaInsets.bottom + ) + if tableView.contentInset.bottom != bottomInset { + tableView.contentInset.bottom = bottomInset + } } private func updateBannerRow(animated: Bool) { diff --git a/Loop/Views/StatusTableView.swift b/Loop/Views/StatusTableView.swift index 220e16eef..70362f5ad 100644 --- a/Loop/Views/StatusTableView.swift +++ b/Loop/Views/StatusTableView.swift @@ -259,24 +259,8 @@ enum ActionTabBarMetrics { static let barHeight: CGFloat = 49 - static var bottomSafeAreaInset: CGFloat { - UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .flatMap { $0.windows } - .first { $0.isKeyWindow }? - .safeAreaInsets.bottom ?? 0 - } - - static var interfaceOrientation: UIInterfaceOrientation { - let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } - let scene = scenes.first { $0.windows.contains { $0.isKeyWindow } } - ?? scenes.first { $0.activationState == .foregroundActive } - ?? scenes.first - return scene?.interfaceOrientation ?? .portrait - } - - static var tableContentInset: CGFloat { - guard !interfaceOrientation.isLandscape else { return 0 } + static func tableContentInset(isLandscape: Bool, bottomSafeAreaInset: CGFloat) -> CGFloat { + guard !isLandscape else { return 0 } if #available(iOS 26.0, *), bottomSafeAreaInset == 0 { return barHeight + 40 } @@ -287,13 +271,14 @@ enum ActionTabBarMetrics { struct LegacyTabBarBackground: ViewModifier { var isVisible: Bool = true + var bottomSafeAreaInset: CGFloat = 0 func body(content: Content) -> some View { if !isVisible { content .frame(height: 0) } else if #available(iOS 26.0, *) { - if ActionTabBarMetrics.bottomSafeAreaInset == 0 { + if bottomSafeAreaInset == 0 { content .frame(height: ActionTabBarMetrics.barHeight) .padding(.bottom, 16) @@ -315,8 +300,6 @@ struct LegacyTabBarBackground: ViewModifier { struct ActionTabView: View { - @State private var orientation: UIInterfaceOrientation - private let content: Content private let tabs: [ActionTab] @@ -326,24 +309,20 @@ struct ActionTabView: View { ) { self.content = content() self.tabs = tabs() - self.orientation = ActionTabBarMetrics.interfaceOrientation } var body: some View { - content - .safeAreaInset(edge: .bottom, spacing: 0) { - ActionTabBar(items: tabs, isHidden: !orientation.isPortrait) - .modifier(LegacyTabBarBackground(isVisible: orientation.isPortrait)) - } - .onAppear { - UIDevice.current.beginGeneratingDeviceOrientationNotifications() - orientation = ActionTabBarMetrics.interfaceOrientation - } - .onDisappear { - UIDevice.current.endGeneratingDeviceOrientationNotifications() - } - .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in - orientation = ActionTabBarMetrics.interfaceOrientation - } + GeometryReader { geometry in + let isPortrait = geometry.size.height >= geometry.size.width + content + .safeAreaInset(edge: .bottom, spacing: 0) { + ActionTabBar(items: tabs, isHidden: !isPortrait) + .modifier(LegacyTabBarBackground( + isVisible: isPortrait, + bottomSafeAreaInset: geometry.safeAreaInsets.bottom + )) + } + } + .ignoresSafeArea(.keyboard) } } From fbf850cf1e025d298f8d419efbf796c18cd1bb3a Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Tue, 18 Aug 2026 11:18:28 -0700 Subject: [PATCH 2/4] Use GeometryReader instead of pulling UIApplication.shared.connectedScenes --- Loop/Views/StatusTableView.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Loop/Views/StatusTableView.swift b/Loop/Views/StatusTableView.swift index 70362f5ad..4855b9a78 100644 --- a/Loop/Views/StatusTableView.swift +++ b/Loop/Views/StatusTableView.swift @@ -215,6 +215,7 @@ struct ActionTabBar: UIViewRepresentable { tag: idx ) } + uiView.setNeedsLayout() } func makeCoordinator() -> Coordinator { Coordinator() } @@ -300,6 +301,8 @@ struct LegacyTabBarBackground: ViewModifier { struct ActionTabView: View { + @Environment(\.verticalSizeClass) private var verticalSizeClass + private let content: Content private let tabs: [ActionTab] @@ -312,8 +315,8 @@ struct ActionTabView: View { } var body: some View { - GeometryReader { geometry in - let isPortrait = geometry.size.height >= geometry.size.width + let isPortrait = verticalSizeClass != .compact + return GeometryReader { geometry in content .safeAreaInset(edge: .bottom, spacing: 0) { ActionTabBar(items: tabs, isHidden: !isPortrait) From a54cf3442e94596165e791b79d999ee89e253756 Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Tue, 25 Aug 2026 14:27:09 -0700 Subject: [PATCH 3/4] Use GeometryReader instead of pulling UIApplication.shared.connectedScenes --- .../StatusTableViewController.swift | 28 +++++------ Loop/Views/StatusTableView.swift | 46 +++++++++++-------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 19cf257b9..701c310bc 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -73,10 +73,23 @@ final class StatusTableViewController: LoopChartsTableViewController { var statusBarBackgroundView: UIView? + var tableBottomContentInset: CGFloat = 0 { + didSet { + applyTableBottomContentInset() + } + } + + private func applyTableBottomContentInset() { + guard isViewLoaded, tableView.contentInset.bottom != tableBottomContentInset else { return } + tableView.contentInset.bottom = tableBottomContentInset + } + override func viewDidLoad() { super.viewDidLoad() + applyTableBottomContentInset() + statusTableViewModel.settingsViewModel.delegate = self statusTableViewModel.settingsViewModel.servicesViewModel.delegate = self statusTableViewModel.settingsViewModel.pumpManagerSettingsViewModel.didTap = { [weak self] in @@ -643,8 +656,8 @@ final class StatusTableViewController: LoopChartsTableViewController { let statusRowMode = self.determineStatusRowMode() updateBannerAndHUDandStatusRows(statusRowMode: statusRowMode, newSize: currentContext.newSize, animated: animated) - updateTableBottomContentInset() - + applyTableBottomContentInset() + redrawCharts() reloading = false @@ -777,17 +790,6 @@ final class StatusTableViewController: LoopChartsTableViewController { override func viewDidLayoutSubviews() { updateStatusBar() - updateTableBottomContentInset() - } - - private func updateTableBottomContentInset() { - let bottomInset = ActionTabBarMetrics.tableContentInset( - isLandscape: view.bounds.width > view.bounds.height, - bottomSafeAreaInset: view.safeAreaInsets.bottom - ) - if tableView.contentInset.bottom != bottomInset { - tableView.contentInset.bottom = bottomInset - } } private func updateBannerRow(animated: Bool) { diff --git a/Loop/Views/StatusTableView.swift b/Loop/Views/StatusTableView.swift index 4855b9a78..74b9d1f5b 100644 --- a/Loop/Views/StatusTableView.swift +++ b/Loop/Views/StatusTableView.swift @@ -125,9 +125,12 @@ struct StatusTableView: View { } var body: some View { - ActionTabView { + ActionTabView { tableBottomContentInset in wrappedView .ignoresSafeArea(edges: .bottom) + .onChange(of: tableBottomContentInset, initial: true) { _, newValue in + viewController.tableBottomContentInset = newValue + } .onChange(of: viewModel.temporaryPresetsManager.activeOverride) { _, _ in Task { await viewController.reloadData(animated: true) @@ -260,8 +263,8 @@ enum ActionTabBarMetrics { static let barHeight: CGFloat = 49 - static func tableContentInset(isLandscape: Bool, bottomSafeAreaInset: CGFloat) -> CGFloat { - guard !isLandscape else { return 0 } + static func tableContentInset(isPortrait: Bool, bottomSafeAreaInset: CGFloat) -> CGFloat { + guard isPortrait else { return 0 } if #available(iOS 26.0, *), bottomSafeAreaInset == 0 { return barHeight + 40 } @@ -301,30 +304,35 @@ struct LegacyTabBarBackground: ViewModifier { struct ActionTabView: View { - @Environment(\.verticalSizeClass) private var verticalSizeClass - - private let content: Content + private let content: (CGFloat) -> Content private let tabs: [ActionTab] - + init( - @ViewBuilder content: @escaping () -> Content, + @ViewBuilder content: @escaping (CGFloat) -> Content, @ActionTabBuilder tabs: @escaping () -> [ActionTab], ) { - self.content = content() + self.content = content self.tabs = tabs() } var body: some View { - let isPortrait = verticalSizeClass != .compact - return GeometryReader { geometry in - content - .safeAreaInset(edge: .bottom, spacing: 0) { - ActionTabBar(items: tabs, isHidden: !isPortrait) - .modifier(LegacyTabBarBackground( - isVisible: isPortrait, - bottomSafeAreaInset: geometry.safeAreaInsets.bottom - )) - } + GeometryReader { geometry in + let isPortrait = geometry.size.height >= geometry.size.width + let bottomSafeAreaInset = geometry.safeAreaInsets.bottom + + content( + ActionTabBarMetrics.tableContentInset( + isPortrait: isPortrait, + bottomSafeAreaInset: bottomSafeAreaInset + ) + ) + .safeAreaInset(edge: .bottom, spacing: 0) { + ActionTabBar(items: tabs, isHidden: !isPortrait) + .modifier(LegacyTabBarBackground( + isVisible: isPortrait, + bottomSafeAreaInset: bottomSafeAreaInset + )) + } } .ignoresSafeArea(.keyboard) } From 32c7ff86f271d22b9141e4baeb07c45b452916fe Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Tue, 25 Aug 2026 14:37:30 -0700 Subject: [PATCH 4/4] Use GeometryReader instead of pulling UIApplication.shared.connectedScenes --- Loop/Views/StatusTableView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Loop/Views/StatusTableView.swift b/Loop/Views/StatusTableView.swift index 74b9d1f5b..e68dfe4b3 100644 --- a/Loop/Views/StatusTableView.swift +++ b/Loop/Views/StatusTableView.swift @@ -304,6 +304,8 @@ struct LegacyTabBarBackground: ViewModifier { struct ActionTabView: View { + @Environment(\.verticalSizeClass) private var verticalSizeClass + private let content: (CGFloat) -> Content private let tabs: [ActionTab] @@ -317,7 +319,7 @@ struct ActionTabView: View { var body: some View { GeometryReader { geometry in - let isPortrait = geometry.size.height >= geometry.size.width + let isPortrait = verticalSizeClass != .compact let bottomSafeAreaInset = geometry.safeAreaInsets.bottom content(