I have created a small UIView which contains two UIButtons. The view responds to UITapGesture events. The Buttons are supposed to respond to TouchUpInside, however when I tap the buttons the responder is the underlying view and the tap gesture selector is triggered. Looking for advice or suggestions.
You can modify the method that responds to the tap gesture in the orange view:
-(void) handleTapFrom:(UITapGestureRecognizer*) recognizer {
CGPoint location = [recognizer locationInView:orangeView];
UIView *hitView = [orangeView hitTest:location withEvent:nil];
if ([hitView isKindOfClass:[UIButton class]]) {
return;
}
//code that handle orange view tap
...
}
This way if you touch a UIButton, the tap will be ignored by the underlying view.
The right answer (which prevents the tabrecognizer from highjacking any taps and doesn't need you to implement a delegate etc) is found here. I got a lead to this answer via this post.
In short use:
tapRecognizer.cancelsTouchesInView = NO;
"Which prevents the tap recognizer to be the only one to catch all the
taps"
Each UIView has an 'exclusiveTouch' property. If it's set to YES the view won't pass the touch down the responder chain. Try setting this property on your UIButtons and see if that makes a difference.
How are the views ordered in the view hierarchy? Also, are you creating the interface in IB? If you hook up the connections properly, this shouldn't be an issue at all...
The problem with this design is that it's similar to embedding one button into another. While you may have a valid reason to do it, you should be more careful with the event flow.
What happens is the gesture recognizer intercepting touches before they reach subviews (the two buttons in your case). You should implement UIGestureRecognizerDelegate protocol, namely, gestureRecognizer:shouldReceiveTouch: method, and return NO if the touch is inside any of the two buttons. That will prevent the orange view from usurping touches intended for the buttons and the buttons will start to work as expected.
Check out this question.
uibutton-inside-a-view-that-has-a-uitapgesturerecognizer
Related
I have some UIButtons within a UIScrollView, but I do not want to delay the button touches. However, as soon as the scroll view detects a drag/scroll, I want to cancel the UIButton touch and proceed with the scrolling of the UIScrollView.
I have included the following...
_scrollView.delaysContentTouches = NO;
...but (obviously) the button touch does not cancel when dragged. Does anyone have any suggestions as to how I can implement this functionality?
You can override this method of UIScrollView.
-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return YES;
}
And in your UIButton, you should add different method for different control events.
show highlight effect for UIControlEventTouchDown.
trigger button for UIControlEventTouchUpInside | UIControlEventTouchUpOutside;
clear highlight effect for UIControlEventTouchCancel.
You could use scrollViewWillBeginDragging to fire off a notification and handle the button canceling by listening for it in your buttons' code. I think this is what you are trying to do, but I'm not sure if I have understood your question correctly.
In my program I have placed a UIButton as a subview of a UIView, both of which have userInteractionEnabled set to true. When the button is tapped, an event is called to handle the button tap, which works as expected. However, the button's UIView superview also handles an event which should not be triggered in this case. Can anybody explain why the UIButton AND UIView are both triggering an event? Any help is appreciated.
Should you resign first responder after the button has done its duty so that the view that the button is in is no longer in control.
How about adding: bringSubviewtoFront:scrollview
to the end of your UIButton code. The idea here is to make your uiview the foremost view; which is what I think is happening when you touch the uiview and thus reactivating the triggering events
I have found a solution: I simply made the button a superview of the view which was handling unwanted events, rather than a subview.
I am working on a custom controller. I want to create a slider, to choose between more options. The problem is that when I touch the button I want to it doesn't call my touchesBegan method. But if I press in any other part of my view, it works. How can I get my button to move?
Thanks.
The UIButton is capturing the touch events. if you try this
myButton.userInteractionEnabled = NO;
Then it will no longer consume the touch events but it will also no longer fire the onTouchUpInside event. In this case you process all of the touches in the super view and position the slider button accordingly.
It should drag just fine. This will work for any UIView.
touchesBegan will not work on a UIButton .. for that to work you will have to subclass UIButton and implement touchesBegan
refer this answer for more info https://stackoverflow.com/a/4863734/919545
Why don't you create a custom UIView and avoid using the UIButton class? It is simplier that subclassing UIButto or to set
myButton.userInteractionEnabled = NO;
I have a UIView in which I added a UITapGestureRecognizer. Inside that view I also have a subview in which is basically some kind of a UITableView. The question is that why doesn't the UITableView recognizes the tap on a row, instead it goes to the tap gesture recognizer's handler all the time. Why is this, and how do I solve this? If I set the number of taps to 2, then it works fine. Any idea on how to solve this? Basically it doesn't call the didSelectRowAtIndexPath.
Set cancelsTouchesInView of your recognizer to NO. Otherwise, it "consumes" the touch for itself, and does not pass it on to the table view. That's why the selection event never happens.
If you want both your UITableView and your UITapGestureRecognizer to receive touch events, then yes the cancelsTouchesInView = NO will work. If you want the tap gesture recognizer not to receive the touch events meant for the table view it is slightly less easy but very do-able.
Basically when you are creating your gesture recognizer you set self as its delegate. Then you implement the gestureRecognizer:shouldReceiveTouch: delegate method. A basic implementation might look like this.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
UITableView *tableView = self.tableView;
CGPoint touchPoint = [touch locationInView:tableView];
return ![tableView hitTest:touchPoint withEvent:nil];
}
Essentially this method (as implemented) asks the tableView if this touch's location falls within the tableView's jurisdiction, and if it does, it will block the gesture recognizer from receiving the touch...allowing the tableView to receive the touch.
I am launching a simple UIView with a textField - let's call it orderSetNameView - upon a button tap. I wish to make this view modal, but without using a
[UIViewController presentModalViewContoller:animated:].
It seems I could simply set textInputView.exclusiveTouch = YES to achieve that.
Apple documentation says about exclusiveTouch:
A Boolean value indicating whether the receiver handles touch events
exclusively. If YES, the receiver blocks other views in the same
window from receiving touch events; otherwise, it does not. The
default value is NO.
I assume "same window" means same UIWindow, of which I have only one.
The problem is that when I instantiate my orderSetNameView, add it as a subview, and set exclusiveTouch = YES, touch events happen in all other views of my app, i.e., touch events in other views are not blocked as expected.
// ....
[self.view addSubview:self.orderSetNameView];
[self.orderSetNameView openWithAnimationForAnimationStyle:kMK_AnimationStyleScaleFromCenter];
}
// Set as modal
self.orderSetNameView.exclusiveTouch = YES;
Shouldn't orderSetNameView block touch events in all other views? What am I missing?
From Apple developer forums:
exclusiveTouch only prevents touches in other views during the time in which there's an active touch in the exclusive touch view. That is, if you put a finger down in an exclusive touch view touches won't start in other views until you lift the first finger. It does not prevent touches from starting in other views if there are currently no touches in the exclusiveTouch view.
To truly make this view the only thing on screen that can receive touches you'd need to either add another view over top of everything else to catch the rest of the touches, or subclass a view somewhere in your hierarchy (or your UIWindow itself) and override hitTest:withEvent: to always return your text view when it's visible, or to return nil for touches not in your text view.
Put this in AppDelegate or another file. Use this single time.
// Multi Touch Disable
UIView.appearance().isExclusiveTouch = true
UIButton.appearance().isExclusiveTouch = true