Как я могу создать прозрачные кнопки, которые реагируют на нажатия, но передают жесты перетаскивания на подлежащие представления?

Вопрос или проблема

У меня есть полноэкранное представление сцены SpriteKit, которое обрабатывает панорамные жесты внутри себя. Поверх него я наложил нативные представления SwiftUI с OnTapGesture, которые должны реагировать на нажатия, но не должны мешать панорамным жестам и передавать их ниже.

.allowHitTesting(false) полностью удаляет любую реакцию на касания, панорамные жесты работают, но обработчики кнопок не срабатывают.

Я написал маленький пример, который воспроизводит мою ситуацию:

import SwiftUI

struct TestView: View
{
    var body: some View
    {
        ZStack
        {
            Rectangle() //симулирует представление spriteKit
                .fill(.blue)
                .ignoresSafeArea()
                .simultaneousGesture(DragGesture().onChanged(
                    { value in
                        //симулирует обработку перетаскивания spriteKit,
                        //например, анимированное перелистывание страниц
                        print("DragGesture",value.velocity)
                    } ))
                
            HStack(spacing: 0) //мои кнопки
            {
                Rectangle()
                    .fill(.red.opacity(0.00001))
                    .border(.red, width: 2)
                    .onTapGesture
                    {
                        // это вызывается,
                        // но жест перетаскивания sk НЕ вызывается
                        print("перевернуть страницу налево")
                    }
                
                Rectangle()
                    .fill(.clear)
                    .border(.green, width: 2)
                    .onTapGesture
                    {
                        // это НЕ вызывается,
                        // и жест sk вызывается
                        print("перевернуть страницу направо")
                    }
                
                Button(action:
                        {
                            // это вызывается,
                            // но жест перетаскивания sk НЕ вызывается
                            print("кнопка Нажата")
                        },
                       label:
                        {
                            Rectangle()
                                .fill(.black)
                                .border(.yellow, width: 2)
                        })
            }
        }
    }
}


#Preview {
    TestView()
}

Так что, чтобы повторить: Как я могу сделать ПРОЗРАЧНЫЕ (или с непрозрачностью 0.0001) накладывающиеся кнопки, которые захватывают одиночные касания, но могут передавать панорамные (или перетаскивающие) жесты представлениям ниже их?

.

Ответ или решение

Как создать прозрачные кнопки в SwiftUI, которые реагируют на касания и передают жесты перетаскивания вниз

Создание интерфейса, в котором прозрачные кнопки могут реагировать на касания и одновременно передавать жесты перетаскивания (drag gestures) в слои под ними, может быть непростой задачей, особенно если вы работаете с комбинацией SwiftUI и SpriteKit. В этом ответе мы разберем, как этого добиться, основываясь на предоставленном примере.

Проблема

Вы имеете полноэкранное представление SpriteKit, обрамлённое сверху прозрачными кнопками в SwiftUI. Кнопки должны обрабатывать одиночные касания, не мешая при этом срабатыванию жестов перетаскивания, которые должны обрабатываться слоями под ними.

Использование .allowHitTesting(false) полностью отключает любые реакции на касания — это приводит к тому, что кнопки не реагируют на нажатия, но перетаскивание работает как задумано. Задача состоит в том, чтобы сделать кнопки «прозрачными» для управления касанием, но чтобы они обрабатывали одиночные нажатия.

Решение

Чтобы решить изложенную задачу, необходимо использовать .background или .contentShape для создания области касания на кнопках. Таким образом, кнопки могут реагировать на одиночные касания, но не будут препятствовать жестам перетаскивания.

Вот пример кода, который демонстрирует данное решение:

import SwiftUI

struct TestView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.blue)
                .ignoresSafeArea()
                .simultaneousGesture(DragGesture().onChanged { value in
                    print("DragGesture", value.translation)
                })

            HStack(spacing: 0) {
                Rectangle()
                    .fill(Color.red.opacity(0.0001))
                    .border(Color.red, width: 2)
                    .contentShape(Rectangle()) // Устанавливаем область касания
                    .onTapGesture {
                        print("flip page left")
                    }

                Rectangle()
                    .fill(Color.clear)
                    .border(Color.green, width: 2)
                    .contentShape(Rectangle()) // Устанавливаем область касания
                    .onTapGesture {
                        print("flip page right")
                    }

                Button(action: {
                    print("button PRESSED")
                }, label: {
                    Rectangle()
                        .fill(Color.black)
                        .border(Color.yellow, width: 2)
                })
            }
        }
    }
}

#Preview {
    TestView()
}

Объяснение кода

  1. Прозрачные кнопки: Для создания кнопок с нулевой прозрачностью используйте fill(Color.red.opacity(0.0001)), что делает их практически неразличимыми на экране.

  2. Область касания: Используйте модификатор .contentShape(Rectangle()). Этот модификатор позволяет установить определенный фон для обработки касаний кнопками. Это эффективно, когда вы хотите, чтобы область, в которой кнопка обрабатывает касания, была больше (или соответствовала прямоугольной форме) по сравнению с самим визуальным представлением кнопки.

  3. Обработка касаний: Каждая кнопка содержит модификатор onTapGesture, что позволяет обрабатывать нажатия, даже если она прозрачная. При этом, жесты перетаскивания будут корректно обрабатываться слоями под кнопками.

Заключение

Создание прозрачных кнопок в SwiftUI, которые корректно обрабатывают одиночные нажатия и передают жесты перетаскивания вниз, возможно благодаря использованию .contentShape и правильного управления прозрачностью. Данный подход позволяет вам достичь нужной функциональности, улучшив пользовательский интерфейс приложения, и обеспечивая при этом интуитивное взаимодействие с пользователем.

Следуя предложенным рекомендациям, вы сможете создать интерфейс, который будет сочетать в себе как интерактивность, так и удобство.

Оцените материал
Добавить комментарий

Капча загружается...