SwiftUI: how to handle BOTH tap & long press of button?

前端 未结 8 1201
刺人心
刺人心 2020-12-15 06:59

I have a button in SwiftUI and I would like to be able to have a different action for \"tap button\" (normal click/tap) and \"long press\".

Is that possible in Swift

8条回答
  •  自闭症患者
    2020-12-15 07:59

    Thought I'd post back on this, in case anyone else is struggling. Strange that Apple's default behaviour works on most controls but not buttons. In my case I wanted to keep button effects while supporting long press.

    An approach that works without too much complexity is to ignore the default button action and create a simultaneous gesture that handles both normal and long clicks.

    In your view you can apply a custom long press modifier like this:

    var body: some View {
    
            // Apply the modifier
            Button(action: self.onReloadDefaultAction) {
                Text("Reload")
            }
                .modifier(LongPressModifier(
                    isDisabled: self.sessionButtonsDisabled,
                    completionHandler: self.onReloadPressed))
        }
    
        // Ignore the default click
        private func onReloadDefaultAction() {
        }
    
        // Handle the simultaneous gesture
        private func onReloadPressed(isLongPress: Bool) {
    
            // Do the work here
        }
    

    My long press modifier implementation looked like this and uses the drag gesture that I found from another post. Not very intuitive but it works reliably, though of course I would prefer not to have to code this plumbing myself.

    struct LongPressModifier: ViewModifier {
    
        // Mutable state
        @State private var startTime: Date?
    
        // Properties
        private let isDisabled: Bool
        private let longPressSeconds: Double
        private let completionHandler: (Bool) -> Void
    
        // Initialise long press behaviour to 2 seconds
        init(isDisabled: Bool, completionHandler: @escaping (Bool) -> Void) {
    
            self.isDisabled = isDisabled
            self.longPressSeconds = 2.0
            self.completionHandler = completionHandler
        }
    
        // Capture the start and end times
        func body(content: Content) -> some View {
    
            content.simultaneousGesture(DragGesture(minimumDistance: 0)
                .onChanged { _ in
    
                    if self.isDisabled {
                        return
                    }
    
                    // Record the start time at the time we are clicked
                    if self.startTime == nil {
                        self.startTime = Date()
                    }
                }
                .onEnded { _ in
    
                    if self.isDisabled {
                        return
                    }
    
                    // Measure the time elapsed and reset
                    let endTime = Date()
                    let interval = self.startTime!.distance(to: endTime)
                    self.startTime = nil
    
                    // Return a boolean indicating whether a normal or long press
                    let isLongPress = !interval.isLess(than: self.longPressSeconds)
                    self.completionHandler(isLongPress)
                })
        }
    }
    

提交回复
热议问题