I am doing a countdown apps with storyboard and navigation controller with 2 view controller
"A" controller is with UILABEL showing how many days
and "B" Controller is a date picker with button below is the code from "B" controller
- (IBAction)doneSetting:(id)sender
{
//Remove the time component from the datePicker. We care only about the date
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSUInteger preservedComponents = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit);
self.datePicker.date = [calendar dateFromComponents:[calendar components:preservedComponents fromDate:self.datePicker.date]];
//Set up a timer that calls the updateTime method every second to update the label
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateTime)
userInfo:nil
repeats:YES];
}
-(void)updateTime
{
//Get the time left until the specified date
NSInteger ti = ((NSInteger)[self.datePicker.date timeIntervalSinceNow]);
NSInteger days = (ti / 86400);
//Update the lable with the remaining time
self.countdownLabel.text = [NSString stringWithFormat:#"%02i days", days];
}
how do I get the countdownlabel.text to appear on "A" Controller?
Thanks in advance
Try this:
//======= in controller B =====
Declare property in Controller "B" in .h file:
#property (nonatomic, copy) void (^LabelTextUpdated)(NSString *textValue);
and in .m of B in your source code:
-(void)updateTime
{
//Get the time left until the specified date
NSInteger ti = ((NSInteger)[self.datePicker.date timeIntervalSinceNow]);
NSInteger days = (ti / 86400);
//Update the lable with the remaining time
self.countdownLabel.text = [NSString stringWithFormat:#"%02i days", days];
if (_LabelTextUpdated) {
_LabelTextUpdated(#"Value");
}
}
//========== And in controller "A" ===========
from where you are pushing controller "B" write this:
B *object_B = YOUR_OBJECT_OF_B;
[object_B setLabelTextUpdated:^(NSString *textValue) {
//Here you will receive updated text
NSLog(#"Your Text is %#", textValue);
}];
//Push your B controller.
Related
I'm using ActionSheetDatePicker for time picker i change mode in picker as UIDatePickerModeCountDownTimer to take Hours and Minutes. It works well displayed Hours and min but when i getting Hrs and Min it return me NSDate init.
Ex. I selected 5 min to return me 300, so i get.
Here is code :
ActionSheetDatePicker *datePicker = [[ActionSheetDatePicker alloc] initWithTitle:#"Select a time" datePickerMode:UIDatePickerModeCountDownTimer selectedDate:self.selectedTime target:self action:#selector(ExtrTravelTimeWasSelected:element:) origin:sender];
datePicker.minuteInterval = minuteInterval;
[datePicker showActionSheetPicker];
and delegate
- (void)ExtrTravelTimeWasSelected:(NSDate *)selectedTime element:(id)element {
self.selectedTime = selectedTime;
}
So Is there any way to convert 300(NSDate) to Hr and Min ?
If I'm understanding correctly you're getting the value in seconds. try the following:
-(NSString *)getTimeStringFromSeconds:(double)seconds
{
NSDateComponentsFormatter *dcFormatter = [[NSDateComponentsFormatter alloc] init];
dcFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
dcFormatter.allowedUnits = NSCalendarUnitHour | NSCalendarUnitMinute;
dcFormatter.unitsStyle =
NSDateComponentsFormatterUnitsStylePositional;
return [dcFormatter stringFromTimeInterval:seconds];
}
Try this, I have updated your code,
- (void)ExtrTravelTimeWasSelected:(NSDate *)selectedTime element:(id)element {
self.selectedTime = selectedTime;
// Split up the date components
NSDateComponents *time = [[NSCalendar currentCalendar]
components:NSCalendarUnitHour | NSCalendarUnitMinute
fromDate:selectedTime];
NSLog(#"h:mm::%zd:%2zd",[time hour],[time minute]);
}
I have a button on which i have placed a UITapGestureRecognizer. When i tap on that button, i call a method that starts the time.
My question is, i am able to get the timer string on a label. But the timer starts from current date-time, and i want to start the timer always from zero(like a countdown timer). Here below is my code for timer.
-(IBAction)micPressed{
if (gestureRecognizer.state==UIGestureRecognizerStateBegan) {
gestureRecognizer.view.alpha=0.2f;
[label setHidden:NO];
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1f
target:self
selector:#selector(_timerFired:)
userInfo:nil
repeats:YES];
}
}
else{
[label setHidden:YES];
gestureRecognizer.view.alpha=10.2f;
if ([_timer isValid]) {
[_timer invalidate];
}
_timer = nil;
}
}
- (void)_timerFired:(NSTimer *)timer {
NSDateFormatter *dateformatter=[[NSDateFormatter alloc]init];
[dateformatter setDateFormat:#"mm:ss:SSS"];
NSString *dateInStringFormated=[dateformatter stringFromDate:[NSDate dateWithTimeIntervalSinceNow:0]];
NSLog(#"%#",dateInStringFormated);
[label setText:dateInStringFormated];
}
Please can anyone suggest me the solution. Any help is appreciated.
Your code should be like,
NSDateFormatter *dateformatter=[[NSDateFormatter alloc]init];
[dateformatter setDateFormat:#"mm:ss:SSS"];
NSString *dateInStringFormated=[dateformatter stringFromDate:[dateformatter dateFromString:#"00:00:000"]];
NSLog(#"test : %#",dateInStringFormated);
I have just chage [NSDate dateWithTimeIntervalSinceNow:0] with [dateformatter dateFromString:#"00:00:000"].
It will start with zero always.
Update :
I think you want something like timer that update minutes,seconds and milliseconds as i understand your question now. You can achieve it something like,
Schedule timer like,
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES];
Now your updateTimer should be like,
-(void)updateTimer{
static double counter = 0;
static int counter2 = 0;
int seconds = (int)counter % 60;
int minutes = ((int)counter / 60) % 60;
NSLog(#"%02d:%02d:%03d",minutes,seconds,counter2); // This should be your labels text that updating continuously
counter = counter + 0.01;
counter2++;
if (counter2 == 100) {
counter2 = 0;
}
}
Hope this will help :)
The problem is you are starting your timer from current time itself. To make a like countdown timer. You need to do the following:
Take an int named Counter and intialize it from 0
Fire your NSTimer and at that time increase the counter
Display the counter value in the UILabel text.
Once your NSTimer stops, again make it to 0.
That's it! This is how you can achieve the desired result.
Code:
int counter = 0;
- (void)_timerFired:(NSTimer *)timer {
counter++;
}
Once NSTimer is invalidate again make it to counter = 0
Remember the time when you have started the timer:
_startDate = [NSDate date];
_Timer = ...
Then use the time interval since the start date in your _timerFired method:
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
NSString *dateInStringFormatted = [formatter stringFromTimeInterval:[[NSDate date] timeIntervalSinceDate:_startDate]];
I want to make a timer in my app, but i find a problem like this:
My code run very well at viewController which contain the code below, but after i dismiss this viewController a few seconds, Xcode will report an erro: exc_bad_instruction (code=exc_i386_invop subcode=0x0
If i didn't use dispatch_suspend(self.timer) and dispatch_resume(self.timer), everything will be ok
otherwise, I can see the error will happen when Xcode deal with [viewController .cxx_destruct]
so, anyone can tell me how to fix it?
Thank you
Here is my code,
- (void)timeButtonClick:(UIButton *)button{
if (self.isTiming == YES){
self.isTiming = NO;
self.executeTime = [self.timeButton currentTitle];
dispatch_suspend(self.timer);
}else if (self.isTiming == NO){
self.isTiming = YES;
self.createDate = [NSDate date];
dispatch_resume(self.timer);
}
}
- (dispatch_object_t)timer{
if (_timer == nil) {
dispatch_queue_t timerQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerQueue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
__weak typeof (self)weakSelf = self;
dispatch_source_set_event_handler(timer, ^{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"HH:mm:ss";
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSCalendarUnit unit = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDate *currentDate = [NSDate date];
NSDateComponents *currentCmps = [calendar components:unit fromDate:weakSelf.createDate toDate:currentDate options:NSCalendarWrapComponents];
NSDate *executeTime = [dateFormatter dateFromString:weakSelf.executeTime];
NSDateComponents *executeCmps = [calendar components:unit fromDate:executeTime];
NSString *newExecuteTime = [NSString stringWithFormat:#"%02ld:%02ld:%02ld", executeCmps.hour + currentCmps.hour, executeCmps.minute + currentCmps.minute, executeCmps.second + currentCmps.second];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.timeButton setTitle:newExecuteTime forState:UIControlStateNormal];
});
});
_timer = timer;
}
return _timer;
}
Move your timer to another place like AppDelegate, you are getting this error because you are dispatching the timer to another thread, then when the VC is dismissed the timer wants to set the title to timeButton which wa salready destroyed when the VC was dismissed.
You could also try to make a strong weakself, but I really suggest you to move the timer code to another class.
I have this code:
NSDate *now = [NSDate date];
static NSDateFormatter *dateFormatter;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"h:mm:ss";
}
recordStatusLabel.text = [dateFormatter stringFromDate:now];
NSLog(#"Time now: %#", [dateFormatter stringFromDate:now]);
that counts the current time. How can i change it to start in this format?
00:00:00 (hours:minutes:seconds)
from a NSString variable:
Example: i got this value for my variable
NSString * time = #"02:16:23";
then the counter will continue the count to:
02:16:24
.
.
.
02:20:13
Create an instance of NSTimer class and set time for event fire 1 second with repeat option YES. In the event handling update your label with current time. When your functionality is complete, invalidate the timer to stop firing events.
Here is the code to create instance of NSTimer class:
NSTimer *countUpTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:#selector(countUpTimerFired:) userInfo:nil repeats:YES];
Here is the method for event handling:
- (void)countUpTimerFired:(id)sender {
dispatch_async(dispatch_get_main_queue(), ^{
recordStatusLabel.text = [dateFormatter stringFromDate:[NSDate date]];
});
}
Keep of dateFormatter and countUpTimer as class variables.
This is the simple approach to achieve your required functionality as you are starting your time from current device time; So you won't be requiring extra efforts to get value from label, incrementing the value and then converting back to string.
EDIT:
If you want to start the counter from anyother time value or from a string, you can keep a integer variable to keep the value of time in seconds. Then increment the value when timer event gets called (every second) and then converting that integer to time string.
Here's the code for initial value:
NSString *timeString = recordStatusLabel.text; //contains a string in time format like #"2:16:23" or #"00:00:00" or current time or any other value.
NSArray *timeComponents = [timeString componentsSeparatedByString:#":"];
int timeInSeconds = [timeComponents[0] intValue]*3600 + [timeComponents[1] intValue]*60 + [timeComponents[2] intValue];
in the event handling of timer:
- (void)countUpTimerFired:(id)sender {
timeInSeconds++;
int hours = timeInSeconds/3600;
int minutes = (timeInSeconds%3600)/60;
int seconds = timeInSeconds%60;
dispatch_async(dispatch_get_main_queue(), ^{
[recordStatusLabel setText:[NSString stringWithFormat:#"%d:%02d:%02d", hours, minutes, seconds]];
});
}
Since you are dealing with a timer which the fastest value is the second, then to achieve performance you just fire a timer which repeats every second.
Declare your instance variables
#implementation Yourclass {
NSDate *startDate;
NSTimer *yourTimer;
NSString *myTime;
}
When you click a button to start timer
-(IBAction)startTimer:(id)sender {
startDate = [NSDate date];
yourTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timeHandler:) userInfo:nil repeats:YES];
[yourTimer fire];
}
Implement your method which is the handler method of timer
-(void)timeHandler:(NSTimer *)myTimer {
//Difference between dates in seconds
NSTimeInterval elapsedTime = [startDate timeIntervalSinceNow];
//Divide by 3600 to get the hours
NSInteger hours = elapsedTime/3600;
//Divide by 60 to get the minutes
NSInteger minutes = elapsedTime/60;
NSInteger seconds = elapsedTime;
myTime = [NSString stringWithFormat:#"%i:%i:%i",hours, minutes, seconds];
// update the label
recordStatusLabel.text = myTime;
}
I have a countdown timer from the current date until 10 minutes have passed.
It counts down completely fine when I'm on the view, but when I change views and come back, then the timer stops, could anybody point me in the right direction?
-(void)updateCountdown {
dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"YYYY-MM-dd-HH-mm-ss"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSUInteger unitFlags = NSMinuteCalendarUnit|NSSecondCalendarUnit;
NSDateComponents *dateComponants = [calendar components:unitFlags fromDate:startingDate toDate:endingDate options:0];
NSInteger minutes = [dateComponants minute];
NSInteger seconds = [dateComponants second];
NSString *countdownText = [NSString stringWithFormat:#"Free Check: %ld:%ld", (long)minutes, (long)seconds];
timeLabel.text = countdownText;
NSLog (#"Startdate is %#", startingDate);
NSLog(#"Enddate is %#", endingDate);
//Attempt at saving the time but I guess this only saves the text?
NSString * saveStringTime = timeLabel.text;
NSUserDefaults * defaultsTime = [NSUserDefaults standardUserDefaults];
[defaultsTime setObject:saveStringTime forKey:#"saveStringTime"];
[defaultsTime synchronize];
[self performSelector:#selector(updateCountdown) withObject:nil afterDelay:1];
}
I think I need something in my ViewDidLoad to get whatever time it was and put that back in?
Thank you
You have to get the "savedStringTime" in viewWillAppear to resume from the "savedTime". Since viewDidLoad will get invoked only if the view is loaded to memory, viewDidLoad will not get invoked when the user navigates back to the same view(since view is still in memory).
To get proper working, Invoke your method to save the time in "viewWillDisappear" and in "viewWillAppear" check if the "savedStringTime" is available, if yes then resume it or else start a fresh counter.