Why doesn't UIView.exclusiveTouch work?
Asked Answered
L

2

13

In one of my iPhone projects, I have three views that you can move around by touching and dragging. However, I want to stop the user from moving two views at the same time, by using two fingers. I have therefore tried to experiment with UIView.exclusiveTouch, without any success.

To understand how the property works, I created a brand new project, with the following code in the view controller:

- (void)loadView {

    self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
    UIButton* a = [UIButton buttonWithType:UIButtonTypeInfoDark];
    [a addTarget:self action:@selector(hej:) forControlEvents:UIControlEventTouchUpInside];
    a.center = CGPointMake(50, 50);
    a.multipleTouchEnabled = YES;

    UIButton* b = [UIButton buttonWithType:UIButtonTypeInfoDark];
    [b addTarget:self action:@selector(hej:) forControlEvents:UIControlEventTouchUpInside];
    b.center = CGPointMake(200, 50);
    b.multipleTouchEnabled = YES;

    a.exclusiveTouch = YES;

    [self.view addSubview:a];
    [self.view addSubview:b];

}

- (void)hej:(id)sender
{
    NSLog(@"hej: %@", sender);
}

When running this, hej: gets called, with different senders, when pressing any of the buttons - even though one of them has exclusiveTouch set to YES. I've tried commenting the multipleTouchEnabled-lines, to no avail. Can somebody explain to me what I'm missing here?

Thanks, Eli

Linoleum answered 9/5, 2009 at 13:33 Comment(0)
S
18

From The iPhone OS Programming Guide:

Restricting event delivery to a single view:

By default, a view’s exclusiveTouch property is set to NO. If you set the property to YES, you mark the view so that, if it is tracking touches, it is the only view in the window that is tracking touches. Other views in the window cannot receive those touches. However, a view that is marked “exclusive touch” does not receive touches that are associated with other views in the same window. If a finger contacts an exclusive-touch view, then that touch is delivered only if that view is the only view tracking a finger in that window. If a finger touches a non-exclusive view, then that touch is delivered only if there is not another finger tracking in an exclusive-touch view.

It states that the exclusive touch property does NOT affect touches outside the frame of the view.

To handle this in the past, I use the main view to track ALL TOUCHES on screen instead of letting each subview track touches. The best way is to do:

if(CGRectContainsPoint(thesubviewIcareAbout.frame, theLocationOfTheTouch)){
    //the subview has been touched, do what you want
}
Shoe answered 14/5, 2009 at 7:18 Comment(3)
@Bradlarson I updated the OP with the current link to the documentation, you are welcome :)Zoonosis
@RichardJ.RossIII - So that's where that went. I was looking for where they put the event handling documentation the other day. Thanks.Crossbow
I don't think your interpretation is what the doc says. According to the doc, I think @eliego's code should work as he expected. If not it is a bug either of UIKit or the doc.Salaidh
C
0

I was encountering an issue like this where taps on my UIButtons were getting passed through to a tap gesture recognizer that I had attached to self.view, even though I was setting isExclusiveTouch to true on my UIButtons. Upon reviewing the materials here so far, I decided to put some code in my tap gesture code that checks if the tap location is contained in any UIButton frame and if that frame is also visible on the screen at the same time. If both of those conditions are true, then the UIButton will already have handled the tap, and the event triggered in my gesture recognizer can then be ignored as a pass through of the event. My logic allows me to loop over all subviews, checking if they are of type UIButton, and then checking if the tap was in that view and the view is visible.

    @objc func singleTapped(tap: UITapGestureRecognizer)
    {
        anyControlsBreakpoint()

        let tapPoint = tap.location(in: self.view)

        // Prevent taps inside of buttons from passing through to singleTapped logic
        for subview in self.view.subviews
        {
            if subview is UIButton {
                if pointIsInFrameAndThatFrameIsVisible(view: subview, point: tapPoint)
                {
                    return // Completely ignores pass through events that were already handled by a UIButton
                }
            }
        }

Below is the code that checks if point was inside a visible button. Note that I hide my buttons by setting their alpha to zero. If you are using the isHidden property, your logic might need to look for that.

    func pointIsInFrameAndThatFrameIsVisible(view : UIView, point : CGPoint) -> Bool
    {
        return view.frame.contains(point) && view.alpha == 1
    } 

Chromaticity answered 7/12, 2022 at 18:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.