【问题标题】:SwiftUI hide TabBar in subviewSwiftUI 在子视图中隐藏 TabBar
【发布时间】:2020-02-15 02:05:22
【问题描述】:

我正在使用 SwiftUI,但 TabBar 存在一些问题。 我想隐藏特定子视图上的 TabBar。

试过了

UITabBar.appearance().isHidden = true

它仅适用于 TabView 中的直接视图。但是当我将它放在子视图中时它不起作用。

有没有人解决这个问题?

谢谢。

【问题讨论】:

标签: swift xcode swiftui ios13


【解决方案1】:

我使用的基本思想是结合 ObservableObject 和 ZStack。我已将 TabView 放入带有条件子视图演示的 ZStack 中。 It's look like。 浏览github repo

【讨论】:

  • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
【解决方案2】:

这里没有办法隐藏 TabView,所以我不得不在 ZStack 中添加 TabView,如下所示:

var body: some View {
    ZStack {
        TabView {
            TabBar1().environmentObject(self.userData)
                .tabItem {
                    Image(systemName: "1.square.fill")
                    Text("First")
            }
            TabBar2()
                .tabItem {
                    Image(systemName: "2.square.fill")
                    Text("Second")
            }
        }

        if self.userData.showFullScreen {
            FullScreen().environmentObject(self.userData)

        }
    }
}

用户数据:

  final class UserData: ObservableObject {
    @Published var showFullScreen = false
}

标签栏1:

struct TabBar1: View {
    @EnvironmentObject var userData: UserData

    var body: some View {
        Text("TabBar 1")
            .edgesIgnoringSafeArea(.all)
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
            .background(Color.green)
            .onTapGesture {
                self.userData.showFullScreen.toggle()
        }
    }
}

全屏:

struct FullScreen: View {
    @EnvironmentObject var userData: UserData

    var body: some View {
        Text("FullScreen")
            .edgesIgnoringSafeArea(.all)
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
            .background(Color.red)
            .onTapGesture {
                self.userData.showFullScreen.toggle()
        }
    }
}

Github查看完整代码

还有其他一些方法,但取决于视图的结构

【讨论】:

  • 其实是个好主意
【解决方案3】:

实际上可以通过使用这个方便的小框架为 TabView 获取底层的 UITabbarController :

https://github.com/siteline/SwiftUI-Introspect

此解决方案以 MVVM 模式为例,对 Tabbar 可见性进行编程控制,并能够使用 NSNotifications 在代码中的任何位置显示、隐藏、启用、禁用表单

SwiftUI 视图:像这样设置标签视图

struct MainTabView: View {

var viewModel: MainTabViewModel

var body: some View {

    TabView() {

        Text("View1")
        .tabItem {
            Text("View1")
        }

        Text("View2")
        .tabItem {
            Text("View2")
        }

    }

    .introspectTabBarController { tabBarController in
        // customize here the UITabBarViewController if you like
        self.viewModel.tabBarController = tabBarController
    }

}
}

然后对于 ViewModel

final class MainTabViewModel: ObservableObject {

var tabBarController: UITabBarController?

init() {
    startListeningNotifications()
}

func startListeningNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(showTabbarView), name: "showBottomTabbar", object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(hideTabbarView), name: "hideBottomTabbar", object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(enableTabbarTouch), name: "enableTouchTabbar", object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(disableTabbarTouch), name: "disableTouchTabbar", object: nil)
}

@objc func showTabbarView() {
    self.tabBarController?.tabBar.isHidden = false
}

@objc func hideTabbarView() {
    self.tabBarController?.tabBar.isHidden = true
}

@objc func disableTabbarTouch() {
    self.tabBarController?.tabBar.isUserInteractionEnabled = false
}

@objc func enableTabbarTouch() {
    self.tabBarController?.tabBar.isUserInteractionEnabled = true
}

deinit {
    NotificationCenter.default.removeObserver(self)
}


}

最后要控制标签栏,只需在您喜欢的任何地方使用这些功能(将在本示例的模式中的视图模型中)

public func showTabbar() {
    DispatchQueue.main.async {
        NotificationCenter.default.post(name: .showBottomTabbar, object: nil)
    }
}

