can a titleView be made to accept tap gestures? - ios

In a custom class MMTimerButton, I set up the button to respond to a single and a double tap
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor: [UIColor clearColor]];
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
singleTapRecognizer.numberOfTapsRequired=1;
singleTapRecognizer.delaysTouchesEnded=YES;
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
doubleTapRecognizer.numberOfTapsRequired=2;
[singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer];
[self addGestureRecognizer:singleTapRecognizer];
[self addGestureRecognizer:doubleTapRecognizer];
}
return self;
}
I implement the two methods like this in the same file
-(void)handleTap:(UITapGestureRecognizer *)sender{
NSLog(#"single tap");
}
-(void)handleDoubleTap:(UITapGestureRecognizer *)sender{
NSLog(#"double tap");
}
In the header, I declare two methods
#import <UIKit/UIKit.h>
#interface MMTimerButton : UIButton
-(void)handleTap:(UITapGestureRecognizer *)sender;
-(void)handleDoubleTap:(UITapGestureRecognizer *)sender;
In the storyboard, I added a button to the interface and set its custom class to be MMTimerButton.
Nothing's happening when I click the button (in the simulator). One click, two click, both nothing.
I also tried doing it without a button, just adding gesture recognizers to the titleView. Nothing happens when I click on the title in the simulator
UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleDoubleTap:)];
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleSingleTap:)];
[singleTap requireGestureRecognizerToFail : doubleTap];
[doubleTap setDelaysTouchesBegan : YES];
[singleTap setDelaysTouchesBegan : YES];
[doubleTap setNumberOfTapsRequired : 2];
[singleTap setNumberOfTapsRequired : 1];
self.navigationItem.title =#"title";
[self.navigationItem.titleView addGestureRecognizer:doubleTap];
[self.navigationItem.titleView addGestureRecognizer:singleTap];
However, when I add this code to header view, it works fine (in the simulator)
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UILabel *headerLabel = [[UILabel alloc]init];
headerLabel.tag = section;
headerLabel.userInteractionEnabled = YES;
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.text = [NSString stringWithFormat:#"0:00",section];
headerLabel.textAlignment = UITextAlignmentCenter;
headerLabel.frame=CGRectMake(10,-5,tableView.frame.size.width,41);
UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleDoubleTap:)];
UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : #selector (handleSingleTap:)];
[singleTap requireGestureRecognizerToFail : doubleTap];
[doubleTap setDelaysTouchesBegan : YES];
[singleTap setDelaysTouchesBegan : YES];
[doubleTap setNumberOfTapsRequired : 2];
[singleTap setNumberOfTapsRequired : 1];
self.navigationItem.title =#"title";
[headerLabel addGestureRecognizer:doubleTap];
[headerLabel addGestureRecognizer:singleTap];
return headerLabel;
//return nil;
}

You are showing a lot of troubled code here, so it's hard to know where to start.
In the case of your initWithFrame:, the problem is that this method is never called for a button. Moreover, you've got a conflict because the button already contains the ability to respond to a tap, internally (it's a button!).
In the second set of code, you never set the titleView to a view, so it is nil, and your code does nothing. Also, you can't set both the title and the titleView (you are setting the title).
The answer to your title question is, of course a titleView can respond to a tap. Lots of apps do this (including mine).
Also, you do not need your requireGestureRecognizerToFail - a single tap gesture recognizer and a double tap gesture recognizer already know how to mediate between themselves.
So basically my advice would be: stop over-complicating everything. Don't make big assumptions and dive in. Start with stuff that you know works and take little steps. Test everything with logging / breakpoints as you go, to see that what's happening is what you imagine.

Related

UITapGesture Selector for UILabel issue

