siteline / swiftui-introspect

Introspect underlying UIKit/AppKit components from SwiftUI
MIT License
5.58k stars 344 forks source link

Bottom bar showing on every screen in SwiftUI in iOS 15 #137

Closed GovindAgarwa closed 1 year ago

GovindAgarwa commented 2 years ago

Hi
Bottom bar showing every screen So I want to hide this bar when user will click on card(refer screenshots).

To Solve this issue

I am using Introspect with the code .introspectNavigationController { nav in nav.hidesBottomBarWhenPushed = true } but its not working in SwiftUI iOS 15.

Please let me know proper solution to hide Bottom bar. Thanks in advance

simulator_screenshot_3A881CD2-4A77-4202-8F47-DF3D4AD21C8E

PatrikTheDev commented 2 years ago

I think this is due to the order of operations, you can only introspect it after it has been initialized and being pushed on the screen

Furthermore, you're trying to introspect the navigation controller, that's incorrect, try introspecting the child view controller

alexanderkhitev commented 1 year ago

Hey guys! I trued multiple options but no one works for me. Do you have any news/updates? Thanks!

davdroman commented 1 year ago

A bit late to this, but @PatrikTheDev is right on the money, and there's really nothing this library alone can do to work around the order of operations. So unfortunately I'm closing this for now.

honkmaster commented 1 year ago

FYI: introspectNavigationController never worked for us reliably too when we want to hide the bottom bar on specific views. What seemed to be quite reliable however, was to use introspectViewController and add an additional guard to compare the topViewController.

introspectViewController {
  guard $0.navigationController?.topViewController == $0 else { return }
  $0.navigationController?.hidesBottomBarWhenPushed = true
}
davdroman commented 1 year ago

Hmm interesting. Can you provide a minimal reproducible example of this? I'm trying to replicate it but it's still not working for me. I'd really appreciate it. Here are my attempts so far:

NavigationStack

import SwiftUI
import Introspect

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationStack {
                NavigationLink("Tap Me") {
                    Text("Detail View")
                        .introspectViewController {
                            guard $0.navigationController?.topViewController == $0 else { return }
                            $0.navigationController?.hidesBottomBarWhenPushed = true
                        }
                }
                .navigationTitle("Primary View")
            }
            .tabItem {
                Label("Home", systemImage: "house")
            }
        }
    }
}

NavigationView

import SwiftUI
import Introspect

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationView {
                NavigationLink("Hello") {
                    Text("Hello, World!")
                        .introspectViewController {
                            guard $0.navigationController?.topViewController == $0 else { return }
                            $0.navigationController?.hidesBottomBarWhenPushed = true
                        }
                }
                .navigationBarTitle("Page One")
            }
            .tabItem {
                Image(systemName: "1.circle")
                Text("Home")
            }

        }
    }
}
davdroman commented 1 year ago

Digging even deeper into this, sadly I think this is virtually impossible unless Apple makes some changes to UIKit itself.

I believe there's some internal logic in UIKit which checks if the currently selected tab's root view controller type is UINavigationController to determine whether the tab bar should be hidden during a push transition, and if it's not, any hidesBottomBarWhenPushed gets ignored completely.

The way TabView (UITabBarController) in SwiftUI sets up its viewControllers is by first wrapping them in a UIHostingController, so there will never be a UINavigationController at the root of a tab, hence hidesBottomBarWhenPushed is always ignored.

You can verify this by running the following demo I put together: https://github.com/davdroman/SwiftUIHidesBottomBarWhenPushedExperiment