有没有一种简单的方法可以使用 SwiftUI 获得更可定制的选项卡栏 View ?我主要是从 macOS 的角度来问(尽管在任何系统上都可以工作的系统是理想的),因为标准系统的 macOS 实现存在各种问题:
- 它周围有一个圆形边框,这意味着它与 subview 中的任何背景颜色一起看起来都很糟糕。
- 它不支持选项卡图标。
- 在定制方面非常有限。
- 它有问题(有时它不会按预期切换 View )。
- 看起来相当过时。
当前代码:
import SwiftUI
struct SimpleTabView: View {
@State private var selection = 0
var body: some View {
TabView(selection: $selection) {
HStack {
Spacer()
VStack {
Spacer()
Text("First Tab!")
Spacer()
}
Spacer()
}
.background(Color.blue)
.tabItem {
VStack {
Image("icons.general.home")
Text("Tab 1")
}
}
.tag(0)
HStack {
Spacer()
VStack {
Spacer()
Text("Second Tab!")
Spacer()
}
Spacer()
}
.background(Color.red)
.tabItem {
VStack {
Image("icons.general.list")
Text("Tab 2")
}
}
.tag(1)
HStack {
Spacer()
VStack {
Spacer()
Text("Third Tab!")
Spacer()
}
Spacer()
}
.background(Color.yellow)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.tabItem {
VStack {
Image("icons.general.cog")
Text("Tab 3")
}
}
.tag(2)
}
}
}
最佳答案
为了解决这个问题,我整理了以下简单的自定义 View ,它提供了与 iOS 更相似的选项卡界面,即使在 Mac 上运行也是如此。它的工作原理只是采用一组元组,每个元组概述了选项卡的标题、图标名称和内容。
它可以在浅色和深色模式下工作,并且可以在 macOS 或 iOS/iPadOS/等上运行,但您可能只想使用标准 TabView
在iOS上运行时的实现;由你决定。
它还包含一个参数,以便您可以根据偏好将栏定位在顶部或底部(整个顶部更符合 macOS 准则)。
以下是结果示例(在深色模式下):
这是代码。一些注意事项:
- 它使用
Color
的基本扩展,因此可以使用系统背景颜色,而不是硬编码。 - 唯一稍微有点 hack 的部分是额外的
background
和shadow
修饰符,它们是防止 SwiftUI 将阴影应用到每个 subview 所必需的( !)。当然,如果您不想要阴影,则可以删除所有这些行(包括zIndex
)。
Swift v5.1:
import SwiftUI
public extension Color {
#if os(macOS)
static let backgroundColor = Color(NSColor.windowBackgroundColor)
static let secondaryBackgroundColor = Color(NSColor.controlBackgroundColor)
#else
static let backgroundColor = Color(UIColor.systemBackground)
static let secondaryBackgroundColor = Color(UIColor.secondarySystemBackground)
#endif
}
public struct CustomTabView: View {
public enum TabBarPosition { // Where the tab bar will be located within the view
case top
case bottom
}
private let tabBarPosition: TabBarPosition
private let tabText: [String]
private let tabIconNames: [String]
private let tabViews: [AnyView]
@State private var selection = 0
public init(tabBarPosition: TabBarPosition, content: [(tabText: String, tabIconName: String, view: AnyView)]) {
self.tabBarPosition = tabBarPosition
self.tabText = content.map{ $0.tabText }
self.tabIconNames = content.map{ $0.tabIconName }
self.tabViews = content.map{ $0.view }
}
public var tabBar: some View {
HStack {
Spacer()
ForEach(0..<tabText.count) { index in
HStack {
Image(self.tabIconNames[index])
Text(self.tabText[index])
}
.padding()
.foregroundColor(self.selection == index ? Color.accentColor : Color.primary)
.background(Color.secondaryBackgroundColor)
.onTapGesture {
self.selection = index
}
}
Spacer()
}
.padding(0)
.background(Color.secondaryBackgroundColor) // Extra background layer to reset the shadow and stop it applying to every sub-view
.shadow(color: Color.clear, radius: 0, x: 0, y: 0)
.background(Color.secondaryBackgroundColor)
.shadow(
color: Color.black.opacity(0.25),
radius: 3,
x: 0,
y: tabBarPosition == .top ? 1 : -1
)
.zIndex(99) // Raised so that shadow is visible above view backgrounds
}
public var body: some View {
VStack(spacing: 0) {
if (self.tabBarPosition == .top) {
tabBar
}
tabViews[selection]
.padding(0)
.frame(maxWidth: .infinity, maxHeight: .infinity)
if (self.tabBarPosition == .bottom) {
tabBar
}
}
.padding(0)
}
}
这是一个如何使用它的示例。显然,您也可以向它传递一个完全自定义的 subview ,而不是像这样动态构建它们。只需确保将它们包装在 AnyView
初始值设定项内即可。
图标及其名称是自定义的,因此您必须使用自己的替代品。
struct ContentView: View {
var body: some View {
CustomTabView(
tabBarPosition: .top,
content: [
(
tabText: "Tab 1",
tabIconName: "icons.general.home",
view: AnyView(
HStack {
Spacer()
VStack {
Spacer()
Text("First Tab!")
Spacer()
}
Spacer()
}
.background(Color.blue)
)
),
(
tabText: "Tab 2",
tabIconName: "icons.general.list",
view: AnyView(
HStack {
Spacer()
VStack {
Spacer()
Text("Second Tab!")
Spacer()
}
Spacer()
}
.background(Color.red)
)
),
(
tabText: "Tab 3",
tabIconName: "icons.general.cog",
view: AnyView(
HStack {
Spacer()
VStack {
Spacer()
Text("Third Tab!")
Spacer()
}
Spacer()
}
.background(Color.yellow)
)
)
]
)
}
}
关于macos - SwiftUI:适用于 macOS 和 iOS 的自定义选项卡 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60674035/