I added tapGesture for self.view and UILabel (subview of mainView), each performs different selectors.
But the only main view tapGesture is being called and label tapgesture is not being called. How it is handled?
Here is the code:
UITapGestureRecognizer *tapGesForSelf = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForSelf:)];
[self.view addGestureRecognizer:tapGesForSelf];
UITapGestureRecognizer *tapLblClick = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForLbl:)];
[lbl addGestureRecognizer:tapLblClick];
For two selectors only one method is called tapGesForSelf.
Here lbl is the subview of self.view.
Try this
- (void)viewDidLoad {
[super viewDidLoad];
[_lbl setUserInteractionEnabled:YES];
UITapGestureRecognizer *tapGesForSelf = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForSelf:)];
[self.view addGestureRecognizer:tapGesForSelf];
UITapGestureRecognizer *tapLblClick = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesForLbl:)];
[_lbl addGestureRecognizer:tapLblClick];
[tapGesForSelf requireGestureRecognizerToFail:tapLblClick];
}
- (void)tapGesForSelf:(UITapGestureRecognizer *)gesture
{
NSLog(#"self");
}
- (void)tapGesForLbl:(UITapGestureRecognizer *)gesture
{
NSLog(#"label");
}
I post answer for your question now once I tried and it worked well.
First see the label in design.I set label text as "Tap Me"
Now I set the code for view and label
- (void)viewDidLoad
{
[super viewDidLoad];
//TapGesture for Label
UITapGestureRecognizer *tapLabel = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionTapLabel:)];
tapLabel.delegate = self;
tapLabel.numberOfTapsRequired = 1;
lblTapMe.userInteractionEnabled = YES;
[lblTapMe addGestureRecognizer:tapLabel];
//TapGesture for View
UITapGestureRecognizer *tapMainView = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionTapMainView:)];
tapMainView.delegate = self;
tapMainView.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapMainView];
}
//Action method for Label
-(void)actionTapLabel:(UITapGestureRecognizer *)gestureOnLabel{
UILabel *label = (UILabel *)gestureOnLabel.view;
NSLog(#"Lable text is - %#",label.text);
}
//Action method for View
-(void)actionTapMainView:(UITapGestureRecognizer *)gestureOnMainView{
NSLog(#"The Main view is tapped");
}
Output Screenshot
Please setUserInteractionEnabled:YES for interact the TapGesture
[lbl setUserInteractionEnabled:YES];

UILongPressGestureRecognizer on UISwitch does not fire

Today I tried to add an additional UILongPressGestureRecognizer to an UISwitch, which did not really work. My code is the following:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething:)];
[lpgr setDelaysTouchesBegan:YES];
[lpgr setDelaysTouchesEnded:YES];
[lpgr setCancelsTouchesInView:YES];
[self.switcher addGestureRecognizer:lpgr];
in the viewDidLoad method of the viewController that is the parent viewController of that switch. (the switcher is an instance variable, set through a storyboard. The IBOutlet is set properly from the UISwitch to the switcherProperty of its viewController).
On a couple of other controls (like a UIButton, UISlider, UIStepper and so on) this works, even without the touches cancelled or delayed, and perfectly triggers the target method. However, with my switch, it refuses that behavior.
I have tried removing any other gestureRecognizer on the UISwitch by iterating through all gestureRecognizers on that switch and calling [switcher removeGestureRecognizer: ... ], I´ve also set all of them to require my longPressGestureRecognizer to fail. Other types of gestureRecognizers don´t fire as well.
As far as I understand, GestureRecognizers will receive touches, before the view or control, which they belong to, receives them. Thus, setCancelsTouchesInView:YES and setDelaysTouchesInView:YES should enable the gestureRecognizer to handle every single gesture until it fails or succeeds, without the view knowing, right?
--------------EDIT------------------
The whole content of my method:
- (void)viewDidLoad
{
[super viewDidLoad];
UIGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.view addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.button addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.slider addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.segmentedControl addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.switcher addGestureRecognizer:lpgr];
lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(edit:)];
[self.stepper addGestureRecognizer:lpgr];
}
As I said, for the other controls this is working, only the switch does not want to work
try with it
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if ((touch.view == yourSwitch)) {//change it to your condition
return NO;
}
return YES;
}
make sure you confirm UIGestureRecognizerDelegate for it to work.

How do I get UIGestureRecognizers to work?

