uɥƃnɐʌuop
uɥƃnɐʌuop

Reputation: 15113

Get the visible UIButton that was touched while animating when 2 or more views overlap?

I am programmatically generating several UIButtons and then animating them with a block animation. I am able to determine which button was touched by implementing the code in this answer (demonstrated below).

My issue now is that the images can overlap, so when there is more than 1 view at the given touch location, my code in touchesBegan pulls out the wrong button (i.e., gets the image underneath the visible button that I'm touching).

I wanted to use [touch view] to compare to the UIButtons on screen:

if (myButton==[touch view]) { ...

But that comparison always fails.

My touchesBegan:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];

    for (UIButton *brain in activeBrains) {
        //Works, but only when buttons do not overlap
        if ([brain.layer.presentationLayer hitTest:touchLocation]) {
            [self brainExplodes:brain];
            break;
        }

        /* Comparison always fails
        if (brain == [touch view]) {
            [self brainExplodes:brain];
            break;
        }
        */
    }
}

So my question is how can I determine which of the overlapping images is above the other(s)?

Upvotes: 0

Views: 682

Answers (3)

uɥƃnɐʌuop
uɥƃnɐʌuop

Reputation: 15113

Thanks @Aaron for you help in coming to a good solution. I did refactor your answer for my situation to gain an unnoticable perfomance gain (wee) but more importantly, I think, there's less reading if I have to refactor in the future.

It's pretty obvious in retrospect, I suppose, but of course the activeBrains array reflects the order of the subviews (since each new brain is added to the array right after it's added to the super view). So by simply looping backwards through the array, the proper brain is exploding.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];

    for(int i=activeBrains.count-1; i>=0; i--) {
        UIButton *brain = [activeBrains objectAtIndex:i];

        if ([brain.layer.presentationLayer hitTest:touchLocation]) {
            [self explodeBrain:brain];
            break;
        }
    }
}

Upvotes: 0

MJVDM
MJVDM

Reputation: 3963

The UIView class contains a tag property that you can use to tag individual view objects with an integer value. You can use tags to uniquely identify views inside your view hierarchy and to perform searches for those views at runtime. (Tag-based searches are faster than iterating the view hierarchy yourself.) The default value for the tag property is 0.

To search for a tagged view, use the viewWithTag: method of UIView. This method performs a depth-first search of the receiver and its subviews. It does not search superviews or other parts of the view hierarchy. Thus, calling this method from the root view of a hierarchy searches all views in the hierarchy but calling it from a specific subview searches only a subset of views.

Upvotes: 0

Aaron Hayman
Aaron Hayman

Reputation: 8502

I made a few assumptions here in my code here, but essentially you need to get a list of all the Buttons that have been touched and then find the one 'on top'. The one on top should have the highest index of the buttons in the array of subviews.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];
    NSMutableArray *brainsTouched = [[NSMutableArray alloc] init];
    for (UIButton *brain in activeBrains) {
        //Works, but only when buttons do not overlap
        if ([brain.layer.presentationLayer hitTest:touchLocation]) {
            [brainsTouched addObject:brain];
        }
    }
    NSUInteger currentIndex;
    NSInteger viewDepth = -1;
    UIButton *brainOnTop;
    for (UIButton *brain in brainsTouched){
        currentIndex = [self.view.subviews indexOfObject:brain];
        if (viewDepth < currentIndex){ 
            brainOnTop = brain;
            viewDepth = currentIndex;
        }
    }
    [self brainExplodes:brainOnTop];
}

Also, I typed this in the edit window so please excuse typos.

Upvotes: 1

Related Questions