如何在 SwiftUi 中使 .sheet 呈现在选项卡栏上方而不覆盖它?我见过一种“解决方案”,但在 SwiftUi 中却没有。 到目前为止,我尝试的任何操作都只是将工作表放置在标签栏上
import SwiftUI
struct ContentView: View {
@State private var showSheet = true
var body: some View {
TabView {
HomeView(showSheet: $showSheet)
.tabItem {
Label("Home", systemImage: "house")
}
Text("Second Tab")
.tabItem {
Label("Second", systemImage: "2.circle")
}
}
}
}
struct HomeView: View {
@Binding var showSheet: Bool
var body: some View {
VStack {
Button("Show TabView Sheet") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
SheetContent()
.presentationDetents([.medium, .large])
.interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled(upThrough: .large))
}
}
}
}
struct SheetContent: View {
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
// Add more content here as needed
}
}
}
#Preview {
ContentView()
}
.clear
,然后向工作表内容添加不透明背景。如果您在底部添加一些填充,选项卡按钮将能够显示出来。
但是,当我尝试这样做时,我发现纸张后面的内容被蒙版覆盖,不仅起到调光效果,还阻碍了交互:这意味着,点击选项卡按钮不起作用。
因此,为了使其正常工作,我认为您可能需要使用自定义选项卡栏和自定义工作表。然而,这并不像听起来那么困难:
1。自定义工具栏为了实现自定义工具栏,使用枚举来定义可用的选项卡类型效果很好:
enum TabType: CaseIterable {
case home
case second
var titleKey: String {
"\(self)".capitalized
}
var symbolName: String {
switch self {
case .home: "house"
case .second: "2.circle"
}
}
}
自定义选项卡栏可以仅使用
ForEach
来迭代所有枚举类型。通过使用枚举工具,这会变得更容易
CaseIterable
。要使标签看起来像原生标签 TabView
,可以使用自定义
LabelStyle
:struct TabLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
VStack {
configuration.icon
configuration.title
.font(.caption)
}
.frame(maxWidth: .infinity)
}
}
struct CustomTabBar: View {
@Binding var selection: TabType
var body: some View {
HStack {
ForEach(TabType.allCases, id: \.self) { type in
Label(type.titleKey, systemImage: type.symbolName)
.labelStyle(TabLabelStyle())
.symbolVariant(selection == type ? .fill : .none)
.foregroundStyle(selection == type ? Color(.link) : Color(.secondaryLabel))
.onTapGesture {
withAnimation(.spring(duration: 0.2)) { selection = type }
}
}
}
.padding(.vertical)
.background(.background)
}
}
要切换所选内容,
ZStack
效果很好。这是更新后的
ContentView
,现在使用 ZStack
+ 自定义 tar 栏作为原生 TabView
的替代品:struct ContentView: View {
@State private var showSheet = false
@State private var selection = TabType.home
var body: some View {
VStack(spacing: 0) {
ZStack {
switch selection {
case .home: HomeView(showSheet: $showSheet)
case .second: Text("Second Tab")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
CustomTabBar(selection: $selection)
}
}
}
以这种方式使用自定义标签栏还有其他一些优点:
如果需要,您可以更改选项卡标签的样式
自定义工作表可以简单地实现为覆盖层。这在您的情况下特别有效,因为您已禁用交互式关闭并且还希望允许与后台交互。
我假设您希望在切换选项卡时“工作表”消失,因此覆盖层会像以前一样附加到
HomeView
上。我想知道为什么您需要将标志
showSheet
作为对此视图的绑定传递,而不是使其成为 HomeView
中的状态变量,但这样做可以确保从另一个选项卡切换回来时工作表仍然可见.struct HomeView: View {
@Binding var showSheet: Bool
var body: some View {
VStack {
Button("Show TabView Sheet") {
showSheet.toggle()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.overlay(alignment: .bottom) {
if showSheet {
SheetContent()
.transition(.move(edge: .bottom))
}
}
.animation(.easeInOut, value: showSheet)
}
}
struct SheetContent: View {
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
// Add more content here as needed
}
.frame(maxWidth: .infinity, minHeight: 200)
.background {
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 20, topTrailing: 20))
.fill(.yellow)
}
}
}
以下是它们如何协同工作: