Установите zIndex представлений в другой иерархии представлений

У меня есть 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, то он будет работать, но тогда будет покрыт не весь экран.

Пожалуйста, включите минимально воспроизводимый пример

jnpdx 04.04.2024 16:55

Хорошо, я добавил пример

ykasur 04.04.2024 17:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

1. Вместо этого вы можете использовать эту иерархию:

VStack  
  |---OptionButton
  |---ZStack
        |---Dismisser
        |---Menu

2. Если необходимо перекрыть опциональную кнопку:

  • от 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 05.04.2024 11:29

@ykasur Действительно. Есть также два дополнительных решения. Если первые два вам не подходят, могу поделиться, но это мало нагружает пользовательский интерфейс.

b m gevariya 05.04.2024 13:49

Non Swifty ways

1. Сделайте 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

2. Сделайте несколько 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()
        }
}

Другие вопросы по теме