gestureRecognizer:shouldReceiveTouch: implementation failing - iphone

I'm trying to ignore my gestureRecognizer if the view touched is of certain classes (namely UIButton and UIBarButtonItem) using this:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
NSLog(#"%#", touch.view);
if ([touch.view isKindOfClass:[UIButton class]]){
return NO;
} else if ([touch.view isKindOfClass:[UIBarButtonItem class]]){
return NO;
}
return YES; // handle the touch
}
That works great for UIButton. It doesn't work for UIBarButtonItem. My NSLog results when the UIBarButtonItem is touched are:
<UIToolbarTextButton: 0x7b8f500; frame = (4 0; 60 40); opaque = NO; layer = <CALayer: 0x7b8f900>> 2012-06-26 12:09:48.021
I have tried changed the [UIBarButtonItem class] to [UIToolBarTextButton class], but as this is an undocumented class, I cannot do it. Any other way to do this?
Thanks.

If your UIBarButtonItem is in a UINavigationBar, then you would need to subclass UINavigationBar and implement another
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
in order to catch the gesture for the UIBarButtonItem. There may be other ways of achieving this.

Related

UIPinchGestureRecognizer not working in xcode 4.3.2

Using xCode 4.3.2, I have the following code in initWithFrame in my view.m:
UIPinchGestureRecognizer *recognizer = [[UIPinchGestureRecognizer alloc]
initWithTarget: self action: #selector(pinch:)
];
oldScale = recognizer.scale;
[self pinch: recognizer];
[self addGestureRecognizer: recognizer];
// code to create label is also in here, works. label displays no problem
[self addSubview: label];
my pinch method is as follows:
- (void) pinch: (UIPinchGestureRecognizer *) recognizer
{
NSLog(#"pinch:");
label.bounds = CGRectMake(
(self.bounds.size.width - width * recognizer.scale) / 2,
(self.bounds.size.height - height * recognizer.scale) / 2,
width * recognizer.scale,
height * recognizer.scale
);
label.font = [UIFont systemFontOfSize: 20 * recognizer.scale];
NSString *verdict;
if (recognizer.scale > oldScale) {
verdict = #"spread";
} else if (recognizer.scale < oldScale) {
verdict = #"pinch";
} else {
verdict = #"neither";
}
oldScale = recognizer.scale;
label.text = [NSString stringWithFormat: #"%# %g",
verdict, recognizer.scale
];
}
because i actually call pinch in my initWithFrame method, it runs once, but when i perform a pinch in the iphone simulator, it doesn't register at all. is there some setting in xcode 4.3.2 i don't know about? this code works everywhere else i've tried running it - but those versions of xcode are all 4.3.
Add UIGestureRecognizerDelegate in .h file
Use following code in .m file in view did load...
// Gesture Reconizer Delegate Methods
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(checkpinch)];
[pinch setDelegate:self];
[pinch setDelaysTouchesBegan:YES];
[self.ScrollView addGestureRecognizer:pinch];
[pinch release];
// Gesture Reconizer Methods
-(void)checkpinch {
NSLog(#"YES");
}
>>See this Edited..
Pinch can detect on scroll-view if you want to detect on view than see following reference...
Detect pinch on UIView
Hope, this will help you...
Maybe I am oversimplifying this, but wouldn't be easier to: 1) In your VC, create an IBoutlet property to an instance of your view 2) connect that IBOutlet to your view 3) setup your UIPinchGetsureRecognizer in the VC by overriding the view setter method and change target to the view and keep the pinch method in the view. This way the UIPinchGetsureRecognizer will load every time the view is loaded and be ready for a pinch. Then you would not need to call the pinch method (which defeats the purpose). KB

UIScrollView with a UIButton and UITapGestureRecognizer

I've been playing around with this code, but can't get it right.
Current behaviour: I can scroll around freely on a UIScrollView, even when tapping on buttons. When i tap on something that isn't a button, it calls a method (like i want it to), as a result of a UITapGestureRecognizer. However, i have to hold down on a button for a couple of seconds then touch up for it to trigger the button's UIControlEventTouchUpInside.
What I want to happen: Exactly the same, but to be able to touch the UIButton easily, like I can if i turn off the UITapGestureRecognizer on the view it's sitting on.
Here's my code:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//...
[cell.xButton addTarget:self action:#selector(xButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.delaysContentTouches = YES;
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
[self addGestureRecognizer:tapGestureRecognizer];
tapGestureRecognizer.delegate = self;
}
}
- (void) handleTapFrom: (UITapGestureRecognizer *)recognizer
{
[self.header removeKeyboard];
}
As stavash suggested, you could do the following:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
UIView* touchedView = [touch view];
if([touchedView isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
This works in most cases. In addition you may want to check whether the touched view is a descendent of a the scroll view using isDescendantOfView:.
Hope it helps.
I think your answer is UIGestureRecognizerDelegate's method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
Check if the touch's view property is any of the UIButtons you are trying to avoid: if so return NO, otherwise return YES. Don't forget to assign the UITapGestureRecognizer's delegate so it works. Hope this helps.

How to dismiss an UITextView with tapping outside?

I've got a bit of a doubt with UITextViews. I use to put UITextFields, and remove the keyboard by tapping outside with the faithful old:
-(void)touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event {
for (UIView* view in self.view.subviews) {
if ([view isKindOfClass:[UITextField class]])
[view resignFirstResponder];
}
}
I have this UITextView which is set on my view (and actually is self.myTV, to be precise), and I do the following
-(void)touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event {
for (UIView* view in self.view.subviews) {
if ([view isKindOfClass:[UITextView class]])
[view resignFirstResponder];
}
}
Same as above, save for the different class...
Well it doesn't seem to work. Anyone has a suggestion?
An easy way to resign first responder for any subview that is currently the first responder is:
[self.view endEditing:NO];
Or if you want to force the first responder to resign without asking it first:
[self.view endEditing:YES];
The problem is that UITextView is not FirstResponder in your case as per the subview hierarchy of your app.
I would suggest you to categorize UIView to find the first responder as under:
This category on UIView, which calls on the UIWindow and traces for the first responder.
#implementation UIView (FindAndResignFirstResponder)
- (BOOL)findAndResignFirstResponder
{
if (self.isFirstResponder) {
[self resignFirstResponder];
return YES;
}
for (UIView *subView in self.subviews) {
if ([subView findAndResignFirstResponder])
return YES;
}
return NO;
}
#end

How to have two different methods for a SWIPE over a button and a TouchUpInside?

I have a view in which I have several large buttons which I set up like this:
SwipeButton* btn = [[[SwipeButton alloc] initWithFrame:CGRectMake(55, 0, 300, 50)] autorelease];
btn.tag = k;
[btn setBackgroundImage:[UIImage imageNamed:[NSString stringWithFormat:#"titleBackground.png"]]
forState:UIControlStateNormal];
[btn addTarget:self
action:#selector(indexAction:)
forControlEvents:UIControlEventTouchUpInside];
I would like to be able to either touch these buttons and then fire the indexAction method, or - if a swipe was recognised - to fire another method.
I thought I subclass UIButton to have swipes come through, but this isn't really a solution as now both swipes and clicks are recognised, so BOTH methods fire.
Is there a way around this? How can I prioritise the swiping and only allow the touchupinside if there was NO SWIPE?
Any help would be very much appreciated.
Here is how I subclassed the UIButton:
#import "SwipeButton.h"
#implementation SwipeButton
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.superview touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self.superview touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self.superview touchesEnded:touches withEvent:event];
}
#end
I don't think that you need to subclass UIButton class. Why don't you try something like this:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [UIImage imageNamed:#"picture_0.png"];
[button setImage:image forState:UIControlStateNormal];
[button setFrame:CGRectMake((applicationFrame.size.width - image.size.width)/2, applicationFrame.size.height/2, image.size.width, image.size.height)];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
UISwipeGestureRecognizer *recognizer;
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeFrom:)];
[recognizer setDirection:UISwipeGestureRecognizerDirectionRight];
[recognizer setDelegate:self];
[[self view] addGestureRecognizer:recognizer];
[recognizer release];
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeFrom:)];
[recognizer setDirection:UISwipeGestureRecognizerDirectionLeft];
[recognizer setDelegate:self];
[[self view] addGestureRecognizer:recognizer];
[recognizer release];
Then you create the methods to handle taps and swipes on the button:
// Prevent recognizing touches on the slider
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIButton class]]) {
return YES;
}
return NO;
}
-(void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer {
if ([recognizer direction] == UISwipeGestureRecognizerDirectionLeft) {
// Handle left swipe
NSLog(#"left swipe");
} else {
// Handle right swipe
NSLog(#"right swipe");
}
}
- (void)buttonTapped:(id)sender {
NSLog(#"button pressed");
}
First of the three previous method is declared in a protocol, so don't forget to say that your class is implementing that protocol:
#interface MyViewController : UIViewController <UIGestureRecognizerDelegate>
It works, I just tried it myself. Hope it helps! ;)
Take a look at the UIGestureRecognizer documentation and related examples (e.g.).

Resign First Responder on ScrollView Touch

How can I hide the keyboard on ScrollView touch event...
Scenario is like that...
->View ->ScrollView ->Textfield
I want to hide the keyboard on touch of scrollView. I tried to override the class for scrollview but still i can't do it.
Doing like this will help:
#interface MyClass <UIScrollViewDelegate> {
}
#implementation
- (void)viewDidLoad {
yourScrollView.delegate = self;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[myTextField resignFirstResponder];
}
If you really want to handle the touch event, then you need to subclass the UIScrollView and override the method:
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
}
More about UIScrollView touch
This is what worked for me
in your viewController's viewDidLoad-method
UITapGestureRecognizer *tapScroll = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapped)];
tapScroll.cancelsTouchesInView = NO;
[viewScroller addGestureRecognizer:tapScroll];
where viewScroller is your scroller. In the tapped-method we have
- (void) tapped {
[self.view endEditing:YES];
}
Don't know why but the above did not work for me...even though it should
Try this:
Add gesturerecognizer for scrollview,
UITapGestureRecognizer *tapScroll = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped)];
tapScroll.cancelsTouchesInView = NO;
[yourScrollview addGestureRecognizer:tapScroll];
[tapScroll release];
Resign your keyboard in (tapped:) method.
Please, take a look at this answer, This is the easiest one I found.
UitextField resignFirstResponder does not work in scroll view
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
[self.view endEditing:YES];
return YES;
}