Взаимодействие DragGesture и ScrollView в SwiftUI - PullRequest
1 голос
/ 19 февраля 2020

В приложении, над которым я работаю, есть часть, которая имеет, в основном, «навигацию вперед» - при нажатии на кнопки будет отображаться следующий слайд. Однако вторичная «обратная» навигация также необходима. Вот подход, который я использовал:

import SwiftUI

struct Sample: View {
    @State private var dragOffset: CGFloat = -100
    var body: some View {
        VStack {

            Text("Perhaps a title")

            ScrollView {
                VStack {
                    Text("Some scrollable content is going to be here")

                    // ...

                    Button(action: {
                        // Go to the next slide
                    }) { Text("Next") }
                }
            }

            Text("and, maybe, something else")
        }
        .overlay(
            Image(systemName: "arrow.left").offset(x: dragOffset / 2),
            alignment: .leading
        )
        .gesture(
            DragGesture()
                .onChanged{
                    self.dragOffset = $0.translation.width
                }
                .onEnded {
                    self.dragOffset = -100 // Hide the arrow

                    if $0.translation.width > 100 {
                        // Go to the previous slide
                    }
                }
        )
    }
}

Существует небольшой индикатор (стрелка влево), который изначально скрыт (dragOffset = -100). Когда начинается жест перетаскивания, смещение подается в переменную состояния dragOffset , которая фактически показывает стрелку. Когда жест перетаскивания заканчивается, стрелка снова скрывается и, если достигается определенное смещение, отображается предыдущий слайд.

Работает достаточно хорошо, за исключением того, что когда пользователь прокручивает содержимое в ScrollView, этот жест также запускается и обновляется на некоторое время, но затем, я полагаю, отменяется ScrollView, и onEnded не вызывается. В результате индикатор стрелки остается на экране.

Отсюда возникает вопрос: как правильно сделать такой жест, который бы работал вместе с ScrollView? Возможно ли это даже с текущим состоянием SwiftUI?

1 Ответ

1 голос
/ 19 февраля 2020

Для таких временных состояний лучше использовать GestureState, поскольку он автоматически сбрасывается в исходное состояние после отмены / завершения жеста.

Так что здесь возможен подход

Демо:

enter image description here

Код:

struct Sample: View {
    @GestureState private var dragOffset: CGFloat = -100
    var body: some View {
        VStack {

            Text("Perhaps a title")

            ScrollView {
                VStack {
                    Text("Some scrollable content is going to be here")

                    // ...

                    Button(action: {
                        // Go to the next slide
                    }) { Text("Next") }
                }
            }

            Text("and, maybe, something else")
        }
        .overlay(
            Image(systemName: "arrow.left").offset(x: dragOffset / 2),
            alignment: .leading
        )
        .gesture(
            DragGesture()
                .updating($dragOffset) { (value, gestureState, transaction) in
                    let delta = value.location.x - value.startLocation.x
                    if delta > 10 { // << some appropriate horizontal threshold here
                        gestureState = delta
                    }
                }
                .onEnded {
                    if $0.translation.width > 100 {
                        // Go to the previous slide
                    }
                }
        )
    }
}

Примечание: dragOffset: CGFloat = -100 это может иметь разный эффект на разных реальных устройствах, поэтому, вероятно, лучше рассчитать его явно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...