So I have a method then when called generates a simple UIView with some labels in it:
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 300, 250)];
view1.backgroundColor = [UIColor redColor];
view1.userInteractionEnabled = YES;
[self.view addSubview:view1];
I call this method 6 times so it places 6 UIViews (I give them different coordinates of course) around the screen.
How can I detect when a user swipes right on one of them and then trigger some other method?
I've tried something like this:
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(myLabel1Tap)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[view1 addGestureRecognizer:swipeRight];
and then a method:
- (void)myLabel1Tap {
}
But I'm not sure what to do inside that method, how can I know which view was swiped if they are all called the same 'view1'?
change the gesture recognizers selector to accept an argument (by adding a colon after the method signature)
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(myLabel1Swipe:)];
this will mean the gesture recognizer will get passed in, and then you can perform actions based on the gesture recognizers properties, e.g.
- (void)myLabel1Swipe:(UISwipeGestureRecognizer *)recogniser
{
UIView *swipedView = [recognizer view];
//do whatever you want with this view
}
Related
I have an object based on UIView that I've set up to recognise swipe gestures. The view has another UIView based object in the middle of it.
When I swipe left or right, regardless of where I put my finger, the correct event fires.
When I swipe up or down the correct event only fires if my finger isn't starting in the subview in the middle?
Why?
Here's the init code for the object:
- (id)init
{
self = [super init];
if (self) {
[self setFrame:CGRectMake(0,
0,
[self getScreenWidth],
[self getScreenHeight])];
backView = [[UIView alloc] initWithFrame:CGRectMake(0,
0,
[self getScreenWidth],
[self getScreenHeight])];
backView.backgroundColor = [UIColor whiteColor];
[self addSubview:backView];
[self bringSubviewToFront:backView];
theCard = [[actCard alloc] init:CARD_POS_CENTER];
[backView addSubview:theCard];
[backView bringSubviewToFront:theCard];
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(showGestureForSwipeRecognizer:)];
swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(showGestureForSwipeRecognizer:)];
swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(showGestureForSwipeRecognizer:)];
swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(showGestureForSwipeRecognizer:)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[self addGestureRecognizer:swipeLeft];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
[self addGestureRecognizer:swipeRight];
[swipeUp setDirection:UISwipeGestureRecognizerDirectionUp];
[self addGestureRecognizer:swipeUp];
[swipeDown setDirection:UISwipeGestureRecognizerDirectionDown];
[self addGestureRecognizer:swipeDown];
NSLog(#"Enabled: %d",swipeLeft.isEnabled);
NSLog(#"Enabled: %d",swipeRight.isEnabled);
NSLog(#"Enabled: %d",swipeUp.isEnabled);
NSLog(#"Enabled: %d",swipeDown.isEnabled);
}
return self;
}
Try setting the delegate of your Upward swipe and downward swipe to be your MainViewController.Something like :
swipeDown .delegate = self;
swipeUp .delegate = self;
This should make your main view controller ( which contains the secondary UIView ) responsible for the gestures received.
Hope this helps!
Obvious! What a newb I am.
The UITextView still had scrollEnabled. That means the UITextView took the up & down swipes, they don't get passed on to the view.
In my app, I have an image and a UITextView.
I have created a UITapGestureRecognizer for both the views but the issue is that wherever I click on the screen, only the method associated with the UITextView gets executed.
Even if I click on the image, only the UITapGestureRecognizer method associated with the UITextView gets executed.
Following is the code I've implemented:
UITapGestureRecognizer *tapGestureRecognizerImage = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTapFromImage:)];
[infobutton addGestureRecognizer:tapGestureRecognizerImage];
[[self view] addGestureRecognizer:tapGestureRecognizerImage];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTapFrom:)];
[messageOne addGestureRecognizer:tapGestureRecognizer];
[[self view] addGestureRecognizer:tapGestureRecognizer];
//The following are the methods associated
- (void) handleTapFrom: (UITapGestureRecognizer *)recognizer {
//Code to handle the gesture
NSLog(#"I am in handleTapFrom method");
}
- (void) handleTapFromImage: (UITapGestureRecognizer *)recognizer {
//Code to handle the gesture
NSLog(#"I am in handleTapFrom Image method");
[self.view makeToast:#"Your verification code does not match. Re-enter your verification code"];
}
I am sure I am missing something here.
The association in storyboard is correct to my knowledge.
Please correct me where I am going wrong
Thanks for your time
You should not add gesture on self.view.
It should get added on the view for which you want to identify tap event.
You are setting both the Tap Gesture Objects on [self view] object.
Also, the UIImageView object, lets call it imageObj, should have userInteractionEnabled = YES.
instead of:
[[self view] addGestureRecognizer:tapGestureRecognizerImage];
you should do:
[imageObj setUserInteractionEnabled:YES];
[imageObj addGestureRecognizer:tapGestureRecognizerImage];
You generally use -addGestureRecognizer: on the object you want your gesture object to work on.
Say you have a UITapGestureRecognizer object called myTapGesture.
Then, to make it work...
on a UILabel *lblSomeObj it will be:
[lblSomeObj addGestureRecognizer:myTapGesture];
on a UIView *vwSomeObj it will be:
[vwSomeObj addGestureRecognizer:myTapGesture];
etc...
Just add the gesture in the respective views and not in self.view.
UITapGestureRecognizer *tapGestureRecognizerImage = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFromImage:)];
[infobutton addGestureRecognizer:tapGestureRecognizerImage];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
[messageOne addGestureRecognizer:tapGestureRecognizer];
Add gesture on UIImageView object and make sure that image view userInteractionEnabled is set to YES
imageObj.userInteractionEnabled = YES;
[imageObj addGestureRecognizer:tapGestureRecognizerImage];
You need to include this piece of code:-
[tapGestureRecognizerImage requireGestureRecognizerToFail:tapGestureRecognizer];
[imageObj addGestureRecognizer:tapGestureRecognizerImage];
I have a UIScrollView and inside this I have UILabels. I need to detect touch events for the UILabels. At the moment, it is detecting the touch inside the second label only. It ignores the first.
I have the code -
Creating the UIScrollView
backGroundView = [[UIScrollView alloc] init];
backGroundView.frame= self.view.frame;
backGroundView.userInteractionEnabled = YES;
[backGroundView setScrollEnabled:YES];
backGroundView.showsVerticalScrollIndicator = YES;
backGroundView.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
backGroundView.delegate = self;
[self.view addSubview:backGroundView];
Creating the UILabel
UILabel *OneDay = [[UILabel alloc] initWithFrame:CGRectMake(15, stockChart.bounds.origin.y + stockChart.bounds.size.height + 35, 40, 30)];
OneDay.text = #"1d";
OneDay.tag = 1;
OneDay.userInteractionEnabled = YES;
OneDay.layer.borderColor = [UIColor grayColor].CGColor;
OneDay.layer.borderWidth = 1.0f;
OneDay.textAlignment = UITextAlignmentCenter;
[OneDay addGestureRecognizer:detectTimeFrameChange];
[backGroundView addSubview:OneDay];
UILabel *FiveDay = [[UILabel alloc] initWithFrame:CGRectMake(45, stockChart.bounds.origin.y + stockChart.bounds.size.height + 35, 40, 30)];
FiveDay.text = #"5d";
FiveDay.tag = 2;
FiveDay.userInteractionEnabled = YES;
FiveDay.layer.borderColor = [UIColor grayColor].CGColor;
FiveDay.layer.borderWidth = 1.0f;
FiveDay.textAlignment = UITextAlignmentCenter;
[FiveDay addGestureRecognizer:detectTimeFrameChange];
[backGroundView addSubview:FiveDay];
Creating the gesturerecognizer
UITapGestureRecognizer *detectTimeFrameChange = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(timeFrameLabelTapped:)];
detectTimeFrameChange.numberOfTapsRequired = 1;
[backGroundView addGestureRecognizer:detectTimeFrameChange];
Handling gesture
-(void)timeFrameLabelTapped:(UITapGestureRecognizer*)recognizer{
if (recognizer.view.tag == 1) {
NSLog(#"One pressed");
}
else if (recognizer.view.tag == 2){
NSLog(#"2 pressed");
}
}
You can use this :
UITapGestureRecognizer *labelTap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(labelTapped)];
labelTap.numberOfTapsRequired=1;
[yourLabel addGestureRecognizer:labelTap];
handle the touch tap event inside labelTapped method:
-(void)labelTapped
{
//your code to handle tap
}
Touch events are not detected on UIScrollview for getting your requirement add tap gestures to your label.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured)];
[OneDay addGestureRecognizer:singleTap];
-(void)singleTapGestureCaptured{
NSLog(#"touch detected");
}
You can find using tapgesturerecogniser like that...
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
[self.scrollview addGestureRecognizer:singleFingerTap];
//The event handling method
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
if(recognizer.view.tag == 1){}
//Do stuff here...
}
Where have you written the touchesBegan?
If you want to detect the touches in the label you'll have to create a subclass of label and write your touchesBegan there to detect the touch events
The problem here is that you are trying to use the same gesture recognizer for multiple views. A gesture recognizer can only be attached to a single view at once. You are only receiving events from the last view, because that is the view the recognizer is currently attached to. To fix the issue, simply create a gesture recognizer for each view you want to detect touches in.
I have created a UIViewController, which contains a UIView object.
I want the UIView object response to a single tap by 1 finger, and also pinch by 2 fingers.
I have clicked the "User Interaction Enabled" and the "Multiple touch" options in the xib.
I create both UITapGestureRecognizer, and UIPinchGestureRecognizer inside the UIView function initWithFrame:, as it is the only entry point for the UIView object. But the object doesn't response to the UIGestureRecognizer objects. Why? Or, where is the best place to put the UIGestureRecognizer objects?
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Your problem is that when you instantiate your custom view subclass in IB, initWithCoder: is called. If you had done it programmatically using initWithFrame: it would have worked properly.
Two ways you can fix this,
Move the gestures setup to a different method and call them in both initWithFrame: and initWithCoder:. I would recommend doing this if you intend to reuse this component and expose some kind of callbacks on gestures.
If you want to implement this once and have a lot of interacting with the controller element, add them in viewDidLoad.
I tried your code and it works for me.
Where are you setting the view? Maybe it has any other view in front, or its superview has userInteractionEnabled disabled.
I created a UIView subclass and added this code:
-(void) handleSingleTap:(UITapGestureRecognizer *)gr {
NSLog(#"handleSingleTap");
}
-(void) handleDoubleMove:(UIPinchGestureRecognizer *)gr {
NSLog(#"handleDoubleMove");
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// single tap
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self addGestureRecognizer: singleTap];
// 2 fingers pinch
UIPinchGestureRecognizer* doubleMove = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleMove:)];
[self addGestureRecognizer: doubleMove];
}
return self;
}
Then, in my controller:
-(void) viewDidLoad {
[super viewDidLoad];
MyView * myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
myView.backgroundColor = [UIColor greenColor];
[self.view addSubview:myView];
[myView release];
}
And it works.
i think if you are working with .xib, initialization can also be done in
-(void)awakeFormNib;
Adding the GestureRecognizers is typically done in the viewDidLoad method of the UIViewController.
I have a TableView containing UITableViewCells, each with 6 buttons...
A UISwipeGestureRecognizer is added to each of the buttons like so:
- (void)awakeFromNib {
UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_XXSButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_XSButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_SButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_MButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_LButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_XLButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_XXLButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
}
- (void)handleRightSwipe:(UIGestureRecognizer *)gestureRecognizer {
UIButton *button = (UIButton *)[gestureRecognizer view];
[self zeroButton:button];
}
When the swipe is recognised, it calls the method zeroButton .
No matter which button I do the swipe on, the view retrieved is of the first button. I would have expected [gestureRecognizer view] to return the button the swipe was performed on.
Any ideas why I always receive the gesture of the first button only?
I recognise that this code isn't the most elegant to start with, having to create 6 different UISwipeGestureRecognizer. I would have preferred to create the gesture on the UITableViewCell itself, and manually check if the swipe originated or passed over a particular button. Unfortunately, my attempt at this has proven unreliable. So if someone has a suggestion on how to implement that method instead would be even better
(I'll post that "solution" later)
Thanks...
One alternative approach, since you are using UIButtons would be using addTarget:action:forControlEvents: on your buttons for the events:
UIControlEventTouchDragInside
UIControlEventTouchDragOutside
UIControlEventTouchDragEnter
UIControlEventTouchDragExit
and the do your handling in the action method you specify.
It should not be difficult to "map" the branches within your gesture handling method to different methods (or just one method, if you like) inside of your class.
I understand this is not a completely satisfactory answer, since it gives up using gesture recognizers (and their facilities), but it could work for you.
As to why the gesture recognizer does not work correctly when applied to a UITableViewCell (and possibly it also explains why only the first button seems to receive the gesture), the behavior you described in your comment, reminded me that UIScrollView (base class of UITableView) is greedy with touches (not so much as a UIWebView, though) and that this for sure has an impact on how touches are forwarded. So, I don't know if it works, but I found this in UIScrollView:
delaysContentTouches
If the value of this property is YES, the scroll view delays handling the touch-down gesture until it can determine if scrolling is the intent. If the value is NO , the scroll view immediately calls touchesShouldBegin:withEvent:inContentView:. The default value is YES.
This could work or not, it could even make the UITableView not work at all, but it is easy to try out.
If this does not work and my previous hypothesis is still correct, a way around it would be subclassing UITableView and overriding the touch related methods (e.g., touchesShouldBegin:withEvent:inContentView:) so you can customize their behavior.
OLD and WRONG ANSWER:
It is my understanding the you can share one same gesture recognizer among different views, like this:
rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
[_LButton addGestureRecognizer:rightSwipe];
[_SButton addGestureRecognizer:rightSwipe];
[_XButton addGestureRecognizer:rightSwipe];
[rightSwipe release];
I don't know if this can solve your issue, since what happens in your case is that, no matter which button you swipe, it is always that first gesture recognizer that gets activated and this is pretty hard to explain since you have different buttons. This could possibly be related to some overlapping (I am guessing) among the buttons, or to the fact that all of your buttons are "embedded" in a table cell, so the swipe mechanism at a cell level interferes with the swipe mechanism at the sub-cell level, but I am not sure about it.
As an alternative approach, which is surely working, you could attach your gesture recognizer to the very table cell (you mentioned your attempt to attach it to the table view itself; attaching to the table cell will work).
Hope this helps.
Actually, it was an error in the zeroButton code that caused it to always reset the first button. I used the tag assigned to the button to determine which action to create. My tags were incorrectly set! silly me. So the code in my original question was actually correct.
Since I have amended the code as follow:
- (void)awakeFromNib {
UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleRightSwipe:)];
rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
rightSwipe.delegate = self;
[self addGestureRecognizer:rightSwipe];
[rightSwipe release];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIButton class]])
{
// only recognises gesture started on a button
return YES;
}
return NO;
}
- (void)handleRightSwipe:(UIGestureRecognizer *)gestureRecognizer {
// Determine which button received the right swipe
// Get the position of the point tapped in the UIViewCell co-ordinate system
CGPoint tapPoint = [gestureRecognizer locationInView:self];
NSLog(#"%.1f %.1f", tapPoint.x , tapPoint.y);
UIView *viewAtBottomOfHierarchy = [self hitTest:tapPoint withEvent:nil];
if ([viewAtBottomOfHierarchy isKindOfClass:[UIButton class]])
{
[self zeroButton:(UIButton *)viewAtBottomOfHierarchy];
}
}
This looks at where the swipe originate and determine which button it started into (if any)...