【问题标题】:Navigation + Tabview + Sheet broken in iOS 15iOS 15 中的 Navigation + Tabview + Sheet 损坏
【发布时间】:2021-11-18 15:01:18
【问题描述】:

看起来 Navigation + TabView + Sheet 在 iOS 15 中被破坏了。

当我这样做时: ContentView -> DetailView -> 底页

当底部工作表出现时,Detail 视图会自动从堆栈中弹出: https://www.youtube.com/watch?v=gguLptAx0l4

我希望Detail 视图即使在底部工作表出现时也会保留在那里。有谁知道为什么会发生这种情况以及如何解决它?

这是我的示例代码:

import Combine
import SwiftUI
import RealmSwift

struct ContentView: View {

    var body: some View {
        NavigationView {
            TabView {
                TabItemView(num: 1)
                    .tabItem {
                        Text("One")
                    }
                TabItemView(num: 2)
                    .tabItem {
                        Text("Two")
                    }
            }
        }
    }
}

struct TabItemView: View {

    private let num: Int

    init(num: Int) {
        self.num = num
    }

    var body: some View {
        NavigationLink(destination: DetailView(text: "Detail View \(num)")) {
            Text("Go to Detail View")
        }
    }
}

struct DetailView: View {

    @State private var showingSheet = false

    private let text: String

    init(text: String) {
        self.text = text
    }

    var body: some View {
        Button("Open Sheet") {
            showingSheet.toggle()
        }.sheet(isPresented: $showingSheet) {
            Text("Sheet Text")
        }
    }
}

这适用于 iOS 14 顺便说一句

更新 1:

尝试了@Sebastian 将NavigationView 放入TabView 的建议。虽然这修复了导航错误,但它从根本上改变了行为(我不想在 DetailView 中显示选项卡)。

还尝试了使用IntrospectNavigationLink 目标上设置navigationController.hidesBottomBarWhenPushed = true 的建议,但这并没有做任何事情:

struct ContentView: View {
    
    var body: some View {
        TabView {
            NavigationView {
                TabItemView(num: 1)
            }.tabItem {
                Text("One")
            }
            NavigationView {
                TabItemView(num: 2)
            }.tabItem {
                Text("Two")
            }
        }
    }
}

struct TabItemView: View {
    
    private let num: Int
    
    init(num: Int) {
        self.num = num
    }
    
    var body: some View {
        NavigationLink(destination: DetailView(text: "Detail View \(num)").introspectNavigationController { navigationController in
            navigationController.hidesBottomBarWhenPushed = true
        }) {
            Text("Go to Detail View")
        }
    }
}

struct DetailView: View {
    
    @State private var showingSheet = false
    
    private let text: String
    
    init(text: String) {
        self.text = text
    }
    
    var body: some View {
        Button("Open Sheet") {
            showingSheet.toggle()
        }.sheet(isPresented: $showingSheet) {
            Text("Sheet Text")
        }
    }
}

【问题讨论】:

  • 这比我想象的要复杂!我尝试保存 NavigationLink 状态,保存 TabView 选择状态,并将工作表向上移动到 TabView 或 NavigationView 级别。运气不好!
  • 这里也一样,并且没有真正的解决方法,即使下面的答案也可以,但是如果我们使用 fullScreenCover 或 sheet,navigationLink 会弹出到预览视图。
  • @skywalkerdude 你找到解决方案了吗?

标签: swiftui swiftui-navigationview ios15 swiftui-tabview


【解决方案1】:

您需要翻转TabViewNavigationView 的嵌套方式。不要在NavigationView 中嵌套多个TabView 视图,而是使用TabView 作为父组件,每个选项卡都有一个NavigationView

这是更新后的ContentView 的样子:

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationView {
                TabItemView(num: 1)
            }
            .tabItem {
                Text("One")
            }
            
            NavigationView {
                TabItemView(num: 2)
            }
            .tabItem {
                Text("Two")
            }
        }
    }
}

这是有道理的,而且更正确:标签应该始终可见,但您希望在每个标签中显示具有不同内容的不同导航堆栈。

它以前的工作并没有使它更正确 - SwiftUI 可能只是改变了处理意外情况的想法。这一点,以及在这些情况下没有错误消息,是使用一个试图渲染你扔给它的任何东西的框架的缺点!


如果目标是在 NavigationView 上推送新视图时(例如,在消息应用程序中点击对话时)隐藏选项卡,则必须使用不同的解决方案。 Apple 将 UIViewController.hidesBottomBarWhenPushed 属性添加到 UIKit 以支持此特定用例。

这个属性是在 UIViewController 上设置的,当它出现时,它不应该显示一个工具栏。换句话说:不是 UINavigationController 或 UITabBarController,而是您推送到 UINavigationController 上的子 UIViewController。

SwiftUI 本机不支持此属性。您可以使用SwiftUI-Introspect 设置它,或者简单地使用 UIKit 编写应用程序的导航结构,然后在 SwiftUI 中编写视图,使用UIHostingViewController 链接它们。

【讨论】:

  • 有趣。为什么在 NavigationViews 中嵌套 TabView 不正确?让标签始终可见或让屏幕出现并覆盖标签不是产品/设计决定吗?例如,Instagram 底部有 5 个标签,但如果您点击右上角的消息,消息视图会完全覆盖这些标签。这是我不知道的反模式吗?
  • 通过设置targetViewController.hidesBottomBarWhenPushed = true 以不同方式支持您提到的行为。 (此标志将隐藏任何底部栏 - 选项卡栏和工具栏,就像在邮件应用程序中一样。)
  • 我应该补充一点:据我所知,hidesBottomBarWhenPushed 尚未内置到 SwiftUI 中,因此您必须使用 SwiftUI-Introspect 或类似的东西来设置该标志。
  • 你能解释一下如何使用hidesBottomBarWhenPushed = true吗?我导入了 SwiftUI-introspect,但我无法让底部栏消失。我把它放在NavigationViewTabView上,都无济于事
  • TabView{...}.introspectTabBarController{tabView in tabView.hidesBottomBarWhenPushed = true }
猜你喜欢
  • 2021-11-27
  • 2021-12-02
  • 1970-01-01
  • 2021-11-20
  • 1970-01-01
  • 2017-02-24
  • 2014-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多