Skip to content

Commit ff31b2b

Browse files
committed
[LOOP-5961] Fix Toolbar on SE
1 parent cdb643e commit ff31b2b

2 files changed

Lines changed: 64 additions & 28 deletions

File tree

Loop/View Controllers/StatusTableViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ final class StatusTableViewController: LoopChartsTableViewController {
643643
let statusRowMode = self.determineStatusRowMode()
644644

645645
updateBannerAndHUDandStatusRows(statusRowMode: statusRowMode, newSize: currentContext.newSize, animated: animated)
646-
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: UIDevice.current.orientation.isLandscape ? 0 : 52, right: 0)
646+
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: ActionTabBarMetrics.tableContentInset, right: 0)
647647

648648
redrawCharts()
649649

Loop/Views/StatusTableView.swift

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,7 @@ private struct WrappedStatusTableViewController: UIViewControllerRepresentable {
8787
}
8888

8989
struct StatusTableView: View {
90-
91-
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
92-
@Environment(\.verticalSizeClass) private var verticalSizeClass
93-
94-
private var isLandscape: Bool {
95-
UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height
96-
}
97-
90+
9891
private let wrapped: WrappedStatusTableViewController
9992

10093
var viewController: StatusTableViewController {
@@ -194,19 +187,26 @@ struct ActionTab: Identifiable {
194187
struct ActionTabBar: UIViewRepresentable {
195188

196189
let items: [ActionTab]
190+
var isHidden: Bool = false
197191

198192
func makeUIView(context: Context) -> UITabBar {
199193
let bar = UITabBar()
200194
bar.delegate = context.coordinator
201195
let appearance = UITabBarAppearance()
202196
appearance.configureWithOpaqueBackground()
197+
let titleAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.label]
198+
for layout in [appearance.stackedLayoutAppearance, appearance.inlineLayoutAppearance, appearance.compactInlineLayoutAppearance] {
199+
for state in [layout.normal, layout.selected] {
200+
state.titleTextAttributes = titleAttributes
201+
}
202+
}
203203
bar.standardAppearance = appearance
204204
bar.scrollEdgeAppearance = appearance
205-
bar.tintColor = .label
206205
return bar
207206
}
208207

209208
func updateUIView(_ uiView: UITabBar, context: Context) {
209+
uiView.isHidden = isHidden
210210
context.coordinator.tabs = items
211211
uiView.items = items.enumerated().map { idx, item in
212212
UITabBarItem(
@@ -241,17 +241,56 @@ enum ActionTabBuilder {
241241
static func buildArray(_ components: [[ActionTab]]) -> [ActionTab] { components.flatMap { $0 } }
242242
}
243243

244+
enum ActionTabBarMetrics {
245+
246+
static let barHeight: CGFloat = 49
247+
248+
static var bottomSafeAreaInset: CGFloat {
249+
UIApplication.shared.connectedScenes
250+
.compactMap { $0 as? UIWindowScene }
251+
.flatMap { $0.windows }
252+
.first { $0.isKeyWindow }?
253+
.safeAreaInsets.bottom ?? 0
254+
}
255+
256+
static var interfaceOrientation: UIInterfaceOrientation {
257+
let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
258+
let scene = scenes.first { $0.windows.contains { $0.isKeyWindow } }
259+
?? scenes.first { $0.activationState == .foregroundActive }
260+
?? scenes.first
261+
return scene?.interfaceOrientation ?? .portrait
262+
}
263+
264+
static var tableContentInset: CGFloat {
265+
guard !interfaceOrientation.isLandscape else { return 0 }
266+
if #available(iOS 26.0, *), bottomSafeAreaInset == 0 {
267+
return barHeight + 40
268+
}
269+
return 52
270+
}
271+
}
272+
244273
struct LegacyTabBarBackground: ViewModifier {
245274

246-
private let isCompatibilityModeActive = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool ?? false
247-
275+
var isVisible: Bool = true
276+
248277
func body(content: Content) -> some View {
249-
if #available(iOS 26.0, *), !isCompatibilityModeActive {
278+
if !isVisible {
250279
content
251280
.frame(height: 0)
281+
} else if #available(iOS 26.0, *) {
282+
if ActionTabBarMetrics.bottomSafeAreaInset == 0 {
283+
content
284+
.frame(height: ActionTabBarMetrics.barHeight)
285+
.padding(.bottom, 16)
286+
} else {
287+
content
288+
.frame(height: 0)
289+
.padding(.bottom, 6)
290+
}
252291
} else {
253292
content
254-
.frame(height: 49)
293+
.frame(height: ActionTabBarMetrics.barHeight)
255294
.background(
256295
Color(UIColor.systemBackground)
257296
.ignoresSafeArea(edges: .bottom)
@@ -273,27 +312,24 @@ struct ActionTabView<Content: View>: View {
273312
) {
274313
self.content = content()
275314
self.tabs = tabs()
276-
277-
self.orientation = Self.currentOrientation()
315+
self.orientation = ActionTabBarMetrics.interfaceOrientation
278316
}
279317

280318
var body: some View {
281319
content
282320
.safeAreaInset(edge: .bottom, spacing: 0) {
283-
if orientation.isPortrait {
284-
ActionTabBar(items: tabs)
285-
.modifier(LegacyTabBarBackground())
286-
}
321+
ActionTabBar(items: tabs, isHidden: !orientation.isPortrait)
322+
.modifier(LegacyTabBarBackground(isVisible: orientation.isPortrait))
323+
}
324+
.onAppear {
325+
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
326+
orientation = ActionTabBarMetrics.interfaceOrientation
327+
}
328+
.onDisappear {
329+
UIDevice.current.endGeneratingDeviceOrientationNotifications()
287330
}
288331
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
289-
orientation = Self.currentOrientation()
332+
orientation = ActionTabBarMetrics.interfaceOrientation
290333
}
291334
}
292-
293-
private static func currentOrientation() -> UIInterfaceOrientation {
294-
UIApplication.shared.connectedScenes
295-
.compactMap { $0 as? UIWindowScene }
296-
.first?
297-
.interfaceOrientation ?? .portrait
298-
}
299335
}

0 commit comments

Comments
 (0)