Как ограничить жест перетаскивания в одном направлении только в SwiftUI? - PullRequest
0 голосов
/ 10 октября 2019

Я смущенно провел последние 2 недели, пытаясь решить эту проблему.

Я пытаюсь сделать следующее:

  1. Привязать мой слайд-вид к нижней части экрана
  2. Отключить перетаскивание вверхи только разрешить перетаскивать карту вниз, чтобы закрыть

Что я пробовал:

Я пытался связываться с размером карты с помощьюустановка его высоты на высоту экрана. Вы можете видеть эту строку закомментированной. После этого я перебрал смещение карты и установил ее так, чтобы она выглядела так, как будто карта на самом деле меньше половины своего размера, около 300 в высоту. Проблема в том, что когда я медленно скользлю вверх, я вижу пустое пространство, которое скрыто за экраном. Это не тот эффект, которого я хочу.

Следующее, что я попытался сделать, это изменить высоту карты до желаемой высоты. Затем отрегулируйте смещение таким образом, чтобы карта находилась там, где я хочу. Однако, я чувствую, что ручная настройка не будет надежной на разных экранах. Поэтому я пытаюсь найти правильную математику, необходимую для того, чтобы она всегда находилась в самом низу экрана, когда она всплывает.

Наконец, я хочу просто сделать так, чтобы пользователи могли только перетаскиватьи не вверх.

Буду очень признателен за помощь здесь. Я потратил много времени на сообщения и чтение, изучая новые вещи, но я не могу решить свою конкретную проблему.

Вот моя карточка-слайдер

    import SwiftUI

struct SigninView<Content: View> : View {
    @GestureState private var dragState = DragState.inactive
    @State var position = CardPosition.top

    var content: () -> Content
    var body: some View {
        let drag = DragGesture()
            .updating($dragState) { drag, state, transaction in
                state = .dragging(translation: drag.translation)
            }
            .onEnded(onDragEnded)

        return Group {
           // Handle()
            self.content()
        }
        .frame(height: 333) //UIScreen.main.bounds.height)
        .background(Color.purple)
        .cornerRadius(10.0)
        .shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)
        .offset(y: self.position.rawValue + self.dragState.translation.height)
        .animation(self.dragState.isDragging ? nil : .interpolatingSpring(stiffness: 300.0, damping: 30.0, initialVelocity: 10.0))
        .gesture(drag)
    }

    private func onDragEnded(drag: DragGesture.Value) {
        let verticalDirection = drag.predictedEndLocation.y - drag.location.y
        let cardTopEdgeLocation = self.position.rawValue + drag.translation.height
        let positionAbove: CardPosition
        let positionBelow: CardPosition
        let closestPosition: CardPosition

        if cardTopEdgeLocation <= CardPosition.middle.rawValue {
            positionAbove = .top
            positionBelow = .middle
        } else {
            positionAbove = .middle
            positionBelow = .bottom
        }

        if (cardTopEdgeLocation - positionAbove.rawValue) < (positionBelow.rawValue - cardTopEdgeLocation) {
            closestPosition = positionAbove
        } else {
            closestPosition = positionBelow
        }

        if verticalDirection > 0 {
            self.position = positionBelow
        } else if verticalDirection < 0 {
            self.position = positionAbove
        } else {
            self.position = closestPosition
        }

    }
}

enum CardPosition: CGFloat {
    case top = 100
    case middle = 790
    case bottom = 850
}

enum DragState {
    case inactive
    case dragging(translation: CGSize)

    var translation: CGSize {
        switch self {
        case .inactive:
            return .zero
        case .dragging(let translation):
            return translation
        }
    }

    var isDragging: Bool {
        switch self {
        case .inactive:
            return false
        case .dragging:
            return true
        }
    }
}

Вотмоя страница ContentView, где я ее тестирую:

import SwiftUI

struct ContentView: View {
    @State var show:Bool = false

     var body: some View {
        SigninView {
            VStack {

                Text("TESTING")
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(Color.blue)
            }
        }


    }


}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

1 Ответ

1 голос
/ 10 октября 2019

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

var body: some View {
    ZStack {
        Text("Content here!")
    }
    .overlay(
        SigninView()
            .offset(...),
        alignment: .bottom
    )
}

Это автоматически поместит ваш вид внизу экрана на высоту вашего SigninView, там должно быть немногоздесь нет математикиСмещение, которое вы определите своим жестом, и любое пространство, которое вы хотите создать между основанием и оверлеем.

Далее, чтобы разрешить только жесты вниз, вы не можете просто сжать перевод?

    var translation: CGSize {
        switch self {
        case .inactive:
            return .zero
        case .dragging(let translation):
            return max(0, translation) // clamp this to the actual translation or 0 so it can't go negative
        }
    }
...