Two finger tap on UIButton - objective-c

I've got a UIButton in my application called myButton. I'd like to react to a single finger tap and a two finger tap on the button differently. However, from what I understand UIButton objects are only able to detect single finger touches touchDown, touchUpInside, etc. After doing some research, it looks like I'll have to use the touchesBegan method and just check to see if both of the fingers are within myButton frame. Is there any easier way to do this? Thanks!

Yes it is! By using UITapGestureRecognizer you can do something like this
- (void)viewDidLoad {
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)]
tap.numberOfTapsRequired = 2;
[myButton addGestureRecognizer:tap];
[tap release];
}
- (void)tap:(UITapGestureRecognizer *)gesture {
NSLog(#"Tap!");
}

Related

Getting the UIButton that was double-tapped from UIGestureRecognizer's action method

I wrote a fairly long action method connected to several UIButtons, and now I've realised I would like to have something different happen if I tap one of the buttons twice. So I am setting up two gesture recognizers:
UITapGestureRecognizer *tapOnce = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnce:)];
UITapGestureRecognizer *tapTwice = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTwice:)];
tapOnce.numberOfTapsRequired = 1;
tapTwice.numberOfTapsRequired = 2;
[tapOnce requireGestureRecognizerToFail:tapTwice];
[self.mybutton addGestureRecognizer:tapOnce];
[self.mybutton addGestureRecognizer:tapTwice];
then, I implement the methods:
- (void)tapOnce:(UIGestureRecognizer *)gesture{
//Do what you were already doing
}
- (void)tapTwice:(UIGestureRecognizer *)gesture{
// Some new functionality
}
Now, I don't have a problem with the new functionality. Where I get stuck is trying to get the tapOnce: method to do what my button was already doing. I need to know which button was tapped, so I can't use this:
[myButton sendActionsForControlEvents: UIControlEventTouchUpInside];
I also tried the old:
for (button in myArrayOfButtons){
[button sendActionsForControlEvents: UIControlEventTouchUpInside];
}
No luck either.
Then I went the way of actually creating a separate method for the implementation of my button, so what was:
- (IBAction)buttonPressed:(id)sender {
//my functionality...
}
became:
- (IBAction)buttonPressed:(id)sender {
[self myMethodwithSender:sender];
}
-(void)myMethodwithSender: (id)sender{
// my functionality
}
So that worked in itself, but when I try to put the tap once and twice into play I get stuck. I tried putting my new method in the method for tap once:
- (void)tapOnce:(UIGestureRecognizer *)gesture{
[self myMethodwithSender:sender];
}
but of course there is no sender in this method so it doesn't work (right?)
Then I tried changing this:
UITapGestureRecognizer *tapOnce = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnce:)];
to this:
UITapGestureRecognizer *tapOnce = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myMethodwithSender:)];
Which doesn't work either. So I am not sure how to do this. Any suggestions welcome.
With:
- (void)tapOnce:(UIGestureRecognizer *)gesture{
[self myMethodwithSender:sender];
}
If myMethodWithSender doesn't use sender, you can send nil. If it needs it to be the button, gesture.view should be that button (if you attached the recognizer to the button)

UIButton not works in iOS 5.x , everything is fine in iOS 6.x

By tapping simple UIButton on main UIView, additional View (subview) appears in the center of screen (subview created programmatically). On that subview I have UIButton that launches MPMoviePlayer (this code is inside method that creates subview):
// Create play button
UIButton *playButton = [UIButton buttonWithType:UIButtonTypeCustom];
[playButton addTarget:self
action:#selector(wtf)
forControlEvents:UIControlEventTouchUpInside];
[playButton setTitle:#"" forState:UIControlEventTouchUpInside];
[playButton setImage:[UIImage imageNamed:[self.playButtons objectAtIndex:[sender tag] - 1]] forState:UIControlStateNormal];
playButton.frame = playButtonRect;
playButton.userInteractionEnabled = YES;
This button added as subview to this view inside the same method:
[self.infoView addSubview:playButton];
In iOS Simulator 6.0 and real device with iOS 6.0 everything works fine, in Simulator iOS 5.0 and device with 5.0 I have a very strange behavior: button start to work only when I made drag by the area of this button, when I just clicked on button - it called method what called when user taps anywhere in the screen, like my button doesn't appear on screen (but visually it does). My goal is to make this app for 5.x, so I try to find answer on this strange issue.
Any advices are welcome!
Are you adding a UITapGestureRecognizer to infoView or any of it subviews?
If that is the case, your gesture recognizer is inhibiting the UIButton action. You could use the solution provided by Kevin Ballard or cdasher on this post
You just need to set the view that has the UITapGestureRecognizer as the delegate of that Gesture Recognizer (UIGestureRecognizerDelegate). Then you can add the following code:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return ! ([touch.view isKindOfClass:[UIControl class]]);
}
And your Tap Gesture Recognizer should look like this:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureAction)];
tap.delegate = self;
[self.view addGestureRecognizer:tap];
Hope this helps!

UIKeyboardTypeNumberPad dismiss keyboard

How is a user suppose to indicate that he/she is done typing if the keyboard is of type UIKeyboardTypeNumberPad? There's no return/done button so how do I know the user wants to dismiss the keyboard?
Thanks
After researching this problem for a while, I found no really satisfactory answers. Some solutions hack a "Done" button, but that doesn't work well for localized applications. My apps don't use any words so I definitely don't want a "Done" button.
As mentioned by taskinoor it is convenient for editing to end if the view is touched elsewhere. That's what I did, and here is code showing the details:
Creating the textField programmatically:
aTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[aTextField addTarget:self action:#selector(c1Update) forControlEvents:UIControlEventEditingDidEnd];
aTextField.keyboardType = UIKeyboardTypeNumberPad;
[yourView addSubview:aTextField];
c1Value = aTextField; // I like to keep a pointer for UI updates, alpha etc.
What happens in c1Update:
- (void)c1Update {
[[self brain] setC1:[c1Value.text intValue]]; // update the model
}
Gesture recognizer on view:
- (void)viewDidLoad {
[super viewDidLoad];
[self updateViewColors];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped)];
tapRecognizer.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapRecognizer];
}
- (void) viewTapped {
if (c1Value.isFirstResponder) {[c1Value resignFirstResponder];} // dismisses keyboard, which causes c1Update to invoke.
}
You can either provide a cancel/hide button (outside the keypad) or hide that when touched anywhere outside the keypad. These two are common practices.
try this. it might help you
hide the keypad when touched the screen.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
}

UIImageView subclass not passing touches to controller subviews

When I add a UIImageView subclass (fullscreen) behind a couple of UIButton subclass instances, those buttons stop receiving the touches and don't function anymore. Nothing I have tried has worked to restore their connectivity to user touches.
I have tried:
a) Using a UIImageView subclass for the front view (that receives the touches) and making sure setUserInteractionEnabled is YES. Result: UIButtons are not responding when the UIView is present.
b) Explicitly passing the touches from the front view to the view controller in hopes that it will do the right thing. Result: UIButtons are not responding to the touches.
c) Calling the touchesBegan, touchesMoved and touchesEnded methods directly on the button I want to interact with when any touch on the front UIView is received. Result: Strikingly, the buttons don't respond even when I am cramming the touch events down their throat.
d) Passing the touches from the front view to the UIViewController, and iterating over all subviews handing out the touches manually. I included a touchesBegan method showing how I tried to do this. When executed the loop is infinite, it never progresses past the first view (is of type FrameUIView).
I know this problem is likely the result of my lack of understanding of proper view hierarchy or the responder chain.
if I comment the last view created after the button, then the buttons work fine. Is there something special about UIButtons in this respect?
Here is the code.
I have a UIViewController subclass, in it:
- (void)loadView {
mainAppView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view = mainAppView;
frameImageView = [[FrameUIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
frameImageView.image = [UIImage imageNamed:[[[ThemeController sharedInstance] currentTheme] frameImageFilename]];
[frameImageView setUserInteractionEnabled:YES];
[mainAppView addSubview:frameImageView];
zoomInButton = [[CustomUIButton alloc] initWithFrame:CGRectMake(controlsOriginX + zoomWidth + lightWidth, controlsOriginY, zoomWidth, controlsHeight)];
[zoomInButton setTitle:#"+" forState:UIControlStateNormal];
[[zoomInButton titleLabel] setFont:[UIFont systemFontOfSize:28]];
[zoomInButton addTarget:self action:#selector(doMyAction) forControlEvents:UIControlEventTouchDown];
[zoomInButton addTarget:self action:#selector(cancelMyAction) forControlEvents:UIControlEventTouchUpInside];
[mainAppView addSubview:zoomInButton];
FrameFrontUIView *frameFrontImageView = [[FrameFrontUIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
frameFrontImageView.image = [UIImage imageNamed:[[[ThemeController sharedInstance] currentTheme] frameFrontImageFilename]];
[frameFrontImageView setUserInteractionEnabled:YES];
[mainAppView addSubview:frameFrontImageView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UIView *view in self.view.subviews) {
NSLog(#"view is a %#", [view description]);
[view touchesBegan:touches withEvent:event];
}
}
Anyone with a tip on how to do this wins the internets and a cookie. Also accepting revolvers for russian roulette mayhem. Thank you.
Have you tried using the buttons without the image view behind them? Subclassing UIButton is not advisable. The way to instantiate UIButtons is with factory methods:
UIButton* myButton = [UIButton buttonWithType:UIButtonTypeCustom];
So my guess is that the CustomUIButton instances you have created are not properly set up.
This question is old, but I just ran into the same problem.
The solution I used is to create a container UIView, then add the UIImageView as a subview, then add the buttons, text boxes, etc. over that. Seems to work well.
I don't really understand why this is an issue with using a UIImageView as the superview for buttons.

Overriding -handlePan: in UIScrollView

Is it ok to override -handlePan: in a UIScrollView subclass?
i.e. my app won't get rejected from the app store?
Thanks for sharing your views.
Edit: what about calling -handlePan: in another method of my subclass?
In case anyone is interested, what I did instead of overriding was disabling the default UIPanGestureRecognizer and adding another instance of UIPanGestureRecognizer which is mapped to my custom handler.
Edit for twerdster:
I did it like this
//disables the built-in pan gesture
for (UIGestureRecognizer *gesture in scrollView.gestureRecognizers){
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]){
gesture.enabled = NO;
}
}
//add your own
UIPanGestureRecognizer *myPan = [[UIPanGestureRecognizer alloc] init...];
//customize myPan here
[scrollView addGestureRecognizer:myPan];
[myPan release];
You can make the code even shorter.
//disables the built-in pan gesture
scrollView.panGestureRecognizer.enabled = NO;
//add your own
UIPanGestureRecognizer *myPan = [[UIPanGestureRecognizer alloc] init...];
[scrollView addGestureRecognizer:myPan];
[myPan release];