I am new at iPhone programming (Objective C). I wanted to make button which will be visible on long pressing on another button. Just like system keyboard style. I should choose first or second button by holding my finger on them :-) How can I do this, I could not find tutorials. Thanks
create and attach the UILongPressGestureRecognizer instance to your UIButton.
-(IBAction)SelectImage:(id)sender
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressMethod:)];
[self.button addGestureRecognizer:longPress];
}
Then create the method that handles the gesture
- (void)longPressMethod:(UILongPressGestureRecognizer*)gesture
{
if ( gesture.state == UIGestureRecognizerStateEnded )
{
NSLog(#"Long Press");
//Do your code here what you want to perform
}
}
I'm trying to track hits on UI elements (tap and long press) using UIGestureRecognizer. After hit was tracked (let's say logged via NSLog) UI element should do it's job.
I'm creating gesture recognizers like this:
UITapGestureRecognizer* tap = [[UITapGestureRecognizer] alloc initWithTarget:self action:(OnGesture:)]
tap.cancelsTouchesInView = NO;
tap.delegate = self;
[view addGestureRecognizer:tap];
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer] alloc initWithTarget:self action:(OnGesture:)]
longPress.cancelsTouchesInView = NO;
longPress.delegate = self;
[view addGestureRecognizer:longPress];
I've overridden some gesture recognizer methods:
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)_recognizer shouldReceiveTouch(UITouch*)_touch
{
return YES;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)_recognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)_otherRecognizer
{
return YES;
}
Inside the gesture recognizer handler, I'm trying to find the exact subview of the tap by using the hitTest method.
-(void)OnGesture:(UIGestureRecognizer*)_recognizer
{
if([_recognizer.state == UIGestureRecognizerStateEnded])
{
if([_recognizer isKindOfClass:[UITapGestureRecognizer class]]
|| [_recognizer isKindOfClass:[UILongPressGestureRecognizer class])
{
CGPoint location = [_recognizer locationOfTouch:0 inView:_recognizer.view];
// my problem occurs here:
//---------------------------------------------------------------------------
UIView* hitView = [_recognizer.view hitTest:location withEvent:nil];
//---------------------------------------------------------------------------
NSLog(#"Hit on view: %#", hitView);
}
}
}
So my problem is:
Sometimes (1 out of 10 cases) when I press the UIButton OnGesture method fires, but the IBAction of the "Touch Up Inside" event of that button is not firing.
But when I comment out hitTest call:
//UIView* hitView = [_recognizer.view hitTest:location withEvent:nil];
the bug stops being reproducible. IBAction always gets called.
Why is this happening? How can I fix this?
P.S. there could be some typos in the sample code above.
According to the docs, in order for it to work:
This method ignores view objects that are hidden, that have disabled user interactions, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content.
So you might wanna do self.someSubview.userInteractionEnabled = YES;
I would like to know how can I send a swipe gesture programmatically without actually swiping the phone. For instance, I have button that in the onClick event I would call swipeGestureRecognizer? Is this possible?
You can call the method you are calling on Swipe, when user taps on button. For examaple, you have a method for swipe gesture, call it onSwipe . Now call onSwipe methos when user taps on the button. Thats it
EDIT
Here is the code for you:
-(void)myMethod{
//Write the logic you want to implement for swipe gesture here.
}
-(IBAction)onClick(UIButton *)sender{
[self myMethod];
}
-(IBAction)onSwipe:(UISwipeGestureRecognizer *)recognizer{
[self myMethod];
}
There might be bugs on the code as I am just typing the code using windows. Correct it for yourself in MAC & edit the answer too. it would definitely work for you
If you need to pass the gesture event to the handler, then
UISwipeGestureRecognizer *gesture = [[UISwipeGestureRecognizer alloc] init];
gesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self handleSwipe:gesture];
UITapGestureRecognizer *PressRecognizer1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handlePress:)];
[PressRecognizer1 setNumberOfTouchesRequired:1];
[firstBtn addGestureRecognizer:PressRecognizer1];
-(void)handlePress:(UITapGestureRecognizer*)PressRecognizer {
NSLog(#"working");
if (PressRecognizer.state == UIGestureRecognizerStateBegan) {
UIButton *whichButton=(UIButton *)[PressRecognizer view];
NSLog(#"whichButton %d\n",whichButton.tag);
if (whichButton.tag == 0) {
NSLog(#"currentImageId1 %d",currentImageId1);
[delegate imageZoom:currentImageId1];
}
I have created a UITabelView with customcell which has three images per row.When tap on a image hadlePress method called.But i does not come inside first if condition.
from apple docs:
Although taps are discrete gestures, they are discrete for each state of the gesture recognizer; thus the associated action message is sent when the gesture begins and is sent for each intermediate state until (and including) the ending state of the gesture. Code that handles tap gestures should therefore test for the state of the gesture, for example:
- (void)handleTap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded)
{
// handling code
}
}
if you NSLog(#"%d",sender.state) inside the method (before the if statement) you'll see that the method is getting fired only with the UIGestureRecognizerStateEnded state, thus you should change your
if (PressRecognizer.state == UIGestureRecognizerStateBegan)
to
if (PressRecognizer.state == UIGestureRecognizerStateEnded)
I want to emulate a long a press button, how can I do this? I think a timer is needed.
I see UILongPressGestureRecognizer but how can I utilize this type?
You can start off by creating and attaching the UILongPressGestureRecognizer instance to the button.
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[self.button addGestureRecognizer:longPress];
[longPress release];
And then implement the method that handles the gesture
- (void)longPress:(UILongPressGestureRecognizer*)gesture {
if ( gesture.state == UIGestureRecognizerStateEnded ) {
NSLog(#"Long Press");
}
}
Now this would be the basic approach. You can also set the minimum duration of the press and how much error is tolerable. And also note that the method is called few times if you after recognizing the gesture so if you want to do something at the end of it, you will have to check its state and handle it.
As an alternative to the accepted answer, this can be done very easily in Xcode using Interface Builder.
Just drag a Long Press Gesture Recognizer from the Object Library and drop it on top of the button where you want the long press action.
Next, connect an Action from the Long Press Gesture Recognizer just added, to your view controller, selecting the sender to be of type UILongPressGestureRecognizer. In the code of that IBAction use this, which is very similar to the code suggested in the accepted answer:
In Objective-C:
if ( sender.state == UIGestureRecognizerStateEnded ) {
// Do your stuff here
}
Or in Swift:
if sender.state == .Ended {
// Do your stuff here
}
But I have to admit that after trying it, I prefer the suggestion made by #shengbinmeng as a comment to the accepted answer, which was to use:
In Objective-C:
if ( sender.state == UIGestureRecognizerStateBegan ) {
// Do your stuff here
}
Or in Swift:
if sender.state == .Began {
// Do your stuff here
}
The difference is that with Ended, you see the effect of the long press when you lift your finger. With Began, you see the effect of the long press as soon as the long press is caught by the system, even before you lift the finger off the screen.
Swift version of the accepted answer
I made the additional modification of using UIGestureRecognizerState.Began rather than .Ended since that is probably what most users would naturally expect. Try them both and see for yourself, though.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// add gesture recognizer
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPress(_:)))
self.button.addGestureRecognizer(longPress)
}
func longPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
#IBAction func normalButtonTap(sender: UIButton) {
print("Button tapped")
}
}
Try this:
Adding button in viewDidLoad: like below
-(void)viewDidLoad {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setTag:1]; //you can set any integer value as tag number
btn.title = #"Press Me";
[btn setFrame:CGRectMake(50.0, 50.0, 60.0, 60.0)];
// now create a long press gesture
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPressTap:)];
[btn addGestureRecognizer:longPress];
}
Now call the gesture method like this
-(void)longPressTap:(id)sender {
UIGestureRecognizer *recognizer = (UIGestureRecognizer*) sender
// Recogniser have all property of button on which you have clicked
// Now you can compare button's tag with recogniser's view.tag
// View frame for getting the info on which button the click event happened
// Then compare tag like this
if(recognizer.view.tag == 1) {
// Put your button's click code here
}
// And you can also compare the frame of your button with recogniser's view
CGRect btnRect = CGRectMake(50.0, 50.0, 60.0, 60.0);
if(recogniser.view.frame == btnRect) {
//put your button's click code here
}
// Remember frame comparing is alternative method you don't need to write frame comparing code if you are matching the tag number of button
}
I think you need my solution.
you should have this code for single press
- (IBAction)buttonDidPress:(id)sender {
NSLog("buttonDidPress");
}
first, add long press gesture to button
- (void)viewWillAppear:(BOOL)animated
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(buttonDidLongPress:)];
[self.button addGestureRecognizer:longPress];
}
then call single press event repeatedly if long press gesture is recognized.
- (void)buttonDidLongPress:(UILongPressGestureRecognizer*)gesture
{
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
{
self.timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:#selector(buttonDidPress:) userInfo:nil repeats:YES];
NSRunLoop * theRunLoop = [NSRunLoop currentRunLoop];
[theRunLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
break;
case UIGestureRecognizerStateEnded:
{
[self.timer invalidate];
self.timer = nil;
}
break;
default:
break;
}
}
For Swift 4, the "func longPress" needs to be changed to make it work:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// add guesture recognizer
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPress(_:)))
self.button.addGestureRecognizer(longPress)
}
#objc func longPress(_ guesture: UILongPressGestureRecognizer) {
if guesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
#IBAction func normalButtonTap(sender: UIButton) {
print("Button tapped")
}
}
One-line answer, with no gestures:
[btn addTarget:self action:#selector(handleTouch:) forControlEvents:UIControlEventTouchDown | UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
Details:
This triggers your target on three events:
1- Immediately once finger touches down the button: UIControlEventTouchDown. This captures long presses start.
2 & 3- When user lifts finger up: UIControlEventTouchUpOutside & UIControlEventTouchUpInside. This captures end of the user press.
Note: this works well if you don't care about the extra info provided by the gesture recognizer (e.g. location of touch, etc.)
You can add more intermediate events if needed see them all here https://developer.apple.com/documentation/uikit/uicontrolevents?language=objc.
In Storyboard:
Connect your button to the 3 events, not just the default one that Storyboard selects (Touch Up Inside).
I have a subclassed UIButton for my app, so I've pulled out my implementation. You can add this to your subclass or this could just as easily be recoded as a UIButton category.
My goal was to add the long press to my button without cluttering my view controllers with all of the code. I've decided that the action should be called when the gesture recognizer state begins.
There is a warning that comes out that I've never bothered to solve. Says it is a possible leak, thought I've tested the code and it doesn't leak.
#interface MYLongButton ()
#property (nonatomic, strong) UILongPressGestureRecognizer *gestureRecognizer;
#property (nonatomic, strong) id gestureRecognizerTarget;
#property (nonatomic, assign) SEL gestureRecognizerSelector;
#end
#implementation MYLongButton
- (void)addLongPressTarget:(CGFloat)interval target:(id)target action:(SEL)selector
{
_gestureRecognizerTarget = target;
_gestureRecognizerSelector = selector;
_gestureRecognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPressGestureRecognizer:)];
_gestureRecognizer.minimumPressDuration = interval;
[self addGestureRecognizer:_gestureRecognizer];
}
- (void)handleLongPressGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
NSAssert([_gestureRecognizerTarget respondsToSelector:_gestureRecognizerSelector], #"target does not respond to selector");
self.highlighted = NO;
// warning on possible leak -- can anybody fix it?
[_gestureRecognizerTarget performSelector:_gestureRecognizerSelector withObject:self];
}
}
To assign the action add this line to your viewDidLoad method.
[_myLongButton addLongPressTarget:0.75 target:self selector:#selector(longPressAction:)];
The action should be defined like all IBActions (without the IBAction).
- (void)longPressAction:(id)sender {
// sender is the button
}
None worked hence I tried writing longpress code in IBAction or button click from storyboard in Controller instead of writing in viewDidLoad
- (IBAction)btnClick:(id)sender {
tag = (int)((UIButton *)sender).tag;
// Long press here instead of in viewDidLoad
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
longPress.cancelsTouchesInView = NO;
[sender addGestureRecognizer:longPress];
}