Search code examples
iosuiviewcontrolleruibuttonuislideruiresponder

Is there a general way to be notified of all touches began and moved at the UIViewController level?


I have a UIViewController subclass whose view has a complex hierarchy of descendant views, including UIButtons and a UISlider. I want to hide the controls after 5 seconds have passed without any user interaction. The 5 seconds part is easy: use a timer. I don't know of a general way to detect user interaction though.

My first thought was to override UIResponder's touchesBegan:withEvent: and touchesMoved:withEvent: in the UIViewController subclass, but those don't get called when the user taps a UIButton or drags the knob on the UISlider.

What I don't want to do is register for notifications from every control, if I can help it. I also don't want to mess with the UIWindow. Any other ideas?


Solution

  • I ended up creating a continuous gesture recognizer that recognizes when there is at least one touch. I added it to the UIViewController subclass's view and it works great! It took me a long time to realize that it needed both cancelsTouchesInView and delaysTouchesEnded set to false or else it would fail one way or another.

    class TouchDownGestureRecognizer: UIGestureRecognizer {
    
        override init(target: AnyObject, action: Selector) {
            super.init(target: target, action: action)
    
            cancelsTouchesInView = false
            delaysTouchesEnded = false
        }
    
        override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
            state = .Began
        }
    
        override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
            if numberOfTouches() - touches.count == 0 {
                state = .Ended
            }
        }
    
        override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
            if numberOfTouches() - touches.count == 0 {
                state = .Cancelled
            }
        }
    
        override func shouldBeRequiredToFailByGestureRecognizer(otherGestureRecognizer: UIGestureRecognizer!) -> Bool {
            return false
        }
    
        override func shouldRequireFailureOfGestureRecognizer(otherGestureRecognizer: UIGestureRecognizer!) -> Bool {
            return false
        }
    
        override func canPreventGestureRecognizer(preventedGestureRecognizer: UIGestureRecognizer!) -> Bool {
            return false
        }
    
        override func canBePreventedByGestureRecognizer(preventingGestureRecognizer: UIGestureRecognizer!) -> Bool {
            return false
        }
    
    }