1

My goal, having at the same time, both:

  • a UIButton that handle an event (.touchDown)
  • another view upper in the hierarchy (i.e.: super) that receives a touchBegan/Moved/Ended/Cancelled.

I want that event because, I need the touch force and other stuff for some computing


In the upper/super view, I override the touchesBegan and friends, so that I can get forces and stuff.

BUT, basically, a UIButton doesn't forward a touch event, so (in this example) I extend UIButton (in my code I extend a subclass~ but that doesn't change the problem) and override the touchesBegan and friends, and add next?.touchesBegan(...) to it.


What works:

  • touchesBegan(...) forwards to the super view correctly

What does not work:

  • touchesMoved(...) only forward ONCE to its super views. (even tho the button's touchesMoved is called and that next? is not nil
  • touchesEnded(...) is NOT CALLED when a touchesMoved(...) has been called before (only one touchesMoved(...) call if you follow). and again next? is not nil
// One of the overrided UIButton touches event
extension UIButton {
    open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("Button: touches began - ")
        super.touchesBegan(touches, with: event)
        next?.touchesBegan(touches, with: event)
        if next == nil { print("next was nil!") }
        print("Button: touches began - end\n")
    }
}

// One of the overrided ViewController touches event
// (which is only called once for touchesMoved, and then touchesEnded not called)
extension ViewController {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("ViewController: touches began - ")
        super.touchesBegan(touches, with: event)
        print("ViewController: touches began - end\n")
    }
}

Here is an example project to show you the problem:

  • git clone git@bitbucket.org:5t4rrk/problemtouchmovedonlyonce.git

If someone has any insights about why is this behavioring like this, please let me know \o/

itMaxence
  • 1,230
  • 16
  • 28

1 Answers1

0

I tried a slightly different approach using a subclass of a UIGestureRecognizer. See the code below based on your example project. Interesting it seems the UIButton is checking for the state .begin which the touchesBegan is setting. It might be possible to change your code to not set the state in the touchesBegan method. See if this works for you.

The UIKit gesture recognizers also give you a few delegate methods and the option to use a selector which has access to the state set by the touches... methods. But they don't give you that much information for each individual touch.

Sources:

developer.apple.com/documentation/uikit/touche developer.apple.com/documentation/uikit/uigesturer

raywenderlich.com/6747815-uigesturerecognizer-tu

Code:

class ViewController: UIViewController {
    @IBOutlet weak var button: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        button.addTarget(self, action: #selector(buttonTouched), for: .touchDown)
        
        let gesture = CustomGesture()
        self.view.addGestureRecognizer(gesture)
        self.view.isUserInteractionEnabled = true
    }
    
    @objc
    func buttonTouched(button:UIButton) {
        print("Button ACTION!")
    }
}

class CustomGesture: UIGestureRecognizer {
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        super.touchesBegan(touches, with: event)
        print("ViewController: touches began")
        //  state = .began //Do not set the state to .began as this seems to be blocking the UIButton gesture.
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event!)
        for _ in touches {
            print("ViewController: touches moved")
        }
        state = .changed
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event!)
        print("ViewController: touches ended")
        state = .ended
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesCancelled(touches, with: event!)
        print("ViewController: touches cancelled")
        state = .cancelled
    }
}
Marco Boerner
  • 1,243
  • 1
  • 11
  • 34