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
Related
In my view I want to add a target which should be fired when I click the view. I can do the same through IB or I have done it for buttons even in code. However I have no idea how to do it for UIView programatically.
Anyone has done that before.
Help me.
For clicking a UIView you have to use UIGestureRecognizer or UITouch. This would only help in prompting an action. The UIButton has a selector method whereas the UIView does not have any such method. Also , this is same for UIImageViews etc also.
You can acheive this using UIGestureRecognizer.
Step 1:
Add your UIView as a property in your viewcontroller
#property (strong, nonatomic) IBOutlet UIView *yourView;
Step 2:
Set UIGestureRecognizer for your UIView.
- (void)viewDidLoad {
[super viewDidLoad];
UIGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[self.yourView addGestureRecognizer:gesture];
}
Step 3:
Handle the click on UIView.
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
//to get the clicked location inside the view do this.
CGPoint point = [gestureRecognizer locationInView:self.yourView];
}
Remember that A UIGestureRecognizer is to be be used with a single view.
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 11 UIView on each has been set a Gesture Recognizer. This UIView are within mutable array and each have got a tag. How do to pass tag of UIView pressed to method selector?
viewIcone.userInteractionEnabled = YES;
viewIcone.tag = index;
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(touchUp:)];
tap.delegate = self;
[viewIcone addGestureRecognizer:tap];
[self.arrayMutabile insertObject:viewIcone atIndex:index];
[self addSubview:viewIcone];
The implementation of method selector is:
-(void) touchUp: (UITapGestureRecognizer *) gestureRecognizer{
NSInteger tag = [self.arrayMutabile indexOfObject:self.viewIcone];
NSLog(#"the tag is %d", tag);
}
I always leave 10. Where is the wrong?
Thanks in advance
The UITapGestureRecognizer has a view property which it inherits from it's parent:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIGestureRecognizer_Class/Reference/Reference.html#//apple_ref/occ/cl/UIGestureRecognizer
The view the gesture recognizer is attached to. (read-only)
#property(nonatomic, readonly) UIView *view
Discussion
You attach (or add) a gesture recognizer to a UIView object using the addGestureRecognizer: method.
Availability
Available in iOS 3.2 and later.
Declared In
UIGestureRecognizer.h
This will be the view that was tapped and you can retrieve the tag from there.
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
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.