public func hideTabbar() {
    DispatchQueue.main.async {
        NotificationCenter.default.post(name: .hideBottomTabbar, object: nil)
    }
}

public func enableTouchTabbar() {
    DispatchQueue.main.async {
        NotificationCenter.default.post(name: .enableTouchTabbar, object: nil)
    }
}

public func disableTouchTabbar() {
    DispatchQueue.main.async {
        NotificationCenter.default.post(name: .disableTouchTabbar, object: nil)
    }
}

【讨论】:

    【解决方案4】:

    它正在工作,只需要在主队列上调用更改

    struct ShowTabBar: ViewModifier {
        func body(content: Content) -> some View {
            return content.padding(.zero).onAppear {
                DispatchQueue.main.async {
                    Tool.showTabBar()
                }
            }
        }
    }
    
    struct HiddenTabBar: ViewModifier {
        func body(content: Content) -> some View {
            return content.padding(.zero).onAppear {
                DispatchQueue.main.async {
                    Tool.hiddenTabBar()
                }
            }
        }
    }
    

    遍历窗口的allsubview,隐藏UITabBar。您可以将其编写为 ViewModifier 并在 SwiftUI 中使用它或使用工具将其隐藏。这种方法对我有用。

        extension UIView {
            
            func allSubviews() -> [UIView] {
                var res = self.subviews
                for subview in self.subviews {
                    let riz = subview.allSubviews()
                    res.append(contentsOf: riz)
                }
                return res
            }
        }
        
        struct Tool {
            static func showTabBar() {
                UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
                    if let view = v as? UITabBar {
                        view.isHidden = false
                    }
                })
            }
            
            static func hiddenTabBar() {
                UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
                    if let view = v as? UITabBar {
                        view.isHidden = true
                    }
                })
            }
        }
        
        struct ShowTabBar: ViewModifier {
            func body(content: Content) -> some View {
                return content.padding(.zero).onAppear {
                    Tool.showTabBar()
                }
            }
        }
        struct HiddenTabBar: ViewModifier {
            func body(content: Content) -> some View {
                return content.padding(.zero).onAppear {
                    Tool.hiddenTabBar()
                }
            }
        }
        
        extension View {
            func showTabBar() -> some View {
                return self.modifier(ShowTabBar())
            }
            func hiddenTabBar() -> some View {
                return self.modifier(HiddenTabBar())
            }
        }
    

    【讨论】:

    • 我也可以正常工作,但我在 iOS 14.0 上遇到了一些问题,例如当用户聚焦文本字段时,即使我隐藏了标签栏,标签栏也会重新出现。有什么想法为什么会发生吗?
    【解决方案5】:

    只需使用 UIKit 的UINavigationController,像这样:

    let host = UINavigationController(rootViewController:
    UIHostingController(rootView: HLHome()))
    

    【讨论】:

      【解决方案6】:

      iOS 14 简单解决方案

      安装 Introspect SwiftPM:https://github.com/siteline/SwiftUI-Introspect

      var body: some View {
          List {
              -your code here-
          }
          
          .navigationBarTitle("Title", displayMode: .inline)
          .introspectTabBarController { (UITabBarController) in
              UITabBarController.tabBar.isHidden = true
          }
      }
      

      注意:您必须在父视图中重新启用 TabBar,否则它仍然会被隐藏。

      .introspectTabBarController { (UITabBarController) in
                  UITabBarController.tabBar.isHidden = false
      }
      

      【讨论】:

      • 它隐藏成功,但是当用户导航回父级时,我无法让它重新启用它。我认为这是因为当用户在导航中向后退时,父视图不会刷新。您是如何解决这个问题的?
      • 工作完美,但不要忘记让你的视图全屏,否则你的视图底部会有很大的差距
      • @Gary 声明一个变量来保存 tabBar 引用,然后同一视图的 .onDisappear() 将其设置回来。 ``` @State private var tabBar: UITabBar? .introspectTabBarController { UITabBarController in tabBar = UITabBarController.tabBar self.tabBar?.isHidden = true } .onDisappear() { self.tabBar?.isHidden = false } ```
      • 嗨,我和 Gary 有同样的问题。标签栏不再出现。有人有解决方案,因为@xdeleons 不起作用
      • 实际上工作不完美:您必须在其他任何地方定义isHidden = false
      【解决方案7】:

      iOS 14

      安装 Introspect SwiftPM:https://github.com/siteline/SwiftUI-Introspect

      struct SomeView: View{
          
          @State var uiTabarController: UITabBarController?
          
          var body: some View {
              List {
                  -your code here-
              }
              
              .navigationBarTitle("Title", displayMode: .inline)
              .introspectTabBarController { (UITabBarController) in
                  UITabBarController.tabBar.isHidden = true
                  uiTabarController = UITabBarController
              }.onDisappear{
                  uiTabarController?.tabBar.isHidden = false
              }
          }
      }
      
      

      uiTabarController 的这段代码中,我们引用了UITabarController。当我们返回时,我们再次启用了Tabar。所以,这就是为什么需要这样做。

      【讨论】:

      • 尝试导航到隐藏标签栏的视图 -> 将应用程序置于后台 -> 恢复应用程序 -> 向后导航。您应该会看到您的内容向下移动了
      • 它是只隐藏标签栏还是将它折叠起来使viewcontroller上方展开整个屏幕?
      • 看起来不错,两件坏事;当标签栏再次出现时,它会慢慢出现。 2. 我们需要把这段代码放在每个视图中……有什么改进的建议吗?
      【解决方案8】:

      将 NavigationView 添加为 root 而不是 TabView

       NavigationView{
          TabView(selection:$selectedIndex) {
           }
       }
      

      如果您不想在 TabBar 页面中使用 NavigationView,只需将其隐藏即可。

      .navigationBarHidden(true)
      

      参考示例项目 - https://github.com/TreatTrick/Hide-TabBar-In-SwiftUI

      【讨论】:

      • 这仅在 iOS 14 中有效,甚至不是每次都有效
      • 隐藏导航栏 - 是,隐藏标签栏 - 否。没用。
      【解决方案9】:

      我遇到了同样的问题,最终使用:

      .opacity(hideTabBar == true ? 0 : 1)
      

      其中 hideTabBar 是一个 Bool,我将它传递给需要隐藏 TabBar() 的视图。

      所以基本上你应该尝试这样的事情:

       TabView(selection: your desired tab).opacity(hideTabBar == true ? 0 : 1)
      

      【讨论】:

        【解决方案10】:

        像这样增加 TabView 的框架大小:

        .frame(width: UIScreen.main.bounds.width, height: showTabbar ? UIScreen.main.bounds.height : UIScreen.main.bounds.height + 100.00)
        

        【讨论】:

        • 它会工作,但 SwiftUI 从中心查看布局,似乎需要其他值才能正确放置它
        【解决方案11】:

        正确的答案是优雅,使用原生 SwiftUI 方法,并允许以任何方式全屏呈现任何类型的视图,任何类型的视图上方如下。 所有其他答案都使用 3rd-party 包,以不可恢复的方式利用 UIKit,或者不优雅。

        首先,您需要将要隐藏的视图声明为允许全屏覆盖的视图。一般的想法是,全屏但您想要隐藏的视图应该有一个首选项键,该键将存储要在其顶部显示的视图:

        /// A preference that allows you to show full screen overlays on top of any top-level full screen view.
        ///
        /// This is particularly useful when trying to present views over a `TabView` or a `NavigationView` which block placing views over
        /// their respective safe zones (like the tab bar or the navigation bar).
        struct FullScreenCoverPreferenceKey: PreferenceKey {
            typealias Value = [OverlayView]
            static var defaultValue: [OverlayView] = []
            
            static func reduce(value: inout [OverlayView], nextValue: () -> [OverlayView]) {
                value.append(contentsOf: nextValue())
            }
            
            /// The underlying overlay view behind the preference key.
            struct OverlayView: View, Identifiable {
                let id = UUID()
                
                var content: AnyView
                
                init<Content: View>(@ViewBuilder content: () -> Content) {
                    self.content = AnyView(content())
                }
                
                var body: some View {
                    content
                }
            }
        }
        

        FullScreenCoverPreferenceKey.value 将包含要在全屏视图顶部显示的视图数组(例如 TabView)。

        然后,使用以下方法扩展View 协议,以便将首选项键附加到视图:

        extension View {
            /// Declares that this view is capable of receiving full screen overlays.
            ///
            /// - Important
            /// This modifier should only be used on views that are taking up the entire screen.
            func allowFullScreenOverlays() -> some View {
                self
                    .overlayPreferenceValue(FullScreenCoverPreferenceKey.self) { overlayViewsArray in
                        ZStack {
                            ForEach(overlayViewsArray) { overlayView in
                                overlayView
                            }
                        }
                    }
            }
        }
        

        overlayPreferenceValue 的返回视图呈现为由preferenceKey 的[OverlayView] 数组构成的ZStack,允许我们将多个叠加层堆叠在一起。

        接下来,创建任何类型的叠加层并将其附加到 TabView 的任何子视图:

        ZStack {
            Color.white.ignoresSafeArea() //background if needed
        
            TabView {
                NavigationView {
                    ZStack {
                        NavigationLink(
                            destination:
                                ZStack { Color.orange }
                                    .transformPreference(FullScreenCoverPreferenceKey.self) {
                                        $0.append(FullScreenCoverPreferenceKey.OverlayView {
                                            //The overlay
                                            ZStack {
                                                Color.green
                                                Text("I am full screen!")
                                            }
                                        })
                                    },
                            label: {
                                Text("Press me to show full screen!")
                            })
                        }
                    }
                    .tabItem {
                        Text("Tab")
                    }
            }
        }
        .allowFullScreenOverlays()
        

        如果你喜欢这样,你可以把这种丑陋的.transformPreference() 调用隐藏在另一个视图修饰符后面:

        extension View {
        /// Adds a full screen overlay to the view.
        ///
        /// Important: The view must have a parent with `.allowsFullScreenOverlays()` set.
        func fullScreenOverlay<Content: View>(@ViewBuilder content: @escaping () -> Content) -> some View {
            self
                .transformPreference(FullScreenCoverPreferenceKey.self) {
                    $0.append(FullScreenCoverPreferenceKey.OverlayView {
                        content()
                    })
                }
        }
        

        最终的视图将如下所示,Tab 被完全覆盖:

        【讨论】:

        • 对不起,但这一点也不优雅。它一团糟,失去了所有的推动动画和手势。没有优雅的答案存在,因为 Apple 在 TabView / NavigationView 中留下了一个巨大的漏洞,并让社区为这样一个在 iOS 上存在了近十年的基本功能提出了黑客攻击。
        • @TruMan1 就 SwiftUI 而言,这是正确的答案,允许您从任何地方呈现全屏视图。它与当前的 SwiftUI 一样优雅。我不覆盖 UIKit 外观,不使用 3rd 方包并使用推荐的 Apple 方式来传递视图层次结构中的值(在本例中为视图)。我很抱歉你不明白,但这是你的意见。希望 SwiftUI 3 能有更好的选择!
        • 您甚至可以将.transformPreference 调用隐藏在视图修饰符后面,因此可重用的API 只有.allowFullScreenOverlays().fullScreenOverlay(content:) 两个方法。它不会比 SwiftUI 的当前状态更优雅。
        【解决方案12】:

        不理想和hacky,但对我来说效果很好的最简单的事情是隐藏外部navigationView的navigationBar,然后在每个TabView的视图中添加另一个navigationView。到目前为止效果很好:

        struct LaunchView: View {
            var body: some View {
                NavigationView {
                    TabView {
                        ViewA()
                            .tabItem {
                                Label("TabA", systemImage: "some.image")
                            }
                        ViewB()
                            .tabItem {
                                Label("TabB", systemImage: "some.image")
                            }
                        ViewC()
                            .tabItem {
                                Label("TabC", systemImage: "some.image")
                            }
                    }
                    .navigationBarHidden(true)
                }
            }
        }
        
        struct ViewA: View {
                
            var body: some View {
                NavigationView {
                    // Content
                    .navigationTitle("Settings")
                }
                .navigationViewStyle(StackNavigationViewStyle())
            }
        }
        

        这样您可以在每个单独的视图中设置标题以及 .toolBarItem。

        【讨论】:

        • 前几天我发现了这个,我对此非常满意。但是,直到今天我只使用 iOS 14 模拟器。如果你使用 iOS 15,你最终会在子视图的顶部有更大的空间,也就是两个 NavigationView。您可以通过删除子视图 NavigationViews 来解决此问题,但随后您会在子视图中获得标签栏? 到目前为止我还没有找到解决方法。
        【解决方案13】:

        iOS 15 解决方案

        除了 SwiftUI.TabView 中的视图修饰符之外,此解决方案效果很好。 由于我的 TabView 位于符合 App 的结构中,因此连接的场景中似乎仍然没有任何 UITabBar 子视图。

        使用下面的代码,您只需要在您的 SwiftUI.View 中使用showTabBar()hiddenTabBar()

        extension UIApplication {
            var key: UIWindow? {
                self.connectedScenes
                    .map({$0 as? UIWindowScene})
                    .compactMap({$0})
                    .first?
                    .windows
                    .filter({$0.isKeyWindow})
                    .first
            }
        }
        
        
        extension UIView {
            func allSubviews() -> [UIView] {
                var subs = self.subviews
                for subview in self.subviews {
                    let rec = subview.allSubviews()
                    subs.append(contentsOf: rec)
                }
                return subs
            }
        }
            
        
        struct TabBarModifier {
            static func showTabBar() {
                UIApplication.shared.key?.allSubviews().forEach({ subView in
                    if let view = subView as? UITabBar {
                        view.isHidden = false
                    }
                })
            }
            
            static func hideTabBar() {
                UIApplication.shared.key?.allSubviews().forEach({ subView in
                    if let view = subView as? UITabBar {
                        view.isHidden = true
                    }
                })
            }
        }
        
        struct ShowTabBar: ViewModifier {
            func body(content: Content) -> some View {
                return content.padding(.zero).onAppear {
                    TabBarModifier.showTabBar()
                }
            }
        }
        struct HiddenTabBar: ViewModifier {
            func body(content: Content) -> some View {
                return content.padding(.zero).onAppear {
                    TabBarModifier.hideTabBar()
                }
            }
        }
        
        extension View {
            
            func showTabBar() -> some View {
                return self.modifier(ShowTabBar())
            }
        
            func hiddenTabBar() -> some View {
                return self.modifier(HiddenTabBar())
            }
        }
        

        【讨论】:

        • 虽然这最初似乎可以正常工作,但如果用户拉动并拖动边缘以向后导航然后松开,标签栏就会出现。
        【解决方案14】:

        一般来说,能够创建没有标签栏的页面是件好事 它看起来很流畅,并且您的页面内容在页面上隐藏标签栏时不会改变它的大小

        解决办法

        • 从根容器中隐藏标签栏
        • 添加自定义标签栏修饰符
        • 在 navViews 上使用此修饰符以显示所有导航视图层次结构的标签栏,或在视图层次结构中的特定页面上使用它

        这是一个小型示例项目,您的应用使用这种方法后的外观 https://github.com/alexis-ag/swiftui_classic-tabview_show-hide

        【讨论】:

        • 在我看来这是解决问题的最佳方法。在 iOS 14 和 15 上测试。
        【解决方案15】:

        安装 Introspect SwiftPM:https://github.com/siteline/SwiftUI-Introspect

        为了使用它,你需要在视图中创建一个类型为 UITabBar 的变量,你希望标签栏被隐藏...

        enter code here
        
        @State private var tabBar: UITabBar?
        

        然后在同一个视图中的navigationView下面,你必须添加这一行:

                    .introspectTabBarController { UITabBarController in tabBar = UITabBarController.tabBar
                        self.tabBar?.isHidden = true } .onDisappear() { self.tabBar?.isHidden = false }
        

        【讨论】:

        • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
        【解决方案16】:

        有可能! 基本上你的任务是以某种方式提取 UITabBar 然后以编程方式隐藏它。

        下面是模拟标签栏隐藏在推送行为的代码。

        struct ContentView: View {
            var body: some View {
                TabView {
                    ForEach(titles, id: \.self) { title in
                        NavigationView {
                            view(fromTitle: title)
                        }
                        .tabItem {
                            Text(title)
                            Image(systemName: "photo")
                        }
                        .tag(title)
                    }
                }
            }
        
            private let titles = ["one", "two"]
        
            @ViewBuilder
            private func view(fromTitle title: String) -> some View {
                if title == "one" {
                    RegularView(title: title)
                        .navigationTitle(title)
                        .navigationBarTitleDisplayMode(.inline)
                } else {
                    HideOnPushView(title: title)
                        .navigationTitle(title)
                        .navigationBarTitleDisplayMode(.inline)
                }
            }
        }
        
        struct RegularView: View {
            let title: String
        
            var body: some View {
                VStack(spacing: 20) {
                    Text(title)
                    NavigationLink("Regular push") {
                        Text("1111111")
                    }
                }
            }
        }
        
        struct HideOnPushView: View {
            let title: String
        
            var body: some View {
                VStack(spacing: 20) {
                    Text(title)
                    NavigationLink("Hide on push") {
                        Text("222222")
                            .onAppear {
                                tabBar?.hide()
                            }
                    }
                }
                    .background(
                        TabBarExtractor(tabBar: $tabBar)
                    )
                    .onAppear {
                        tabBar?.show()
                    }
            }
        
            @State private var tabBar: UITabBar?
        }
        

        TabBar 提取器代码:

        import SwiftUI
        
        struct TabBarExtractor: UIViewControllerRepresentable {
            @Binding var tabBar: UITabBar?
        
            func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
        
            func makeUIViewController(context: Context) -> some UIViewController {
                let controller = ViewController()
                controller.onTabBarAppearance = {
                    tabBar = $0
                }
                return controller
            }
        }
        
        private extension TabBarExtractor {
            class ViewController: UIViewController {
                var onTabBarAppearance: ((UITabBar) -> Void)?
        
                override func viewWillAppear(_ animated: Bool) {
                    super.viewWillAppear(animated)
        
                    if let tabBar = self.tabBarController?.tabBar {
                        onTabBarAppearance?(tabBar)
                    } else {
                        print("Could not locate TabBar! Try change extractor place in views hierarchy.")
                    }
                }
            }
        }
        

        TabBar 类别:

        import UIKit
        
        extension UITabBar {
            func toggleVisibility() {
                if isHidden {
                    show()
                } else {
                    hide()
                }
            }
        
            func show() {
                guard isHidden else { return }
        
                let visibleY = frame.origin.y
                let hiddenY = visibleY + frame.height
                frame.origin.y = hiddenY
                isHidden = false
        
                UIView.animate(withDuration: 0.3) { [weak self] in
                    self?.frame.origin.y = visibleY
                }
            }
        
            func hide() {
                guard !isHidden else { return }
        
                let visibleY = frame.origin.y
                let hiddenY = visibleY + frame.height
        
                UIView.animate(withDuration: 0.3) { [weak self] in
                    self?.frame.origin.y = hiddenY
                } completion: { [weak self] completed in
                    guard completed else { return }
        
                    self?.isHidden = true
                    self?.frame.origin.y = visibleY
                }
            }
        }
        

        【讨论】:

          【解决方案17】:

          我曾尝试使用https://stackoverflow.com/a/62963499/11844048 解决方案,但一旦我登陆此视图,TabBar 就会隐藏在所有视图中。我对其进行了一些修改,以实现在单个视图中隐藏 TabBar。

             struct AppInfoView: View {
                  @Environment(\.presentationMode) var mode: Binding<PresentationMode>
                  var body: some View {
                      ZStack{
                      }
                      .frame(maxWidth: .infinity)
                      .background(Color("homepage_bg")).ignoresSafeArea(.all)
                      .onAppear{
                          UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
                              if let view = v as? UITabBar {
                                  view.isHidden = true
                              }
                          })
                      }
                      .onDisAppear(...) //it works too. But seeing TabBar shown bit delay when naviagting back. So below the customizable back button.
                      .navigationBarBackButtonHidden(true)
                      .navigationBarItems(leading: Button(action : {
                          self.mode.wrappedValue.dismiss()
                          UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.allSubviews().forEach({ (v) in
                              if let view = v as? UITabBar {
                                  view.isHidden = false
                              }
                          })
                      }){
                          Image(systemName: "chevron.left")
                      })
          }
          
          extension UIView {
          
                  func allSubviews() -> [UIView] {
                      var res = self.subviews
                      for subview in self.subviews {
                          let riz = subview.allSubviews()
                          res.append(contentsOf: riz)
                      }
                      return res
                  }
              }
          

          【讨论】:

            【解决方案18】:

            这里的大多数答案都通过以下两种方式之一处理此要求:

            • 导入框架以定位 UITabBarController
            • 修改视图层次结构(ZStack、NavigationView、...)

            第一个是一种简洁的方法:它定位实现所需操作的底层元素。但是,对于单个用例来说可能有点过分了

            第二种方法涉及一些权衡,通常被认为是一种气味,因为它引入了层次结构更改,以解决无法访问所需元素的问题。

            相反,我们可以通过创建如下协议扩展来遵循干净、简单的方法

            import UIKit
            
            protocol TabBarAppearanceDelegate {
                func toggleVisibility()
                func hideTabBar()
                func showTabBar()
                // add more methods to control appearance as needed
            }
            
            extension TabBarAppearanceDelegate {
                private var tabBarController: UITabBarController? {
                    // this is where we access the underlying element, no need to import a framework for a one-liner
                    UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController?.children.first as? UITabBarController
                }
                
                func toggleVisibility() {
                    tabBarController?.tabBar.isHidden.toggle()
                }
                
                func hideTabBar() {
                    tabBarController?.tabBar.isHidden = true
                }
                
                func showTabBar() {
                    tabBarController?.tabBar.isHidden = false
                }
            }
            

            然后我们可以让任何对象符合这个协议,并根据需要将其作为依赖注入到视图中。这取决于您的架构,但可能如下所示。

            这是您保持应用范围状态的位置,ObservableObject(如果愿意,您可以指定其他状态):

            import Foundation
            
            class StateController: ObservableObject {
                // you would typically manage app-wide state here
            }
            
            // this is where we adopt the needed behaviour
            extension StateController: TabBarAppearanceDelegate {}
            

            我们现在可以将对象作为视图依赖注入:

            @main
            struct TabBarVisibilityApp: App {
                private let stateController = StateController()
                
                var body: some Scene {
                    WindowGroup {
                        TabView {
                            NavigationView {
                                SampleView(tabBarAppearanceDelegate: stateController)
                            }
                            .tabItem {
                                Label("Home", systemImage: "house")
                            }
                        }
                    }
                }
            }
            

            这就是你将如何使用它(在任何需要该行为的视图中都有效):

            import SwiftUI
            
            struct SampleView: View {
                let tabBarAppearanceDelegate: TabBarAppearanceDelegate
                
                var body: some View {
                    VStack {
                        Spacer()
                        Button(action: {
                            tabBarAppearanceDelegate.toggleVisibility()
                        } ) {
                            Text("Toggle tab bar visibility")
                        }
                        Spacer()
                    }
                }
            }
            

            这种方法简单、可测试,并且不需要额外的依赖项...直到 Apple 提供了一种使用 SwiftUI API 控制标签栏可见性的直接方法。

            【讨论】:

            • 很好的方法,但它只是使 tabBar 变黑。知道 SwiftUI 将如何扩展以使用该空间吗?
            • 嗯,我不太关注@NicolasDegen。会不会和scrollEdgeAppearance有关? iOS15 中有一些新的行为可能会影响标签栏。例如,请参见此处:stackoverflow.com/a/69467581/1811775
            • 这对我有用,几乎完美,我在我的应用程序文件中使用了@StateObject,在您有示例视图的地方使用了@EnvironmentObject。我还使用了 onAppear 和 onDisappear 来显示/隐藏。但是 onDisappear 再次显示标签栏的速度很慢:(
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-12-15
            • 2021-03-21
            • 2019-10-22
            • 1970-01-01
            • 1970-01-01
            • 2020-09-10
            相关资源
            最近更新 更多