I have a number of classes and want to add a UIGestureRecognizer to their UIImageViews.
I add one correctly to all the classes. Thing is I duplicate code so that each class is adding the same recognizer. These are local recognizers
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleSingleTap:)];
[singleTap setNumberOfTapsRequired:1];
[background addGestureRecognizer:singleTap];
[singleTap release];
so it should still work for each class.
The problem is that it only works on the first class, not on the others.
background is the UIImageView and is present in every class. So I have multiple backgrounds.
I present each new ModelViewController class, is this maybe what the issue is?
I use the recognizer as follows
- (void) handleSingleTap : (UIGestureRecognizer*) sender
{
//do whatever
}
Each class also implements the
#interface Someclass : UIViewController <UIGestureRecognizerDelegate>
Im not sure why it isnt working though. I have a print out in each handleSingleTap method. and nothing gets printed.
First
Are you adding the same recognizer object to different views? If so that won't work. UIGestureRecognizer objects only detect/track gestures in one view. So make sure you have a different recognizer object for each of your views like:
UITapGestureRecognizer *r1 = [UITapGestureRecognizer alloc] initWithTarget:self action:...];
[view1 addGestureRecognizer:r1];
[r1 release];
UITapGestureRecognizer *r2 = [UITapGestureRecognizer alloc] initWithTarget:self action:...];
[view2 addGestureRecognizer:r2];
[r2 release];
...
Second Make sure the view (in this case UIImageView) is [uiimageview setUserInteractionEnabled:YES];
Third If you are just detecting simple touches then is not necessary to adopt UIGestureRecognizerDelegate protocol, so just delete that protocol from the #interface
Hope it helps
Related
I'm trying to create a UIView class that acts like a button. I want to do it entirely programmatically. I am able to get the button to appear but it is not clicking properly.
In my classes implementation file I have an initialization method as well as the following:
- (void)handlePress:(UILongPressGestureRecognizer *)sender{
if(sender.state == UIGestureRecognizerStateBegan){
NSLog(#"in handlePress");
}
}
In my view controller implementation file I have:
- (void)viewDidLoad
{
[super viewDidLoad];
self.button = [[buttonclass alloc] initButton];
[self.view addSubview: self.button];
UILongPressGestureRecognizer *singlePress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handlePress:)];
[self.button.buttonView addGestureRecognizer:singlePress];
}
(button being the instance variable of the class buttonclass and buttonView is just a subview of button)
I'm pretty sure the problem is within the lines:
UILongPressGestureRecognizer *singlePress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handlePress:)];
[self.button.buttonView addGestureRecognizer:singlePress];
I'm not even sure if these lines should go within the button initialization function or in the view controller implementation file. Should I add the following line?
singlePress.delegate = self;
I tried adding that line but I do not know where to put it and an error is saying that the delegate and self aren't the same type. Regardless, something is going wrong when I try to hook up the GestureRecognizer to the action or when I'm hooking the GestureRecognizer to the UIView button.
Any help would be greatly appreciated! Thanks and have a great day.
You should add the gesture on button and not on to the subview of button.
UILongPressGestureRecognizer *singlePress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handlePress:)];
[self.button addGestureRecognizer:singlePress];
There is no need to add following line:
singlePress.delegate = self;
In addition dont forget to release the Button object otherwise you will get an memory leak maybe in case you forget to clean up...
[super viewDidLoad];
self.button = [[buttonclass alloc] initButton];
[self.view addSubview: self.button];
[self.button release];
regards
I have problem with swipe with three gestures
in my .m :
- (IBAction)click:(id)sender {
[_text setText:#"Hello World"];
}
- (IBAction)resetText:(id)sender {
[_text setText:#"Reset"];
}
when I clicked on the screen the output message "Hello World" will be shown on label
and it should shown "Reset" when I swipe three fingers from up to down, but it crashes
the weird thing is when I change the name of IBAction from "resetText" to for example "reset" or whatever name without capital letter it works. With any capital letter, it crashes
this is the Xcode file
I looked at your example project, and it seems that:
The crash only occurs on actual devices
The crash only occurs when the swipe gesture requires 3 or more touches
This looks to me like a bug in the UIGestureRecognizer class when added using Interface Builder, so there isn't much you can do about it now. I filed a radar (#14399827) with Apple describing this issue. You should probably do this as well.
However, you can work around this bug by creating the gesture recogniser in code instead of in the storyboard as you are now.
Remove the gesture recogniser from your storyboard (delete it completely), then add this to the viewDidLoad method in your view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UISwipeGestureRecognizer *recogniser = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(resetText:)];
[recogniser setDirection:UISwipeGestureRecognizerDirectionDown];
[recogniser setNumberOfTouchesRequired:3];
[self.view addGestureRecognizer:recogniser];
}
I understand that this isn't ideal, as it may be more convenient in some cases to add the view controller directly to the storyboard, but unfortunately it seems that you can't currently do that due a bug in Apple's implementation.
I updated your view controller and now it is working for all gestures you want. Please check out and let me know.
- (void)viewDidLoad{
[super viewDidLoad];
//gesture for tap
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(click)];
[self.view addGestureRecognizer:tap];
//gesture for right swipe
UISwipeGestureRecognizer *rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(rightSwipeHandle:)];
rightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[rightRecognizer setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:rightRecognizer];
[rightRecognizer release];
//gesture for right swipe
UISwipeGestureRecognizer *leftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipeHandle:)];
leftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[leftRecognizer setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:leftRecognizer];
[leftRecognizer release];}
- (void)rightSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer{ [_text setText:#" World"]; }
- (void)leftSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer{ [_text setText:#"World"]; }
- (void)click{ [_text setText:#"Hello World"]; }
In my iPad Application I have multiple views on a screen.
What I want to do is apply a double tap Gesture Recognizer to the Navigation Bar. But I had no success, however when the same gesture recognizer applied to that view it works.
Here is the code I am using:
// Create gesture recognizer, notice the selector method
UITapGestureRecognizer *oneFingerTwoTaps =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(oneFingerTwoTaps)] autorelease];
// Set required taps and number of touches
[oneFingerTwoTaps setNumberOfTapsRequired:2];
[oneFingerTwoTaps setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:oneFingerTwoTaps];
This works on view, but when this is done:
[self.navigationController.navigationBar addGestureRecognizer:oneFingerTwoTaps]
doesn't work.
For anyone else viewing this, here is a much simpler way to do this.
[self.navigationController.view addGestureRecognizer:oneFingerTwoTaps];
For this you need to subclass UINavigationBar, override the init button in it and add your gesture recognizer there.
So say you make a subclass called 'CustomNavigationBar' - in your m file you would have an init method like this:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
UISwipeGestureRecognizer *swipeRight;
swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipeRight:)];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
[swipeRight setNumberOfTouchesRequired:1];
[swipeRight setEnabled:YES];
[self addGestureRecognizer:swipeRight];
}
return self;
}
Then you need to set the class name of your navigation bar in interface builder to the name of your subclass.
Also it's handy to add a delegate protocol to your navigation bar to listen for methods sent at the end of your gestures. For example - in the case of the above swipe right:
#protocol CustomNavigationbarDelegate <NSObject>
- (void)customNavBarDidFinishSwipeRight;
#end
then in the m file - on the gesture recognised method (whatever you make it) you can trigger this delegate method.
Hope this helps
I have a UIView (lets call it myView), inside a UIScroller.
myView has elements that can be dragged on it.
When one of these elements are touched, the scroller is locked, to prevent the whole thing from scrolling. The elements have exclusiveTouch = YES. I need the whole thing to scroll when just myView is touched.
I had this working with touchesBegan, touchesMoved, etc., but I am converting this class to gestures. The new class has two gestures attached to it: UIPanGestureRecognizer and UITapGestureRecognizer. Something like:
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(drag:)];
[panGesture setMaximumNumberOfTouches:1];
[panGesture setDelegate:self];
[myView addGestureRecognizer:panGesture];
[panGesture release];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(tapAdd:)];
[singleTap setDelegate:self];
[singleTap setNumberOfTapsRequired:1];
[myView addGestureRecognizer:singleTap];
[singleTap release];
When this class used the old methods (touchesBegan, etc.), I simply forwarded the touches using something like
[self.nextResponder touchesBegan: touches withEvent:event];
but how do I do that using gestures? I mean, the UIPanGestureRecognizer will, in my case, run a method called drag: and this method receives a gesture... how do I forward the touch to its scroller parent, so the whole thing scrolls?
thanks
It's possible you could set up a protocal to make a delegate of your view.
In your header, set up
#protocol MyViewDelegate
- (void)drag;
#end
#interface ....... {
id <MyViewDelegate> delegate;
}
#property (nonatomic, assign) id delegate;
#end
You'll have to synthesize delegate in the implementation file.
In the scroller class, you will have to use the protocol
#interface Scroller ....... <MyViewDelegate> {
In that implementation file, make sure to put myView.delegate = self; when initializing it and add - (void)drag { and do what you want in there. If you need more info, you can of course assing the method in your protocol to be something like - (void)dragWithEvent:(UIEvent *)event or something similar.
Then when you call drag: in myView, you can forward this to the protocol method by calling [self.delegate dragWithEvent:event];.
I realize this is a bit extensive to get your scroller to scroll, but it will forward the message like you want.
It's also possible that you could add the gestures to the scroller rather than to myView, then forward all touches in the touchesBegan method and let the scroller handle it directly
I want to call an action in two classes (a superview and a full screen subview) when the user single taps the screen. But, when I add a UITapGestureRecognizer to the subview, the one added to the superview is overridden. Is it possible to add a UITapGestureRecognizer to a subview without overriding the UITapGestureRecognizer added to the superview?
If so, how can I do this?
Thanks!
Edit:
From my main viewController "MyToolBerController", I'm adding the subview from another viewController as follows:
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:nil];
myPhotoView = photoViewController.view;
[self.view addSubview:myPhotoView];
I add the GestureRecognizer in the MyToolBerController like this:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapFrom:)];
[singleTap setNumberOfTapsRequired:1];
singleTap.delegate = self;
[myPhotoView addGestureRecognizer:singleTap];
[singleTap release];
This all works fine, but I need to call a method in the PhotoViewController class when the view is tapped as well as in the MyToolBerController class.
When I add another UITapGestureRecognizer in the photoViewController, it overrides the UITapGestureRecognizer added in the superView.
Gesture recognizers can dispatch multiple actions when the gesture occurs. You can add the subview as another target of the gesture recognizer and only use a single UITapGestureRecognizer instance:
[tapRecognizer addTarget:theSubview action:#selector(whatever:)];
In your gesture recognizer selector method, pass the information along to the subview. There's no need to have multiple gesture recognizers for the same gesture. Something like:
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *)sender
{
CGPoint tapPoint = [sender locationInView:sender.view.superview];
UIView *subview = [parentView viewWithTag:100];
[subview doSomethingWithPoint:tapPoint];
}
This of course means that your subview that needs to be notified should be given the tag 100 either in Interface Builder or in code when the view controller gets loaded.
Update based on Jonah's code:
So instead of retaining the view, retain the view controller:
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:nil];
self.myPhotoViewController = photoViewController;
Which means you need to declare it this way in the MyToolbarController header:
#property (nonatomic, retain) PhotoViewController *myPhotoViewController;
Then, when your gesture selector gets called, pass the message along to the view controller you retained. Something like:
- (IBAction)handleSingleTapFrom:(UIGestureRecognizer *)sender
{
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[myPhotoViewController doSomethingWithPoint:tapPoint];
}
Of course the -doSomethingWithPoint: method is only for example. You can name and create any method you want that takes any parameter you want to pass in your PhotoViewController.
Let me know if you need further clarification.