UILongPressGestureRecognizer iOS for continuous action - ios

I have a button, and I want to "press down and hold" the button so it will keep printing "Long Press" until I release the key press.
I have this in the ViewDidLoad:
[self.btn addTarget:self action:#selector(longPress:) forControlEvents:UIControlEventTouchDown];
and
- (void)longPress: (UILongPressGestureRecognizer *)sender {
if (sender.state == UIControlEventTouchDown) {
NSLog(#"Long Press!");
}
}
I have also tried this:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPress:)];
lpgr.minimumPressDuration = 0.1;
lpgr.numberOfTouchesRequired = 1;
[self.btn addGestureRecognizer:lpgr];
It only prints out Long Press! once even when I hold down the button.
Can anyone tell me where I did wrong or what I missed?
Thanks!

First, UIButton's target-action pair callback only execute once when correspond events fire.
The UILongPressGestureRecognizer need to a minimumPressDuration to get into UIGestureRecognizerStateBegan state, then when finger move, callback will fire with UIGestureRecognizerStateChanged state. At last, UIGestureRecognizerStateEnded state when release finger.
Your requirement is repeatedly fire when button pressed down. None of the top class satisfy your need. Instead, you need to set a repeat fire timer when button press down, and release it when button release up.
so the code should be:
[btn addTarget:self action:#selector(touchBegin:) forControlEvents: UIControlEventTouchDown];
[btn addTarget:self action:#selector(touchEnd:) forControlEvents:
UIControlEventTouchUpInside | UIControlEventTouchUpOutside | UIControlEventTouchCancel];
- (void)touchBegin:(UIButton*)sender {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(longPress:) userInfo:nil repeats:YES];
}
- (void)touchEnd:(UIButton*)sender {
[_timer invalidate];
_timer = nil;
}
- (void)longPress:(NSTimer*)timer {
NSLog(#"Long Press!");
}
By the way, neither UIButton nor UILongPressGestureRecognizer have a state with type UIControlEvents

Related

Two method for UIButton?

We need to set a UIButton with 2 methods, the first one is when you touch it (down and up) you get one action , but when you long press it, you get another one.
For example, to get data about it when you long click, and when regular click , another thing.
How can i achieve this with UIButton ?
UIButton *CAT = [UIButton buttonWithType:UIButtonTypeCustom];
CAT.contentHorizontalAlignment=UIControlContentHorizontalAlignmentCenter;
CAT.backgroundColor=[UIColor clearColor];
[CAT addTarget:self action:#selector(newcat:)forControlEvents:UIControlEventTouchUpInside];
I have started with adding it a gesture with this
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[CAT addGestureRecognizer:longPress];
But this triggered only when you take off your finger .
i want it to be triggered while my finger is still there after 1-2 seconds .
Can i do that? and can i adjust the time required for it to trigger ?
You can use the event UIControlEventTouchDown, use delay time for long press and you must deal with UIControlEventTouchUpInside and UIControlEventTouchUpOutside. good luck!
this is normal method
UIButton *CAT = [UIButton buttonWithType:UIButtonTypeCustom];
CAT.contentHorizontalAlignment=UIControlContentHorizontalAlignmentCenter;
CAT.backgroundColor=[UIColor clearColor];
[CAT addTarget:self action:#selector(newcat:)forControlEvents:UIControlEventTouchUpInside];
UILongPressGestureRecognizer *longpressGesture =
[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(longPressHandler:)];
longpressGesture.minimumPressDuration = 5; // set the time interval
[longpressGesture setDelegate:self];
[CAT addGestureRecognizer:longpressGesture];
[self.view addsubview: CAT];
longpress action method
- (void)longPressHandler:(UILongPressGestureRecognizer *)gestureRecognizer {
NSLog(#"longPressHandler");
// do long press action
}
normal action method
-(void) newcat:(id)sender
{
// do normal action here
}

ios 8 custom keyboard hold button to delete?

I am currently building a custom keyboard and I am almost done. One problem that I have is with the delete button. When the user taps the delete button, it does what it should do and deletes the previous text entry. However when the user holds the button down, nothing happens. How do I make it so that when the user holds down the delete button, the keyboard continuously deletes like in the standard ios keyboard? This is my current code:
pragma mark Keyboards
- (void)addGesturesToKeyboard{
[self.keyboard.deleteKey addTarget:self action:#selector(pressDeleteKey)forControlEvents:UIControlEventTouchUpInside];
and:
-(void)pressDeleteKey{
[self.textDocumentProxy deleteBackward];
}
Thanks for your help.
Swift 3 Use "allowableMovement" property
override func viewDidLoad() {
super.viewDidLoad()
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(KeyboardViewController.handleLongPress(_:)))
longPress.minimumPressDuration = 0.5
longPress.numberOfTouchesRequired = 1
longPress.allowableMovement = 0.1
buttonDelete.addGestureRecognizer(longPress)
}
func handleLongPress(_ gestureRecognizer: UIGestureRecognizer) {
textDocumentProxy.deleteBackward()
}
you can do this by managing button’s events like touchdown, touchupinside and touchoutside.
When button press at that time start timer with delay of 0.2 seconds and delete last characters from textDocumentProxy until button’s touchup method will fire and after that you just need to invalidate timer.
[self.btnDelete addTarget:self action:#selector(btnTocuhDown:) forControlEvents:UIControlEventTouchDown];
[self.btnDelete addTarget:self action:#selector(btnTouchUp:) forControlEvents:UIControlEventTouchUpInside];
[self.btnDelete addTarget:self action:#selector(btnTouchUp:) forControlEvents:UIControlEventTouchUpOutside];
-(void) btnTocuhDown
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(kpTimerMethod:) userInfo:nil repeats:YES];
self.kpTimer = timer;
__weak typeof(self)weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
if (timer == self.kpTimer) {
[weakSelf.kpTimer fire];
}
});
-(void)kpTimerMethod:(NSTimer *)timer
if (self.btnDelete.highlighted)
{
[self deleteLastCharacter];
}
else
{
[timer invalidate];
self.kpTimer = nil;
}
-(void)deleteLastCharacter
NSString *strInput = self.textDocumentProxy.documentContextBeforeInput;
if (strInput.length > 1)
NSString *coupleOfLastCharacters = [strInput substringWithRange:NSMakeRange(strInput.length-2, 2)];
if( [#"yo" caseInsensitiveCompare:coupleOfLastCharacters] == NSOrderedSame ) {
[self.textDocumentProxy deleteLastCharacter];
}
}
[self.textDocumentProxy deleteLastCharacter];
-(void) btnTouchUp
[self.kpTimer invalidate];
self.kpTimer = nil;
- (void)addGesturesToKeyboard{
UILongPressGestureRecognizer *ges = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
ges.minimumPressDuration = 0.1;
ges.numberOfTouchesRequired = 1;
ges.delegate = self;
[self.mykeyboard.deleteKey addGestureRecognizer:ges];
}
- (void)longPress:(UILongPressGestureRecognizer*)gesture {
[self.textDocumentProxy deleteBackward];
}
Set a counter as soon as the screen is touched, such as 2-5 seconds.
The situation known as Long press gesture, and here is the link to the simliar questions.
Long press gesture on UICollectionViewCell

How to preform an action while the button is pressed?

I have a button and i need to change the label while the button is pressed to #"PRESSED" and if it's let go I need to change it to #"LET GO".
I've found many answers, but i have no idea how to implement them in Xcode 5.
That are the ones i tried:
You can start your action on the touchDownInside and stop it on the touchUpInside actions - you can hook them up in IB.
or
Add targets to your button for both control states, UIControlEventTouchDown and UIControlEventTouchUpInside or UIControlEventTouchUpOutside
or even
Add a dispatch source iVar to your controller...
dispatch_source_t _timer;
Then, in your touchDown action, create the timer that fires every so many seconds. You will do your repeating work in there.
If all your work happens in UI, then set queue to be
dispatch_queue_t queue = dispatch_get_main_queue();
and then the timer will run on the main thread.
- (IBAction)touchDown:(id)sender {
if (!_timer) {
dispatch_queue_t queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// This is the number of seconds between each firing of the timer
float timeoutInSeconds = 0.25;
dispatch_source_set_timer(
_timer,
dispatch_time(DISPATCH_TIME_NOW, timeoutInSeconds * NSEC_PER_SEC),
timeoutInSeconds * NSEC_PER_SEC,
0.10 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
// ***** LOOK HERE *****
// This block will execute every time the timer fires.
// Do any non-UI related work here so as not to block the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Do UI work on main thread
NSLog(#"Look, Mom, I'm doing some work");
});
});
}
dispatch_resume(_timer);
}
Now, make sure to register for both touch-up-inside and touch-up-outside
- (IBAction)touchUp:(id)sender {
if (_timer) {
dispatch_suspend(_timer);
}
}
Make sure you destroy the timer
- (void)dealloc {
if (_timer) {
dispatch_source_cancel(_timer);
dispatch_release(_timer);
_timer = NULL;
}
}
I am new to iOS and have no idea whatsoever how to do any of that! Thanks a lot for any help in advance!
I would do this with a GestureRecognzier. Here is some code:
- (void)viewDidLoad
{
[super viewDidLoad];
UILongPressGestureRecognizer *gr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(buttonIsPressed:)];
gr.minimumPressDuration = 0.1;
[self.button addGestureRecognizer:gr];
}
-(IBAction)buttonIsPressed:(UILongPressGestureRecognizer*)gesture {
if (gesture.state == UIGestureRecognizerStateBegan) {
self.label.text = #"Button is pressed!";
}
else if (gesture.state == UIGestureRecognizerStateEnded) {
self.label.text = #"Button is not pressed";
}
}
You need an IBOutlet for the label and the button.
The first method does what you want, here's an example implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2.0 - 50.0, self.view.bounds.size.height / 2.0 - 25.0, 100.0, 50.0)];
myButton.backgroundColor = [UIColor colorWithRed:0.2 green:0.3 blue:0.8 alpha:1.0];
[myButton setTitle:#"PRESS ME" forState:UIControlStateNormal];
[myButton addTarget:self action:#selector(setPressed:) forControlEvents:UIControlEventTouchDown];
[myButton addTarget:self action:#selector(setLetGo:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myButton];
}
-(void)setPressed:(id)sender
{
UIButton *myButton = (UIButton *)sender;
[myButton setTitle:#"PRESSED" forState:UIControlStateNormal];
}
-(void)setLetGo:(id)sender
{
UIButton *myButton = (UIButton *)sender;
[myButton setTitle:#"LET GO" forState:UIControlStateNormal];
}
If you are new to xCode this can be difficult to understand. Follow this and you'll be allright.
Go to Storyboard and put a button in the view now go to the Inspector and change State config from Default to Highlighted
In placeholder 'highlighted title' put 'Pressed'
Now click on the Assistant Editor to open the .m file next to Storyboard
CNTRL + Click and drag from the Button to the .m file code to create a TouchUpInside event, mine called buttonPressed
Put this code in the method body and you are good to go!
This is what it will look like, pressed and released:

How do you move a UIImage when you hold a button?

I declared 2 IBActions "rightButtonPress" and "leftButtonPress" I linked them to 2 IBOutlets and ran a block of code in the IBactions:
-(void)leftButtonPress:(id)sender{
PlayerSprite.center = CGPointMake(PlayerSprite.center.x - 1, PlayerSprite.center.y);
}
-(void)rightButtonPress:(id)sender{
PlayerSprite.center = CGPointMake(PlayerSprite.center.x + 1, PlayerSprite.center.y);
}
and i also created 2 timers:
- (void)viewDidLoad
{
TouchLeftCheck = [NSTimer scheduledTimerWithTimeInterval:.01
target:self
selector:#selector(leftButtonPress:)
userInfo:nil
repeats:YES];
TouchRightCheck = [NSTimer scheduledTimerWithTimeInterval:.01
target:self
selector:#selector(rightButtonPress:)
userInfo:nil
repeats:YES];
BarrelEntrance = [NSTimer scheduledTimerWithTimeInterval:.01
target:self
selector:#selector(barrelStartDown)
userInfo:nil
repeats:YES];
[self frontBarrelAnimation];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
it all works well but when i linked it up to two IBObject Buttons something didn't work, when i linked my IBActions to my Objects(buttons), with "Touch Down" it didn't work. When i was in the ios simulator it only moved my object i time when i held and then it stopped, if you touch it again then the object moves one more time. But is there a link option that will allow me to make it do a smooth animation across the screen when i hold it down, and then stop when i let go? Thanks.
There are three components here:
A timer for animation
The start-touch event
The end-touch event
I recommend using an NSTimer to animate the view while the button is being pressed. This timer will be initialized and started when the button is pressed (touchDown:) and invalidated/nullified when the button is released (touchUpInside: and touchUpOutside:).
So to start off, bind the touch events to the button. (I assume that you are creating these buttons in interface builder, which is fine. If so, you can skip the initialization and just go straight to the addTarget: methods):
UIButton* leftButton = [[UIButton alloc] initWithFrame:...];
[leftButton addTarget:self action:#selector(startLeftAnimation) forControlEvents:UIControlEventTouchDown];
[leftButton addTarget:self action:#selector(stopAnimation) forControlEvents:UIControlEventTouchUpInside];
[leftButton addTarget:self action:#selector(stopAnimation) forControlEvents:UIControlEventTouchUpOutside];
Next, declare an instance variable timer:
#interface MyViewController
{
NSTimer* animationTimer;
}
Create the methods for the button events:
- (void)startLeftAnimation
{
animationTimer = [NSTimer scheduledTimerWithInterval: 0.02 target:self selector:#selector(animateLeft) userInfo:nil repeats:YES];
}
- (void)stopAnimation
{
if(animationTimer != nil)
{
[animationTimer invalidate];
animationTimer = nil;
}
}
Finally, create the animation method
- (void)animateLeft
{
PlayerSprite.center = CGPointMake(PlayerSprite.center.x - 1, PlayerSprite.center.y);
}
Rinse and repeat for the right button animation.

Issues using touchesBegins

I have a button. I want to change the image on initial touch. If the touch is less then 1 seconds, I want it to do X. If the touch is longer then 1 second, I want it to do Y.
I'm having trouble figuring out how to handle this. A UIButton has proved troublesome so I thought I could do it with UIGestureRecognizers or touchesBegin:
Initial thought was have a UITapGestureRecognizer to detected just a quick tap to do X, and use a UILongTapGestureRecognizer to handle a longer press to do Y.
The issue is a UITapGestureRecognizer does not flag UIGestureRecognizerStateBegan, it only ever sends a notice for UIGestureRecognizerStateEnd.
So I decided trying a combination of overridding the touchesBegin: and touchesEnd: methods and using a UILongPressGestureRecognizer:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// change image
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// do X
// change image to original image
}
-(IBAction)longPressDetected:(UILongPressGestureRecognizer *)recognizer {
DLog(#"fired");
if (recognizer.state == UIGestureRecognizerStateBegan) {
// Do y
// change image to original image
}
else if (recognizer.state == UIGestureRecognizerStateCancelled) {
}
else if (recognizer.state == UIGestureRecognizerStateEnded) {
}
}
If the UILongPressGestureRecognzier fires, it cancels the inital touchesBegan: (does not fire touchesEnded: method).
But I have run into the issue where the touchesBegin: method is slow to fire. There is a .5 second delay in the method being fired. What baffles me, if I use UILongPressGestureRecognizer with a longTap.minimumPressDuration = 0, it fires instantly.
This is with in the program where I need it to be. Playing around with it in a dummy area, the touchesBegins: fires instantly as well.
What could cause it to lag within the program?
Is there a different way I can obtain the desired effect?
maybe you could use a timer and a Long Press Gesture Recognizer
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(didRecognizeLongPressGesture:)];
longPress.minimumPressDuration = 0;
[self.button addGestureRecognizer:longPress];
...
- (void)didRecognizeLongPressGesture:(UILongPressGestureRecognizer*)gesture
{
static NSTimer *timer = nil;
if (gesture.state == UIGestureRecognizerStateBegan)
{
timer = [NSTimer scheduledTimerWithTimeInterval:1.1 target:self selector:#selector(changeImage:) userInfo:nil repeats:NO];
//change the image here
}
else if ( gesture.state == UIGestureRecognizerStateEnded)
{
if ([timer isValid])
{
//the user has pressed the button less than a second
[timer invalidate];
timer = nil;
}
}
}
- (void)changeImage:(NSTimer*)timer
{
[timer invalidate];
timer = nil;
//change to the other image here
}

Resources