Как бы вы сделали .sheet, представленный над панелью вкладок, не закрывая его в SwiftUi? Я видел какое-то решение, но не в 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()
}





В вашем примере лист нельзя закрыть в интерактивном режиме, но фоновое взаимодействие включено. Поэтому я бы предложил использовать наложение для замены листа:
// ContentView
HomeView(showSheet: $showSheet)
.tabItem {
Label("Home", systemImage: "house")
}
.toolbarBackground(.visible, for: .tabBar)
.toolbarBackground(.background, for: .tabBar)
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)
}
}
}

РЕДАКТИРОВАТЬ В комментарии вы спрашивали, можно ли изменить размер наложения. Вам нужно будет реализовать это самостоятельно, используя жест перетаскивания. Возможно, что-то вроде этого:
struct SheetContent: View {
let minHeight: CGFloat = 200
let maxHeight: CGFloat = 450
@State private var extraHeight = CGFloat.zero
@State private var dragHeight = CGFloat.zero
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
// Add more content here as needed
}
.frame(maxWidth: .infinity, maxHeight: minHeight + extraHeight)
.offset(y: -dragHeight / 2)
.background {
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 20, topTrailing: 20))
.padding(.bottom, -300)
.foregroundStyle(.yellow)
.offset(y: -dragHeight)
}
.overlay(alignment: .top) {
Capsule()
.frame(width: 36, height: 5)
.foregroundStyle(.secondary)
.padding(5)
.offset(y: -dragHeight)
.gesture(
DragGesture()
.onChanged { val in
let dy = -val.translation.height
let minDragHeight = minHeight - (minHeight + extraHeight)
let maxDragHeight = maxHeight - (minHeight + extraHeight)
dragHeight = min(max(dy, minDragHeight), maxDragHeight)
}
.onEnded { val in
extraHeight = extraHeight + dragHeight
dragHeight = 0
}
)
}
}
}

Ответ обновлен, чтобы показать, как можно реализовать изменение размера с помощью жеста перетаскивания.
Одна из проблем, с которой я все еще сталкиваюсь, — это высота наложения при смене вкладок. Я пытаюсь сделать так, чтобы вкладка меняла содержимое наложения. Но при этом высота текущего положения наложения не обновляется до второй вкладки. Я не могу вставить весь код в комментарий: github.com/sheehanmunim/tabbarandbottom/blob/main/…
@SheehanMunim Рад слышать, что вы добиваетесь прогресса, спасибо, что приняли ответ. Ре. высота наложения при смене вкладок. Если вы хотите, чтобы наложение сохранялось между сменами вкладок, то, похоже, его нужно прикрепить к родительскому представлению, а не к дочерним представлениям. К сожалению, это не работает, если родительским представлением является TabView. Однако это будет работать с пользовательской панелью вкладок ZStack +, с которой и начался мой ответ. Дайте мне знать, если вы хотите, чтобы я отменил предыдущую часть моего ответа (или вы можете найти его в истории).
Основываясь на ответе Бензи Низа, я также реализовал анимацию удушения и точки привязки, чтобы они выглядели как лист. (Огромное спасибо Бензи Низу)
import SwiftUI
struct ContentView: View {
@State private var showSheet = true
var body: some View {
TabView {
HomeView(showSheet: $showSheet)
.tabItem {
Label("Home", systemImage: "house")
}
.toolbarBackground(.visible, for: .tabBar)
.toolbarBackground(.background, for: .tabBar)
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()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.overlay(alignment: .bottom) {
if showSheet {
SheetContent()
.transition(.move(edge: .bottom))
}
}
.animation(.easeInOut, value: showSheet)
}
}
struct SheetContent: View {
let minHeight: CGFloat = 100
let snapHeight: CGFloat = 150
let midHeight: CGFloat = 400
let maxHeight: CGFloat = 700
@State private var extraHeight = CGFloat.zero
@State private var dragHeight = CGFloat.zero
init() {
_extraHeight = State(initialValue: snapHeight - minHeight)
}
var body: some View {
VStack {
Text("First Tab Content")
Text("More Content in the Sheet")
}
.frame(maxWidth: .infinity, maxHeight: minHeight + extraHeight)
.offset(y: -dragHeight / 2)
.background {
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 20, topTrailing: 20))
.padding(.bottom, -300)
.foregroundStyle(.yellow)
.offset(y: -dragHeight)
}
.overlay(alignment: .top) {
Capsule()
.frame(width: 36, height: 5)
.foregroundStyle(.secondary)
.padding(5)
.offset(y: -dragHeight)
}
.animation(.easeInOut, value: extraHeight)
.animation(.easeInOut, value: dragHeight)
.gesture(
DragGesture()
.onChanged { val in
let dy = -val.translation.height
let minDragHeight = minHeight - (minHeight + extraHeight)
let maxDragHeight = maxHeight - (minHeight + extraHeight)
dragHeight = min(max(dy, minDragHeight), maxDragHeight)
}
.onEnded { _ in
let snapPoints: [CGFloat] = [snapHeight, midHeight, maxHeight]
let currentHeight = minHeight + extraHeight + dragHeight
let closestSnapPoint = snapPoints.min(by: { abs($0 - currentHeight) < abs($1 - currentHeight) }) ?? snapHeight
withAnimation(.easeInOut) {
extraHeight = closestSnapPoint - minHeight
dragHeight = 0
}
}
)
}
}
#Preview {
ContentView()
}
Хотя оба приведенных ниже метода, предложенные Бензи Низ и мной, действительны, вы все равно можете столкнуться с проблемами при попытке отредактировать контент в фактическом размере наложения, высота которого постоянно изменяется. Лучше всего использовать это и следовать моему подробному объяснению в этом посте: https://stackoverflow.com/a/78686848/16810909
Есть ли способ заставить это наложение вести себя как лист? (Фиксаторы презентации, позволяющие изменять ее размер до .medium и .large)