首页 > 解决方案 > 如何在 SwiftUI 中使用 NavigationView 创建自定义 TabView?

问题描述

[编辑] - 这个问题已经过编辑和简化。

我需要创建一个 CustomLooking TabView 而不是默认的。

这是我解决问题的完整代码。只需运行下面的代码。

import SwiftUI

enum TabName {
    case explore, network
}

struct ContentView: View {
    @State var displayedTab: TabName = .explore
    var body: some View {
        VStack(spacing: 0) {
            Spacer()
            switch displayedTab {
            case .explore: AViewWhichNavigates(title: "Explore").background(Color.yellow)
            case .network: AViewWhichNavigates(title: "Network").background(Color.green)
            }
            Spacer()
            CustomTabView(displayedTab: $displayedTab)
        }
    }
}

struct CustomTabView: View {
    @Binding var displayedTab: TabName
    var body: some View {
        HStack {
            Spacer()
            Text("Explore").border(Color.black, width: 1).onTapGesture { self.displayedTab = .explore }
            Spacer()
            Text("Network").border(Color.black, width: 1).onTapGesture { self.displayedTab = .network }
            Spacer()
        }
    }
}

struct AViewWhichNavigates: View {
    let title: String
    var body: some View {
        NavigationView(content: {
            NavigationLink(destination: Text("We are one level deep in navigation")) {
                Text("You are at root. Tap to navigate").navigationTitle(title)
            }
        })
    }
}

在 tab#1 上单击导航。切换到标签#2,然后切换回标签#1。您将看到 tab#1 已弹出到根目录。

每次切换标签时,如何防止 customTabView 弹出到根目录?

标签: swiftswiftui

解决方案


问题是未记录 Navigation isActive 状态以及显示的选项卡状态。

通过记录每个选项卡的导航状态以及哪个选项卡处于活动状态,可以为每个选项卡显示正确的导航状态。

可以改进模型以删除元组并使其更灵活,但关键是使用 getter 和 setter 来使用每个选项卡的导航状态的封装模型,以便允许 NavigationLink 通过捆绑。

我已经简化了顶层 VStack 并删除了顶层开关,因为这里不需要它,但可以添加回来以便在实际实现中在顶层使用不同类型的视图


enum TabName : String {
    case Explore, Network
}

struct ContentView: View {
    
    @State var model =  TabModel()
    
    init(){
         UINavigationBar.setAnimationsEnabled(false)
    }
    
    var body: some View {
        VStack(spacing: 0) {
            Spacer()
            AViewWhichNavigates(model: $model).background(Color.green)
            Spacer()
            CustomTabView(model:$model)
        }
    }
}

struct CustomTabView: View {
    @Binding var model: TabModel
    var body: some View {
        HStack {
            Spacer()
            Text("Explore").border(Color.black, width: 1).onTapGesture { model.selectedTab = .Explore }
            Spacer()
            Text("Network").border(Color.black, width: 1).onTapGesture { model.selectedTab = .Network }
            Spacer()
        }
    }
}

struct AViewWhichNavigates: View {
    @Binding var model:TabModel
    
    var body: some View {
        NavigationView(content: {
            NavigationLink(destination: Text("We are one level deep in navigation in \(model.selectedTab.rawValue)"), isActive: $model.isActive) {
                Text("You are at root of \(model.selectedTab.rawValue). Tap to navigate").navigationTitle(model.selectedTab.rawValue)
            }.onDisappear {
                UINavigationBar.setAnimationsEnabled(model.isActive)
            }
        })
    }
}

struct TabModel {
    var selectedTab:TabName = .Explore
    var isActive : Bool {
        get {
            switch selectedTab {
                case .Explore : return tabMap.0
                case .Network : return tabMap.1
            }
        }
        set {
            switch selectedTab {
                case .Explore : nOn(isActive, newValue); tabMap.0 = newValue;
                case .Network : nOn(isActive, newValue); tabMap.1 = newValue;
            }
        }
    }
    //tuple used to represent a fixed set of tab isActive navigation states
    var tabMap = (false, false)
     
    func  nOn(_ old:Bool,_ new:Bool ){
        UINavigationBar.setAnimationsEnabled(new && !old)
    }
    
}

推荐阅读