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
Related
I have 2 UIButtons that each one changes the number in the middle (please see the picture).
I want that when the user holds the button the numbers will still be changing (now, when you want to change from 100 to 200 you need to tap 100 times and it's not good, I want that when you hold the button the numbers will be changing), that probably means that each UIButon's action should be called again and again until the user releases the button.
How can I do that? Thanks!
Here's the code I have written that matches your requirements. Make sure to link the plus and minus button outlets and its target events(touch up inside) in your storyboard. If it's not clear just let me know.
#import "ViewController.h"
#interface ViewController () {
NSTimer *speedTimer;
}
//Link this in your storyboard Outlets
#property (weak, nonatomic) IBOutlet UILabel *speedUILabel;
#property (weak, nonatomic) IBOutlet UIButton *plusUIButton;
#property (weak, nonatomic) IBOutlet UIButton *minusUIButton;
//Link this in your storyboard events respectively
- (IBAction)plusUIButton:(id)sender;
- (IBAction)minusUIButton:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UILongPressGestureRecognizer *plusButtonPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
UILongPressGestureRecognizer *minusButtonPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
[_plusUIButton addGestureRecognizer:plusButtonPress];
[_minusUIButton addGestureRecognizer:minusButtonPress];
plusButtonPress.delegate = self;
minusButtonPress.delegate = self;
plusButtonPress.minimumPressDuration = 0.5;
minusButtonPress.minimumPressDuration = 0.5;
plusButtonPress.allowableMovement = 0.5;
minusButtonPress.allowableMovement = 0.5;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)plusUIButton:(id)sender {
NSInteger plus = [_speedUILabel.text integerValue] + 1;
if (plus >= 0) {
_speedUILabel.text = [NSString stringWithFormat:#"%ld", (long)plus];
}
}
- (IBAction)minusUIButton:(id)sender {
NSInteger minus = [_speedUILabel.text integerValue] - 1;
if (minus >= 0) {
_speedUILabel.text = [NSString stringWithFormat:#"%ld", (long)minus];
}
}
- (void) longPressHandler: (UILongPressGestureRecognizer *) gesture {
if (gesture.view == _plusUIButton) {
if(gesture.state == UIGestureRecognizerStateBegan) {
speedTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(plusUIButton:) userInfo:nil repeats:true];
}
else if(gesture.state == UIGestureRecognizerStateEnded) {
[speedTimer invalidate];
speedTimer = nil;
}
}
else if (gesture.view == _minusUIButton) {
if(gesture.state == UIGestureRecognizerStateBegan) {
speedTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(minusUIButton:) userInfo:nil repeats:true];
}
else if(gesture.state == UIGestureRecognizerStateEnded) {
[speedTimer invalidate];
speedTimer = nil;
}
}
}
#end
You have to use the UILongPressGestureRecognizer which is a continuous calling action instead of calling methods with button tap. So just put the left/Right buttons in separate views and add UILongPressGestureRecognizer to these left/Right views and add target to these recognizers e.g
UILongPressGestureRecognizer *leftLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(decreaseNum:)];
UILongPressGestureRecognizer *rightLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(increaseNum:)];
[leftView addGestureRecognizer:leftLongPress];
[rightView addGestureRecognizer:rightLongPress];
Hope this will resolve your problem .
Use UILongPressGestureRecognizer on both UIButton
UILongPressGestureRecognizer *LeftButtonPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
UILongPressGestureRecognizer *RightButtonPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
[btnLeft addGestureRecognizer:LeftButtonPress];
[btnRight addGestureRecognizer:RightButtonPress];
code on state change for update screen.
- (void) longPressHandler: (UILongPressGestureRecognizer *) gesture {
if(gesture.state == UIGestureRecognizerStateChanged) {
// update number on screen
}
}
You can do like this in Swift:
#IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.Long)) //Tap function will call when user tap on button
let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.Long)) //Long function will call when user long press on button.
tapGesture.locationInView(btn);
btn.addGestureRecognizer(tapGesture) // To set the tap gesture recognizer: the function Tap () will be calling when the user select the button normally.
btn.addGestureRecognizer(longGesture) // To set the long gesture recognizer: the function Long () will be calling when the user select the button with a long press.
}
func Tap() {
print("Tap happend")
}
func Long() {
print("Long press")
}
I have a chatting app that has a uiswitch. If the Switch button is on, I want the app to send "hi" continuously every 3 seconds even the app is in background mode. I know that I can use NSTimer, but I don't really know how to implement that in this code(This is my first time to develop iOS app). Please help me with this.
My code is :
// Allocate, initialize, and add the automatic button.
_AutomaticSend = [[UISwitch alloc] initWithFrame:CGRectMake([self width] - 50.0, textAreaY + Center(160.0, textAreaHeight), 50.0, 50.0)];
[_AutomaticSend addTarget:self action:#selector(changeSwitch:) forControlEvents:UIControlEventValueChanged];
and
// Switch action
//NSUInteger counter = 0;
- (void)changeSwitch:(id)sender{
if([sender isOn]){
for (int a=1; a<=300; a++)
{
//[self performSelector:#selector(changeSwitch:) withObject:_textField afterDelay:70.0];
[sender setOn:YES animated:YES];
[_textField setText:#"hi"];
NSString * text = [_textField text];
// Update status.
[[TSNAppContext singleton] updateStatus:text];
// Add the status to the bubble.
[self appendLocalPeerTableViewCellWithMessage:text];
}
// NSLog(#"Switch is ON");
} else{
NSLog(#"Switch is OFF");
}
}
Now, the app is showing all "hi" after all 300 "hi" is ready to show. But I want it to send it one by one continuously.
Define a NSTimer instance and a counter in your view controller:
#property (nonatomic, strong) NSTimer *timer;
#property (nonatomic, assign) NSInteger counter;
Implement the method when timer is fired:
- (void)timerAction:(id)sender {
[_textField setText:#"hi"];
NSString * text = [_textField text];
// Update status.
[[TSNAppContext singleton] updateStatus:text];
// Add the status to the bubble.
[self appendLocalPeerTableViewCellWithMessage:text];
// stop the timer after sending for 300 times
self.counter += 1;
if (self.counter >= 300) {
[self.timer invalidate];
self.timer = nil;
}
}
Starts the timer when switch is on:
- (void)changeSwitch:(id)sender{
if ([sender isOn]) {
self.counter = 0;
self.timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
} else {
// kill the timer when switch is off
[self.timer invalidate];
self.timer = nil;
}
}
However, you can't make the timer to continue working indefinitely after your app is entering background (with some exceptions: VoIP, GPS applications, etc.). Please refer to the official document Background Execution.
//Nstimer *timer in .h
In your UISwitch Method Write like this
- (void)changeSwitch:(id)sender{
if([sender isOn]){
timer=[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(targetMethod)
userInfo:nil
repeats:NO];
}
else
{
[timer invalidate];
}
}
targetMethod Like this
-(void)targetMethod
{
[sender setOn:YES animated:YES];
[_textField setText:#"hi"];
NSString * text = [_textField text];
// Update status.
[[TSNAppContext singleton] updateStatus:text];
// Add the status to the bubble.
[self appendLocalPeerTableViewCellWithMessage:text];
}
Use NSTimer: Documentation of NSTimer
Declare property of NSTimer in .h file:
#property (retain,nonatomic) NSTimer *myTimer;
- (void)changeSwitch:(id)sender
{
if([sender isOn]){
myTimer = [NSTimer scheduledTimerWithTimeInterval: 4 target: self
selector: #selector(AddMessage:) userInfo: nil repeats: YES];
} else{
[self.timer invalidate];
}
}
After each 4 second, timer will call the below function:
-(void) AddMessage:(NSTimer*) timer
{
//Add Message
}
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
I am trying to implement method that affect UILabel text each every 1 seconds , It is going to be a reminder text like " You can skip ad in 5,4,3,2,1 skip now >>"
My method works fine except in 2nd and 3rd seconds timer call method like a more than 1 times each on second. I am using UIApplication sharedapplication.keywindows for present UI over full screen MPMoviePlayerController . Here is my method :
-(void)closeButtonForVideoPlayer
{
myButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width-30, 12, 25, 25)];
window = [UIApplication sharedApplication].keyWindow;
if (!window)
{
window = [[UIApplication sharedApplication].windows objectAtIndex:0];
}
[myButton addTarget:self action:#selector(videoFinished:) forControlEvents:UIControlEventTouchUpInside];
[myButton setImage:self.closeButton forState:UIControlStateNormal];
labelReminder = [[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width-130, 12, 130, 35)];
labelReminder.textColor = [UIColor whiteColor];
labelReminder.backgroundColor = [UIColor blackColor];
self.timderReminder = [NSTimer scheduledTimerWithTimeInterval:1.0
target: self
selector:#selector(reminderForVideoPlayer:)
userInfo: #{#"boolType":#"YES"} repeats:NO];
}
The reminder method :
-(void)reminderForVideoPlayer:(NSTimer *)userInfo
{
NSString *isVideoAdvertisement = [userInfo.userInfo objectForKey:#"boolType"];
NSLog(#"NSTimer info = %#",userInfo.userInfo);
if([isVideoAdvertisement isEqualToString:#"YES"])
{
if(remind >= 5)
{
[self.timderReminder invalidate];
remind = 0;
NSLog(#"reklam geçildi window");
[labelReminder removeFromSuperview];
labelReminder = nil;
[labelReminder setHidden:YES];
[window addSubview:myButton];
} else {
[window addSubview:labelReminder];
[labelReminder setText:[NSString stringWithFormat:#"Reklami geç: %d",5-remind]];
NSLog(#"reminder window = %d",remind);
remind ++;
self.timderReminder = [NSTimer scheduledTimerWithTimeInterval:1.0
target: self
selector:#selector(reminderForVideoPlayer:)
userInfo: #{#"boolType":#"YES"} repeats:NO];
}
}
Which part i am making mistake ? and how i can fix that issue ?
Best Regards,
Onder OZCAN
Try this control it will fulfil your requirements
https://github.com/mineschan/MZTimerLabel
in viewDidLoad
self.timderReminder = [NSTimer scheduledTimerWithTimeInterval:1.0
target: self
selector:#selector(reminderForVideoPlayer:)
userInfo: #{#"boolType":#"YES"} repeats:YES];
and
-(void)reminderForVideoPlayer:(NSTimer *)userInfo
{
NSString *isVideoAdvertisement = [userInfo.userInfo objectForKey:#"boolType"];
NSLog(#"NSTimer info = %#",userInfo.userInfo);
if([isVideoAdvertisement isEqualToString:#"YES"])
{
if(remind < 5)
{
[labelReminder setText:[NSString stringWithFormat:#"Reklami geç: %d",5-remind]];
NSLog(#"reminder window = %d",remind);
[labelReminder setHidden:NO];
} else {
[labelReminder setHidden:YES];
// [window addSubview:myButton]; //add this button in view did load only over here use setHidden method
[myButton setHidden: NO];
}
}
Try with this code.
Change the repeats parameter of NSTimer to YES. And you won't have to create a timer again each time it's fired. When your counter reaches to 5, just invalidate your timer.
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: