如何仅在SwiftUI中将拖动手势限制为一个方向?

时间:2019-10-10 17:51:29

标签: cocoa-touch swiftui ios13

过去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 个答案:

答案 0 :(得分:1)

首先,您可能不应该将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
        }
    }