最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

swift - How to do full screen navigation destination? - Stack Overflow

programmeradmin1浏览0评论

How to force a view opened by NavigationLink to be fullscreen? I don't want the tab bar to be visible below. I want to keep the ability to swipe from the left side of the screen to go back, so FullScreenCover is not an option. Hiding the tab bar when the details view is open is not a good solution either, since I want the details view to cover the tab bar. In other words, the tab bar should be partially visible beside the details view when the details view is half-swiped to the right. Eventually I want it behaves something like you can see when you half-swipe opened dialog in telegram or whatsapp. Is it even possible purely with swiftUI?

I also want to keep a separate NavigationView for each tab since I want them to have unique titles, etc. So I don't really want to wrap the TabView into single NavigationView.

struct MainTabView: View {
    var body: some View {
        TabView {
            NavigationView {
                List {
                    NavigationLink("First Detail", destination: DetailView(title: "First Detail"))
                    NavigationLink("Second Detail", destination: DetailView(title: "Second Detail"))
                    NavigationLink("Third Detail", destination: DetailView(title: "Third Detail"))
                } .navigationTitle("Home")
            } .tabItem { Label("Home", systemImage: "house") }

            Text("Second Tab") .tabItem { Label("Settings", systemImage: "gear") }
        }
    }
}

struct DetailView: View {
    let title: String

    var body: some View {
        ZStack {
            Color.gray.ignoresSafeArea()
            Text(title).font(.largeTitle)
        }
        .navigationTitle(title)
    }
}

How to force a view opened by NavigationLink to be fullscreen? I don't want the tab bar to be visible below. I want to keep the ability to swipe from the left side of the screen to go back, so FullScreenCover is not an option. Hiding the tab bar when the details view is open is not a good solution either, since I want the details view to cover the tab bar. In other words, the tab bar should be partially visible beside the details view when the details view is half-swiped to the right. Eventually I want it behaves something like you can see when you half-swipe opened dialog in telegram or whatsapp. Is it even possible purely with swiftUI?

I also want to keep a separate NavigationView for each tab since I want them to have unique titles, etc. So I don't really want to wrap the TabView into single NavigationView.

struct MainTabView: View {
    var body: some View {
        TabView {
            NavigationView {
                List {
                    NavigationLink("First Detail", destination: DetailView(title: "First Detail"))
                    NavigationLink("Second Detail", destination: DetailView(title: "Second Detail"))
                    NavigationLink("Third Detail", destination: DetailView(title: "Third Detail"))
                } .navigationTitle("Home")
            } .tabItem { Label("Home", systemImage: "house") }

            Text("Second Tab") .tabItem { Label("Settings", systemImage: "gear") }
        }
    }
}

struct DetailView: View {
    let title: String

    var body: some View {
        ZStack {
            Color.gray.ignoresSafeArea()
            Text(title).font(.largeTitle)
        }
        .navigationTitle(title)
    }
}
Share Improve this question edited Feb 8 at 8:19 routern asked Feb 8 at 8:11 routernroutern 51 silver badge2 bronze badges 4
  • Just write your own tab bar, and hide the built-in one. – Sweeper Commented Feb 8 at 8:25
  • NavigationView is deprecated. If you do not need to support iOS 15 then it would be better to be using a NavigationStack. If you do need to support iOS 15, this would be useful to know. – Benzy Neez Commented Feb 8 at 9:03
  • I know about NavigationStack and use it in the real project, just put NavigationView to keep the code snippet as simple as possible. – routern Commented Feb 8 at 10:10
  • I tried on iPhone 16 (iOS 18.1) – routern Commented Feb 8 at 10:13
Add a comment  | 

1 Answer 1

Reset to default 0

One of the issues in this case is that the tab bar is actually on top of the detail content, not below it. A native tab bar is either visible or hidden, it can't be half visible. So showing half the tab bar as the detail view is swiped away will be difficult.

In order to have more control over the visibility of the tab bar, or tab bar items, a custom tab bar can be used. An example implementation of a custom tab bar can be found in this answer (it was my answer).

Based on the example in that answer, here is how a custom tab bar can be used in this case:

  • Each child view of the TabView has its own copy of the custom tab bar.

  • The custom tab bar is only shown for the top-level view of the NavigationStack (the List view), not for the detail views.

  • This means, when navigating to the detail views of the NavigationStack, the tab bar is "left behind". However, it can be seen in the parent List view when swiping to navigate back.

enum TabType: CaseIterable {
    case home
    case settings

    var titleKey: String {
        "\(self)".capitalized
    }

    var symbolName: String {
        switch self {
        case .home: "house"
        case .settings: "gearshape"
        }
    }
}

struct TabLabelStyle: LabelStyle {
    let isSelected: Bool

    func makeBody(configuration: Configuration) -> some View {
        VStack(spacing: 4) {
            configuration.icon
                .imageScale(.large)
            configuration.title
                .font(.caption)
        }
        .symbolVariant(isSelected ? .fill : .none)
        .foregroundStyle(isSelected ? Color.accentColor : .secondary)
        .frame(maxWidth: .infinity)
    }
}

struct CustomTabBar: View {
    @Binding var selection: TabType

    var body: some View {
        HStack {
            ForEach(TabType.allCases, id: \.self) { type in
                Button {
                    selection = type
                } label: {
                    Label(type.titleKey, systemImage: type.symbolName)
                        .labelStyle(TabLabelStyle(isSelected: selection == type))
                }
            }
        }
        .padding()
    }
}

struct MainTabView: View {
    @State private var selection = TabType.home

    var body: some View {
        TabView(selection: $selection) {
            NavigationStack {
                List {
                    NavigationLink("First Detail") {
                        DetailView(title: "First Detail")
                    }
                    NavigationLink("Second Detail") {
                        DetailView(title: "Second Detail")
                    }
                    NavigationLink("Third Detail") {
                        DetailView(title: "Third Detail")
                    }
                }
                .navigationTitle("Home")
                .safeAreaInset(edge: .bottom) {
                    CustomTabBar(selection: $selection)
                }
            }
            .toolbarVisibility(.hidden, for: .tabBar)
            .tag(TabType.home)

            Text("Second Tab")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .toolbarVisibility(.hidden, for: .tabBar)
                .tag(TabType.settings)
                .safeAreaInset(edge: .bottom) {
                    CustomTabBar(selection: $selection)
                }
        }
    }
}

struct DetailView: View {
    let title: String

    var body: some View {
        ZStack {
            Color.gray.ignoresSafeArea()
            Text(title).font(.largeTitle)
        }
        .navigationTitle(title)
    }
}

发布评论

评论列表(0)

  1. 暂无评论