iOS 15 中 Navigation、Tabview 和 Sheet 功能存在问题

10

看起来在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中显示选项卡)。

还尝试了他使用Introspect的建议,在NavigationLink目标上设置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")
        }
    }
}

1
这比我想象的要棘手!我尝试过保存NavigationLink状态,保存TabView选择状态,并将sheet移动到TabView或NavigationView级别。没有成功! - Adam
我也遇到了同样的问题,而且似乎没有真正的解决方法。即使使用下面的答案,如果我们使用fullScreenCover或sheet,navigationLink也会弹出到上一个视图。 - finalpets
@skywalkerdude 你找到解决方案了吗? - Lukas
@Lukas 我最终只编写了自己的TabView来解决这个问题。 - skywalkerdude
@SYL,这有点难以分享,因为我是在一年前完成的。但是要点是,我没有使用 SwiftUI 提供的 TabView,而是使用 SwiftUI 组件和 GeometryReaders 实现了自己的 TabView。 - skywalkerdude
显示剩余3条评论
1个回答

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 上推出新视图时隐藏选项卡(例如,在消息应用程序中点击对话时),则必须使用其他解决方案。苹果添加了 UIViewController.hidesBottomBarWhenPushed 属性到 UIKit 中以支持这个特定的用例。
该属性设置在不应显示工具栏的 UIViewController 上。换句话说:不是 UINavigationController 或 UITabBarController,而是您将其推入 UINavigationController 的子视图控制器。
该属性在 SwiftUI 中不受原生支持。您可以使用 SwiftUI-Introspect 设置它,或者简单地使用 UIKit 写出应用程序的导航结构,并在其中编写用 SwiftUI 编写的视图,使用 UIHostingViewController 进行链接。

你提到的行为可以通过不同的方式进行支持,即设置 targetViewController.hidesBottomBarWhenPushed = true。(这个标志将隐藏任何底部栏 - 如邮件应用中的选项卡栏和工具栏。) - Sebastian
你能解释一下如何使用 hidesBottomBarWhenPushed = true 吗?我已经导入了 SwiftUI-introspect,但无法使底部栏消失。我在 NavigationViewTabView 上都尝试了,但都没有成功。 - skywalkerdude
@skywalkerdude - 你应该将“hidesBottomBarWhenPushed”应用于你要推送的UIViewController,而不是UITabBarController(= TabBar)或UINavigationController(= NavigationView)。这将是你的NavigationLink的目标视图。(我对Introspect并不是很熟悉,所以我不知道它如何工作。) - Sebastian
@finalpets 这听起来像是一个需要代码示例的单独问题! :-) - Sebastian
抱歉,它仍然无法正常工作。我尝试将其添加到“DetailView”中,因为那是“我正在推送的UIViewController”,我还使用了introspectViewController并添加了日志记录和断点,以确保viewController实际上不是UITabBarControllerUINavigationController。行为完全相同。我的分支在这里:https://github.com/skywalkerdude/navsample/blob/sebastian-suggestion/NavSample/ContentView.swift - skywalkerdude
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接