Search code examples
iosuiviewuigesturerecognizeruipickerviewuitapgesturerecognizer

ios: How to make UIPickerView(UIView) catch touch event before UITouchRecognizer?


Event Handling Guide for iOS said "There may be times when you want a view to receive a touch before a gesture recognizer. ", but I couldn't find how to do that.

I tried delaysTouchesEnded = false, but it does not have any effect.

@IBOutlet weak var myPickerView: UIPickerView!

override func viewDidLoad() {
    super.viewDidLoad()

    let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyViewController.pickerViewTapped(_:)))
    tapGesture.delegate = self
    tapGesture.delaysTouchesEnded = false
    myPickerView.addGestureRecognizer(tapGesture)
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
    // called by UIPickerView 
    print("pickerView called!")
}

func pickerViewTapped(_ sender: UITapGestureRecognizer) {
    // called by UITapGestureRecognizer
    print("pickerViewTapped called!")
}

When I tap myPickerView, the console would be

pickerViewTapped called!
pickerView called!

But I want them in reverse order. How to do that?


Solution

  • Short answer:

    You can't do it using UIGestureRecognizer only.

    Long answer:

    There is always a delay between the action of select a row of a UIPickerView and the call of the method didSelectRow, due to the animation required for move the row to the center of UIPickerView itself.

    Also note that if you tap of the row at center, you don't call the didSelectRow method, but pickerViewTapped only.

    This delay necessarily make this sequence of the execution:

    1. pickerViewTapped
    2. pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){

    The solution I found is this:

    func pickerViewTapped(_ sender: UITapGestureRecognizer) {
        // called by UITapGestureRecognizer
    
        let timeDelay = 0.6
        //0.6 is arbitrary value, depends on you
        DispatchQueue.main.asyncAfter(deadline: .now() + timeDelay) {
            print("pickerViewTapped called!")
        }
    }
    

    I know maybe it is not the answer you were looking for, neither I know why you would have such behaviour but this is the nearest solution I've found.