I'm having trouble just turning on gesture recognizers in my code. I added a handleTap callback to to the tap gesture recognizer but the print statement never happens. I'm just not having any luck. Does anyone see what I'm doing wrong here?
In my ViewController.h
#interface ViewController : UIViewController<UITextFieldDelegate, UIGestureRecognizerDelegate> {
}
#end
This is what I have in my ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
View *myview = [[View alloc] initWithFrame:applicationFrame];
myview.userInteractionEnabled = YES;
myview.multipleTouchEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap)];
tap.numberOfTapsRequired = 1;
tap.delegate = self;
[myview addGestureRecognizer:tap];
self.view = myview;
}
-(void)handleTap:(UITapGestureRecognizer*)recognizer {
NSLog(#"dismiss keyboard");
//[usernameTextField resignFirstResponder];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
Edit: I added a text field and when I click on the text field I see the tap selector print statement. But if I tap off the text field I don't get the selector print statement.
I think the problem is with your selector. Try changing
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap)];
And replace it with
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap:)];
To make the selector match the signature for handleTap.
You are passing the wrong selector when initialising.
The line should read like this:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTap:)];
Notice the colon after handleTap to match the method you want to use.
Apparently subclassing the UIView and overriding drawrect with an empty function caused the selector to be called.

Add a gesture recognizer to tableView header doesn't work?

I know that a "table view header"(the most top-part of a table view) is a View
So I try to add a UITapGestureRecognizer to it ,but it doesn't work...
code is simple :
- (void)tap:(UITapGestureRecognizer *)recognizer
{
// do something
}
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
[self.tableView.tableHeaderView addGestureRecognizer:recognizer];
Any tips here to care ? thanks a lot
Here's the thing that works for me:
Instead adding this:
self.tableView.tableHeaderView
I add gesture recognizer on every UILabel on tableview.
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UILabel *headerLabel = [[UILabel alloc]init];
headerLabel.tag = section;
headerLabel.userInteractionEnabled = YES;
headerLabel.backgroundColor = [UIColor greenColor];
headerLabel.text = [NSString stringWithFormat:#"Header No.%d",section];
headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height);
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(catchHeaderGesture:)];
tapGesture.cancelsTouchesInView = NO;
[headerLabel addGestureRecognizer:tapGesture];
return headerLabel;
//return nil;
}
-(void)catchHeaderGesture:(UIGestureRecognizer*)sender
{
UILabel *caughtLabel = (UILabel*)sender.view;
NSLog(#"header no : %d", caughtLabel.tag);
}
I hope that helps.
First of all
Make sure that you call this code section in viewDidLoad or viewWillAppear
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
[self.tableView.tableHeaderView addGestureRecognizer:recognizer];
Second, please make sure that
self.tableView.tableHeaderView
is not null, add
NSLog([self.tableView.tableHeaderView description]);
And check the console for output
I just tried your code and the tap was recieved correctly

Problem with UITapGestureRecognizer for double tap

I've two UITapGestureRecognizer: singleTap and doubleTap initialized with two different actions.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[singleTap requireGestureRecognizerToFail:doubleTap];
[doubleTap setNumberOfTapsRequired:2];
[imageView addGestureRecognizer:doubleTap];
[imageView addGestureRecognizer:singleTap];
When I run my app in simulator the single tap responds correctly but not the double tap ! When I double clicks nothings happens, I suppose iOS dose recognize the double tap because the action of single tap doesn't being called (due to [singleTap requireGestureRecognizerToFail:doubleTap];), but I can't understand why doesn't it do the action handleDoubleTap.
I think the problem is that UIImageView and UILabel both override the default value of YES for the userInteractionEnabled property, and sets it to NO.
Add imageView.userInteractionEnabled = YES; and try again.
The following code works for me:
- (void)handleTap:(UIGestureRecognizer*)gr {
NSLog(#"----------------- tap ----------------");
}
- (void)handleDoubleTap:(UIGestureRecognizer*)gr {
NSLog(#"================= double tap ============");
}
- (XXXView*)createXXXView {
XXXView *view = [[[XXXView alloc] init] autorelease];
view.xxx=...;//irrelevant
UITapGestureRecognizer *dtr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
dtr.numberOfTapsRequired = 2;
UIGestureRecognizer *tr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[tr requireGestureRecognizerToFail:dtr];
[view addGestureRecognizer:tr];
[view addGestureRecognizer:dtr];
return view;
}

Resources