简体   繁体   English

SwiftUI LongPressGesture 需要很长时间才能识别 TapGesture 何时也存在

[英]SwiftUI LongPressGesture takes too long to recognize when TapGesture also present

I would like to recognize a TapGesture and LongPressGesture on the same item.我想在同一个项目上识别TapGestureLongPressGesture And it works fine, with the following exception: the LongPressGesture alone responds after the duration I specify, which is 0.25 seconds, but when I combine it with the TapGesture , it takes at least 1 second—I can't find a way to make it respond more quickly.它工作正常,除了以下例外: LongPressGesture单独响应我指定的持续时间,即 0.25 秒,但是当我将它与TapGesture结合使用时,它至少需要 1 秒——我找不到制作方法它反应更快。 Here is a demo:这是一个演示:

在此处输入图像描述

And here is the code for it:这是它的代码:

struct ContentView: View {
    @State var message = ""

    var body: some View {
        Circle()
            .fill(Color.yellow)
            .frame(width: 150, height: 150)
            .onTapGesture(count: 1) {
                message = "TAP"
            }
            .onLongPressGesture(minimumDuration: 0.25) {
                message = "LONG\nPRESS"
            }
            .overlay(Text(message)
                        .font(.title).bold()
                        .multilineTextAlignment(.center)
                        .allowsHitTesting(false))
    }
}

Notice that it works fine except for the duration of the LongPress, which is much longer than 0.25 seconds.请注意,它工作正常,除了 LongPress 的持续时间远长于 0.25 秒。

Any ideas?有任何想法吗? Thanks in advance!提前致谢!

To having some multi gesture that fits every ones needs in projects, Apple has nothing offer than normal gesture, mixing them together to reach the wished gesture some times get tricky, here is a salvation, working without issue or bug!为了在项目中拥有一些适合每个人需要的多手势,Apple 提供的只是普通手势,将它们混合在一起以达到想要的手势有时会很棘手,这是一种拯救,没有问题或错误的工作!

Here a custom zero issue gesture called interactionReader , we can apply it to any View.这里有一个名为interactionReader的自定义零问题手势,我们可以将其应用于任何视图。 for having LongPressGesture and TapGesture in the same time .同时拥有LongPressGestureTapGesture


在此处输入图像描述


import SwiftUI

struct ContentView: View {
    
    var body: some View {
        
        Circle()
            .fill(Color.yellow)
            .frame(width: 150, height: 150)
            .interactionReader(longPressSensitivity: 250, tapAction: tapAction, longPressAction: longPressAction, scaleEffect: true)
            .animation(Animation.easeInOut(duration: 0.2))
        
    }
    
    func tapAction() { print("tap action!") }
    
    func longPressAction() { print("longPress action!") }
    
}

struct InteractionReaderViewModifier: ViewModifier {
    
    var longPressSensitivity: Int
    var tapAction: () -> Void
    var longPressAction: () -> Void
    var scaleEffect: Bool = true
    
    @State private var isPressing: Bool = Bool()
    @State private var currentDismissId: DispatchTime = DispatchTime.now()
    @State private var lastInteractionKind: String = String()
    
    func body(content: Content) -> some View {
        
        let processedContent = content
            .gesture(gesture)
            .onChange(of: isPressing) { newValue in
                
                currentDismissId = DispatchTime.now() + .milliseconds(longPressSensitivity)
                let dismissId: DispatchTime = currentDismissId
                
                if isPressing {
                    
                    DispatchQueue.main.asyncAfter(deadline: dismissId) {
                        
                        if isPressing { if (dismissId == currentDismissId) { lastInteractionKind = "longPress"; longPressAction() } }
                        
                    }
                    
                }
                else {
                    
                    if (lastInteractionKind != "longPress") { lastInteractionKind = "tap"; tapAction() }
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {lastInteractionKind = "none"}
                    
                    
                }
                
            }
        
        return Group {
            
            if scaleEffect { processedContent.scaleEffect(lastInteractionKind == "longPress" ? 1.5: (lastInteractionKind == "tap" ? 0.8 : 1.0 )) }
            else { processedContent }
            
        }

    }
    
    var gesture: some Gesture {
        
        DragGesture(minimumDistance: 0.0, coordinateSpace: .local)
            .onChanged() { _ in if !isPressing { isPressing = true } }
            .onEnded() { _ in isPressing = false }
        
    }
    
}

extension View {
    
    func interactionReader(longPressSensitivity: Int, tapAction: @escaping () -> Void, longPressAction: @escaping () -> Void, scaleEffect: Bool = true) -> some View {
        
        return self.modifier(InteractionReaderViewModifier(longPressSensitivity: longPressSensitivity, tapAction: tapAction, longPressAction: longPressAction, scaleEffect: scaleEffect))
        
    }
    
}

The LongPressGesture has another completion that can execute an action when a user first touches down.. and again at the minimum duration. LongPressGesture 有另一个完成,可以在用户第一次触地时执行一个动作.. 并在最短持续时间再次执行。 Could something like this work?像这样的东西可以吗?

struct LongTapTest: View {
    @State var message = ""

    var body: some View {
        Circle()
            .fill(Color.yellow)
            .frame(width: 150, height: 150)
            .onLongPressGesture(minimumDuration: 0.25, maximumDistance: 50, pressing: { (isPressing) in
            if isPressing {
                // called on touch down
            } else {
                // called on touch up
                message = "TAP"
            }
        }, perform: {
            message = "LONG\nPRESS"
        })
            .overlay(Text(message)
                        .font(.title).bold()
                        .multilineTextAlignment(.center)
                        .allowsHitTesting(false))
    }
}

Well, this isn't beautiful, but it works fine.嗯,这并不漂亮,但它工作正常。 It logs the beginning of each tap/press and if it ends before 0.25 seconds it considers it a TapGesture , otherwise it considers it a LongPressGesture :它记录每次点击/按下的开始,如果它在 0.25 秒之前结束,它认为它是TapGesture ,否则它认为它是LongPressGesture

struct ContentView: View {
    @State var pressInProgress = false
    @State var gestureEnded = false
    @State var workItem: DispatchWorkItem? = nil
    @State var message = ""

    var body: some View {

        Circle()
            .fill(Color.yellow)
            .frame(width: 150, height: 150, alignment: .center)
            .overlay(Text(message)
                         .font(.title).bold()
                         .multilineTextAlignment(.center)
                         .allowsHitTesting(false))
            .gesture(
                DragGesture(minimumDistance: 0, coordinateSpace: .global)
                    .onChanged { _ in
                        guard !pressInProgress else { return }
                        pressInProgress = true
                        workItem = DispatchWorkItem {
                            message = "LONG\nPRESSED"
                            gestureEnded = true
                        }
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: workItem!)
                }
                .onEnded { _ in
                    pressInProgress = false
                    workItem?.cancel()
                    guard !gestureEnded else { // moot if we're past 0.25 seconds so ignore
                        gestureEnded = false
                        return
                    }
                    message = "TAPPED"
                    gestureEnded = false
                }
        )
    }
}

If anyone can think of a solution that actually uses LongPressGesture and TapGesture I'd much prefer that!如果有人能想到一个实际使用LongPressGestureTapGesture的解决方案,我会更喜欢那个!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM