// BottomNavigationBarView.swift
// TrexlerLibrary
//
// Created by Emanuel Luna on 2/3/24.
//
import SwiftUI
import UserNotifications
enum ActiveView: String, Hashable {
case home
case search
case settings
}
struct MainView: View {
@Environment(\.horizontalSizeClass) var sizeClass
@State private var activeView: ActiveView? = .home
var body: some View {
Group {
if sizeClass == .compact {
iphoneTabView
} else {
ipadExpansiveView
}
}
.onAppear {
InfoForLibrary().requestNotificationPermission()
}
}
private var iphoneTabView: some View {
TabView(selection: $activeView) {
NavigationView { HomeView() }
.tabItem {
Label("Home", systemImage: "house")
}
.tag(ActiveView.home)
NavigationView { SearchView() }
.tabItem {
Label("Search", systemImage: "magnifyingglass")
}
.tag(ActiveView.search)
NavigationView { SettingsView() }
.tabItem {
Label("Settings", systemImage: "gear")
}
.tag(ActiveView.settings)
}
}
private var ipadExpansiveView: some View {
NavigationView {
List {
NavigationLink(destination: HomeView(), tag: ActiveView.home, selection: $activeView) {
Label("Home", systemImage: "house")
}
NavigationLink(destination: SearchView(), tag: ActiveView.search, selection: $activeView) {
Label("Search", systemImage: "magnifyingglass")
}
NavigationLink(destination: SettingsView(), tag: ActiveView.settings, selection: $activeView) {
Label("Settings", systemImage: "gear")
}
}
.listStyle(SidebarListStyle())
.navigationTitle("Menu")
// Render the currently selected view
currentView
}
}
private var currentView: some View {
switch activeView {
case .home:
return AnyView(HomeView())
case .search:
return AnyView(SearchView())
case .settings:
return AnyView(SettingsView())
case .none:
return AnyView(HomeView())
}
}
}
// Preview
#Preview {
MainView().environmentObject(UserSession())
}
这是我的主要观点。当我使用我的应用程序时,它通常会打开 HomeView,但是当我使用带有分割视图的 iPad 并将应用程序移动到紧凑尺寸或常规尺寸时,我当前所在的视图会移回到 HomeView。
如何让我的应用程序在大小发生变化时保持当前所在的视图?
此行为可能是因为
activeView
的类型与您分配给每个选项卡的 tag
的类型不同。 ActiveTab?
vs ActiveTab
。也就是说,你写的方式ipadExpansiveView
也是有问题的。
对于 iOS 17,您应该迁移到
NavigationStack
和 NavigationSplitView
。
首先,将标题、图像名称和每个页面的视图放入
ActiveView
枚举中。这使得从 ActiveView
值获取这些属性变得更加方便。我还制作了它 CaseIterable
,以便稍后我们可以使用 ForEach
循环遍历它的案例。
enum ActiveView: String, Hashable, CaseIterable {
case home
case search
case settings
var displayName: String {
switch self {
case .home:
"Home"
case .search:
"Search"
case .settings:
"Settings"
}
}
var systemImageName: String {
switch self {
case .home:
"house"
case .search:
"magnifyingglass"
case .settings:
"gear"
}
}
@ViewBuilder
var view: some View {
switch self {
case .home:
Text("Home")
case .search:
Text("Search")
case .settings:
Text("Settings")
}
}
}
我还会将
iPhoneTabView
和 iPadExpansiveView
分开 View
结构:
struct iPhoneTabView: View {
@Binding var activeView: ActiveView
var body: some View {
TabView(selection: $activeView) {
ForEach(ActiveView.allCases, id: \.self) { tab in
NavigationStack { tab.view }
.tabItem {
Label(tab.displayName, systemImage: tab.systemImageName)
}
}
}
}
}
struct iPadExpansiveView: View {
@Binding var activeView: ActiveView
var body: some View {
NavigationSplitView {
List(ActiveView.allCases, id: \.self, selection: Binding($activeView)) { tab in
NavigationLink(value: tab) {
Label(tab.displayName, systemImage: tab.systemImageName)
}
}
} detail: {
activeView.view
}
}
}
NavigationSplitView
由List
的选择驱动。 selection:
参数需要一个 Binding<ActiveView?>
,但是没有 no 活动视图是没有意义的,不是吗?因此,iPadExpansiveView
采用非可选的ActiveView
绑定,并在将其传递给Binding<ActiveView?>
之前将其转换为selection:
。
MainView
看起来像这样,具有非可选的 activeView
状态。
@Environment(\.horizontalSizeClass) var sizeClass
@State private var activeView: ActiveView = .home
var body: some View {
Group {
if sizeClass == .compact {
iPhoneTabView(activeView: $activeView)
} else {
iPadExpansiveView(activeView: $activeView)
}
}
}