У меня есть 4 вида: A, B, C и D.
A и B находятся внутри VStack C.
C и D оба находятся в ZStack.
Я хочу, чтобы A находился под B, поэтому оба находятся в VStack.
Проблема в том, что я хочу установить zIndex для A равным 1, B для 3 и для D для 2. ZIndex применяется только к представлениям внутри одной иерархии представлений. Но если я помещу A, B и D в один и тот же ZStack, то A и B больше не будут находиться друг под другом. Если я помещу D внутрь VStack C, то он больше не будет поверх A и B...
Как мне решить эту головоломку?
Я хочу добиться этого, потому что в моем случае D — это наложение, которое должно обнаруживать события щелчка на всем, кроме B (которое представляет собой всплывающее меню). A — это кнопка для отображения меню B. Предполагается, что нажатие на D закроет меню B. Но элементы в B по-прежнему должны быть доступны для кликов.
Вот пример:
struct Test: View {
@State private var isMenuVisible = false
var body: some View {
ZStack {
VStack { //C
Button(action: { //A
isMenuVisible.toggle()
}, label: {
Text("Open Menu")
})
if (isMenuVisible) {
Rectangle() //B
.fill(Color.blue)
.overlay {
Button(action: {
print("is clickable!")
}, label: {
Text("Menu-Button")
})
}
}
Spacer()
}
if (isMenuVisible) {
Color.red // D
.opacity(0.6)
.onTapGesture {
isMenuVisible.toggle()
}
}
}
}
}
Если я помещу оператор if внутрь ZStack, то он будет работать, но тогда будет покрыт не весь экран.
Хорошо, я добавил пример
VStack
|---OptionButton
|---ZStack
|---Dismisser
|---Menu
fix/known sized button
или dynamic size observer
struct Test: View {
@State private var isMenuVisible = false
//@State private var dynamicHeight = 50.0
var body: some View {
ZStack(alignment: .top) {
Color.clear // make ZStack wider
Button {
isMenuVisible.toggle()
} label: {
Text("Open Menu")
}
.frame(height: 50)//dynamicHeight = observeHeight
.border(.green, width: 1)
if isMenuVisible {
Color.red //Dismisser
.opacity(0.5)
.onTapGesture {
isMenuVisible.toggle()
}
VStack {
Button(action: {
print("is clickable 1!")
}, label: {
Text("Menu-Button-1")
})
Button(action: {
print("is clickable 2!")
}, label: {
Text("Menu-Button-2")
})
}
.border(.green, width: 1)
.padding(.top, 50)//.padding(.top, dynamicHeight)
}
}
}
}
для наблюдения в прямом эфире с любого ракурса
.overlay(
GeometryReader { geo in
Color.clear.onAppear {
dynamicHeight = geo.size.height
}
.onChange(of: geo.frame(in: .global)) { _ in
dynamicHeight = geo.size.height
}
}
)
Таким образом, решение состоит в том, чтобы поместить все представления внутри ZStack, а затем добавить отступы к пунктам меню, вместо того, чтобы позволить VStack обрабатывать отступы.
@ykasur Действительно. Есть также два дополнительных решения. Если первые два вам не подходят, могу поделиться, но это мало нагружает пользовательский интерфейс.
Non Swifty ways
Dismisser
очень большим:Поскольку Dismisser не будет виден &
Он кликабельный, даже если он находится за пределами опциональной кнопки.
VStack {
Button {
isMenuVisible.toggle()
} label: {
Text("Open Menu")
}
.overlay(
ZStack {
if isMenuVisible {
Color.red //Dismisser
.opacity(0.5)
.frame(width: 1500, height: 2500) // alternate
.onTapGesture {
isMenuVisible.toggle()
}
}
}
)
.border(.green, width: 1)
if isMenuVisible {
VStack {
Button(action: {
print("is clickable 1!")
}, label: {
Text("Menu-Button-1")
})
Button(action: {
print("is clickable 2!")
}, label: {
Text("Menu-Button-2")
})
}
.border(.green, width: 1)
}
Spacer()
}
//.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
//.scaleEffect(10)//100
Dismisser
😜Местоположение второго увольнения может меняться в зависимости от ситуации.
var body: some View {
VStack {
Button {
isMenuVisible.toggle()
} label: {
Text("Open Menu")
}
.overlay( ZStack {
if isMenuVisible {
dismisser
}
})
.border(.green, width: 1)
if isMenuVisible {
VStack {
Button { } label: {
Text("Menu-Button-1")
}
}
.border(.green, width: 1)
}
Color.clear
}
.background( ZStack {
if isMenuVisible {
dismisser
}
})
}
//@ViewBuilder // if isMenuVisible is handled here
var dismisser: some View {
Color.red
.opacity(0.5)
.onTapGesture {
isMenuVisible.toggle()
}
}
Пожалуйста, включите минимально воспроизводимый пример