Skip to content

Commit dd14e06

Browse files
committed
Fix the tab rail flickering on narrow iPad windows
The rail was hidden when the split view collapsed, but that freed up enough width for the split to expand again, showing the rail and collapsing the split. The split's state now only hides the rail and showing it again is driven by the container's width growing.
1 parent b4395d9 commit dd14e06

1 file changed

Lines changed: 24 additions & 3 deletions

File tree

ElementX/Sources/Application/Navigation/NavigationTabCoordinator.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ private struct NavigationTabCoordinatorView<Tag: Hashable>: View {
317317
@State private var isFullScreen = true
318318
@State private var isRailVisible = true
319319
@State private var railBackgroundColor: Color = .compound.bgCanvasDefault
320+
@State private var containerWidth: CGFloat = 0
321+
/// The container's width at the moment the rail was hidden, used to avoid flickering. See ``tabRailLayout``.
322+
@State private var railHiddenWidth: CGFloat?
320323

321324
var body: some View {
322325
tabView
@@ -385,8 +388,16 @@ private struct NavigationTabCoordinatorView<Tag: Hashable>: View {
385388
}
386389
}
387390
.onGeometryChange(for: CGSize.self) { geometry in
388-
geometry.size // We don't need the size, but it's a signal to re-compute.
389-
} action: { _ in
391+
geometry.size
392+
} action: { size in
393+
containerWidth = size.width
394+
395+
// The container grew, so give the rail another chance at being shown.
396+
if let railHiddenWidth, size.width > railHiddenWidth {
397+
self.railHiddenWidth = nil
398+
isRailVisible = true
399+
}
400+
390401
guard let newValue = window?.isFullScreen else { return }
391402

392403
if newValue != isFullScreen {
@@ -399,7 +410,17 @@ private struct NavigationTabCoordinatorView<Tag: Hashable>: View {
399410
// feedback loop that locks up the app while resizing.
400411
//
401412
// Use a stored value (instead of a computed value) that is updated on the next run loop to break the loop.
402-
DispatchQueue.main.async { isRailVisible = selectedTabSplitColumnVisibility != .detailOnly }
413+
//
414+
// Only ever hide the rail from here too. Showing it again when the split reports .all would
415+
// evaluate the split at its wider, rail-less width, which flip-flops forever whenever the
416+
// container's width sits between the split's collapse threshold and that threshold plus the
417+
// rail's width. Instead the rail is shown again above, once the container has actually grown.
418+
guard selectedTabSplitColumnVisibility == .detailOnly else { return }
419+
420+
DispatchQueue.main.async {
421+
railHiddenWidth = containerWidth
422+
isRailVisible = false
423+
}
403424
}
404425
}
405426

0 commit comments

Comments
 (0)