Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 215
Hilton Campbell Avatar asked Oct 21 '25 06:10

Hilton Campbell


2 Answers

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
    }

}
like image 177
Hilton Campbell Avatar answered Oct 23 '25 19:10

Hilton Campbell


Add a UIGestureRecognizer to your UIViewController by calling setupTapGesture in viewDidLoad

- (void)setupTapGesture 
{
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleVisibility)];
    tapGesture.delegate = self;
    [self.view addGestureRecognizer:tapGesture];
}

And then use this callback to detect touches from button, sliders (UIControl)

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer    shouldReceiveTouch:(UITouch *)touch 
{
        //To ignore touches from Player Controls View
        if ([[touch.view superview] isKindOfClass:[UIControl class]]) 
        {
            return NO;
        }
        return YES;
}
like image 37
jarora Avatar answered Oct 23 '25 21:10

jarora



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!