iOS 26: Enabling "Reduce Transparency" causes a persistent white bar where the tab bar was hidden, blocking user interaction

Hi everyone,

We're experiencing a bug on iOS 26 that only occurs when the user has Reduce Transparency enabled in Accessibility settings.

App structure:

Our app uses a TabView with a standard tab bar. Inside each tab, we use a NavigationStack. The tab bar is visible on root-level screens, and hidden on all pushed destinations using:

.toolbar(.hidden, for: .tabBar)

The problem:

On iOS 26 with Reduce Transparency off (Liquid Glass active) — everything works correctly. The tab bar hides as expected.

On iOS 26 with Reduce Transparency on — a white bar appears at the bottom of the screen in every place where the tab bar is hidden.

This white bar:

Overlaps content at the bottom of the screen. Blocks scroll, tap, and all user interactions in that area.

We also tried:

  • .toolbarBackground(.hidden, for: .tabBar)
  • Removing all custom UITabBarAppearance configuration

The only workaround we found is setting UIDesignRequiresCompatibility = YES in Info.plist, which reverts the entire app to the pre-iOS 26 design — not a viable long-term solution.

What can we do? Thanks in advance.

Are you able to share a code snippet that reproduces the issue?

I want to make sure there's no differences between our projects when following the steps to reproduce.

To help us get ahead start on this issue, share this code in a bug report filed through Feedback Assistant. Be sure to include a minimal Xcode project that's builds, runs and demonstrates the issue. Include steps to reproduce, affected platforms/versions and any other relevant info needed.

Once you have filed, share the feedback number in this thread, I will make sure that report is seen by the relevant engineering team.

 Travis

@DTS Engineer

Sure! Please try this code with the "Settings → Accessibility → Display & Text Size → Reduce Transparency" option enabled.

Feedback ID: FB22913365

import SwiftUI

struct ContentView: View {
    @State var tab = 0
    
    var body: some View {
        TabView(selection: $tab) {
            TabContentView()
                .tabItem {
                    Label("One", image: "")
                }
                .tag(0)
            
            TabContentView()
                .tabItem {
                    Label("Two", image: "")
                }
                .tag(1)
        }
    }
}

struct TabContentView: View {
    @State var path = [Int]()
    
    var body: some View {
        NavigationStack(path: $path) {
            NavigationStackFirstView(onNext: { path.append(0) })
                .navigationDestination(for: Int.self) { destination in
                    Group {
                        switch destination {
                        case 0:
                            NavigationStackSecondView()
                        default:
                            EmptyView()
                        }
                    }
                    .toolbar(.hidden, for: .tabBar)
                }
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct NavigationStackFirstView: View {
    let onNext: () -> Void
    
    var body: some View {
        VStack {
            Text("View with tabbar")
                .font(.system(size: 24))
                .padding()
            
            Button("Go to view without tabbar") {
                onNext()
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.pastelGray0)
    }
}

struct NavigationStackSecondView: View {
    var body: some View {
        ScrollView {
            VStack(spacing: 0) {
                ForEach(0..<25) { index in
                    Text("Line \(index)")
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(index.isMultiple(of: 2) ? Color.pastelGray2 : Color.pastelGray3)
                }
            }
            .padding(.vertical)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.pastelGray1)
    }
}

extension Color {
    static let pastelGray0 = Color(red: 0.88, green: 0.88, blue: 0.90)
    static let pastelGray1 = Color(red: 0.82, green: 0.83, blue: 0.85)
    static let pastelGray2 = Color(red: 0.75, green: 0.76, blue: 0.78)
    static let pastelGray3 = Color(red: 0.68, green: 0.69, blue: 0.71)
}

I just ran into this issue in one of my apps. With Reduce Transparency enabled on iOS 26, hiding a SwiftUI tab bar could leave its opaque background and safe-area reservation behind.

The good news is that it appears to be fixed in iOS 27 (24A5390f). But while iOS 27 is just around the corner, many people typically take a while to upgrade, so I wanted a workaround.

I mainly work in SwiftUI, so got help from an LLM to build this UIKit bridge. It solves the issue in my app for both zoom and push/pop transitions (parent view has both grid and list layouts).

WORKAROUND

The app still uses SwiftUI as the source of truth for tab bar visibility. In simplified form:

NavigationStack(path: $path) {
    // Root content and navigation destinations
}
.toolbarVisibility(
    path.count == 0 ? .automatic : .hidden,
    for: .tabBar
)

I then apply the workaround to the destination where the tab bar should remain hidden:

DetailView()
    .applyIOS26TabBarVisibilityWorkaround()

The helper only reasserts the hidden state through UIKit. It intentionally does not show the tab bar during teardown, because that can conflict with another destination or an unchanged SwiftUI visibility preference. The parent-owned .toolbarVisibility modifier remains responsible for restoring the bar.

import SwiftUI
import UIKit

extension View {
    /// Works around the iOS 26 tab-bar safe-area bug when Reduce Transparency is enabled.
    func applyIOS26TabBarVisibilityWorkaround() -> some View {
        background(IOS26TabBarVisibilityWorkaround())
    }
}

/// Uses UIKit because SwiftUI can leave the iOS 26 tab-bar safe area behind after hiding the bar.
private struct IOS26TabBarVisibilityWorkaround: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> Controller {
        Controller()
    }

    func updateUIViewController(_ uiViewController: Controller, context: Context) {
        uiViewController.hideTabBar()
    }

    static func dismantleUIViewController(
        _ uiViewController: Controller,
        coordinator: ()
    ) {
        uiViewController.stopApplyingWorkaround()
    }

    final class Controller: UIViewController {
        private weak var trackedTabBarController: UITabBarController?
        private var shouldApplyWorkaround = false

        override func loadView() {
            let view = UIView()
            view.backgroundColor = .clear
            view.isUserInteractionEnabled = false
            self.view = view
        }

        func hideTabBar() {
            shouldApplyWorkaround = true
            applyHiddenTabBarVisibility()
        }

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            applyHiddenTabBarVisibility()
        }

        override func didMove(toParent parent: UIViewController?) {
            super.didMove(toParent: parent)
            guard parent != nil else { return }
            applyHiddenTabBarVisibility()
        }

        func stopApplyingWorkaround() {
            shouldApplyWorkaround = false
            trackedTabBarController = nil
        }

        private func applyHiddenTabBarVisibility() {
            guard shouldApplyWorkaround, parent != nil else { return }
            guard #available(iOS 26.0, *) else { return }
            if #available(iOS 27.0, *) { return }

            guard let tabBarController = resolveTabBarController() else {
                return
            }

            trackedTabBarController = tabBarController

            // Deliberately call this even if isTabBarHidden is already true.
            // Reasserting the state is what corrects the stale iOS 26 layout.
            tabBarController.setTabBarHidden(true, animated: false)
        }

        private func resolveTabBarController() -> UITabBarController? {
            if let tabBarController {
                return tabBarController
            }

            if let tabBarController = parent?.tabBarController {
                return tabBarController
            }

            var ancestor = parent
            while let current = ancestor {
                if let tabBarController = current as? UITabBarController {
                    return tabBarController
                }
                ancestor = current.parent
            }

            var responder: UIResponder? = viewIfLoaded
            while let current = responder {
                if let tabBarController = current as? UITabBarController {
                    return tabBarController
                }
                responder = current.next
            }

            return trackedTabBarController
        }
    }
}

I can’t say this is the cleanest solution, but it fixes the issue for me while leaving the normal SwiftUI navigation transitions intact.

SCREENSHOTS

And here's the resulting detail view before and after the workaround on iOS 26:

Thank you for reporting @MaxT,

This appears to be resolved in a 27 beta release, you can test it yourself on the latest beta release.

In the meantime, use the workaround provided by @Bikrrr

 Travis

iOS 26: Enabling "Reduce Transparency" causes a persistent white bar where the tab bar was hidden, blocking user interaction
 
 
Q