Detailed explanation of the method of realizing millisecond countdown by iOS

  • 2021-12-13 17:24:52
  • OfStack

Preface

Everyone should know that in the development of app, when displaying some products with limited-time discount, a countdown will often be added to remind users of the remaining time of the limited-time discount. For developers, what we need to realize is a countdown function. According to specific needs, this countdown can be made in days, hours, minutes, seconds and milliseconds.

Today, I will mainly talk about the millisecond timer. We know that the ary between seconds and milliseconds is 1000, that is to say, 1 second = 1000 milliseconds, so when we make a millisecond countdown timer, we set a timer with a time interval of 1 millisecond, and reduce the number of milliseconds one by one. However, this is too time-consuming, so the number of milliseconds in many millisecond timers is only between 0 and 9, which means that the time interval of this millisecond timer is 100 milliseconds, so compared with the timer with an interval of 1 millisecond, its consumption is much less, and it also achieves the effect of millisecond timing.

The idea of realizing the whole millisecond countdown is to get the timestamp of a future date and the timestamp of the current date, calculate the time difference between them, then set a timer with a time interval of 100 milliseconds, and update the corresponding value on the countdown timer every 100 milliseconds.

Implementation method

Customize 1 UIview to encapsulate the countdown.

1. Add timestamp and timer attributes to MsecCountDownView. h


@interface MsecCountDownView : UIView

@property(nonatomic, assign)double timeInterval;// A timestamp for a future date 
@property(nonatomic, strong)NSTimer *timer ; // Timer 

@end

2. Implementation of related UI and countdown method in MsecCountDownView. m


@interface MsecCountDownView (){
UIView *countdownBackView;
CGFloat _passTime;
}
@property(nonatomic, strong)UILabel *tipLabel;
@property(nonatomic, strong)UILabel *hoursLabel;
@property(nonatomic, strong)UILabel *minutesLabel;
@property(nonatomic, strong)UILabel *secondsLabel;
@property(nonatomic, strong)UILabel *millionSecondsLabel;
@property(nonatomic, strong)UILabel *label1;
@property(nonatomic, strong)UILabel *label2;
@property(nonatomic, strong)UILabel *label3;
@property(nonatomic, strong)UILabel *label4;
@end

Create related UI


- (instancetype)initWithFrame:(CGRect)frame
{
 self = [super initWithFrame:frame];

 if (self) {

  countdownBackView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
  [self addSubview:countdownBackView];
  _tipLabel=[[UILabel alloc] init];
  _tipLabel.frame = CGRectMake(0, 0, 40, countdownBackView.frame.size.height);
  [countdownBackView addSubview:_tipLabel];

  _tipLabel.font = [UIFont systemFontOfSize:12];


  // Hours 
  _hoursLabel=[[UILabel alloc] initWithFrame:CGRectMake(_tipLabel.frame.origin.x+_tipLabel.frame.size.width, 0, 35, countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_hoursLabel];
  _hoursLabel.font = [UIFont systemFontOfSize:11];

  _label1=[[UILabel alloc] initWithFrame:CGRectMake(_hoursLabel.frame.origin.x+_hoursLabel.frame.size.width, _hoursLabel.frame.origin.y, 8, countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_label1];

  // Minutes 
  _minutesLabel=[[UILabel alloc] initWithFrame:CGRectMake(_label1.frame.origin.x+_label1.frame.size.width, _hoursLabel.frame.origin.y, 20, countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_minutesLabel];
  _minutesLabel.font = [UIFont systemFontOfSize:11];

  _label2=[[UILabel alloc] initWithFrame:CGRectMake(_minutesLabel.frame.origin.x+_minutesLabel.frame.size.width, _hoursLabel.frame.origin.y, 8, countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_label2];

  // Seconds 
  _secondsLabel=[[UILabel alloc] initWithFrame:CGRectMake(_label2.frame.origin.x+_label2.frame.size.width, _hoursLabel.frame.origin.y, 20 , countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_secondsLabel];


  _secondsLabel.font = [UIFont systemFontOfSize:11];

  _label3=[[UILabel alloc] initWithFrame:CGRectMake(_secondsLabel.frame.origin.x+_secondsLabel.frame.size.width, _hoursLabel.frame.origin.y, 8 , countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_label3];


  _millionSecondsLabel=[[UILabel alloc] initWithFrame:CGRectMake(_label3.frame.origin.x+_label3.frame.size.width, _hoursLabel.frame.origin.y, 20, countdownBackView.frame.size.height)];
  [countdownBackView addSubview:_millionSecondsLabel];


   // Milliseconds 

  _millionSecondsLabel.font = [UIFont systemFontOfSize:11];

  _label1.textAlignment=1;
  _label2.textAlignment=1;
  _label3.textAlignment = 1;
  _hoursLabel.textAlignment=1;
  _minutesLabel.textAlignment=1;
  _secondsLabel.textAlignment=1;
  _millionSecondsLabel.textAlignment=1;


  _passTime=0.0;
 }


 return self;
}

Generate 1 timer


// The timestamp of a certain date in the future is obtained, and the time difference between the timestamp and the current timestamp is obtained, and a timer is generated 
- (void)setTimeInterval:(double)timeInterval
{

 _timeInterval = timeInterval ;

 NSDateFormatter *dataFormatter = [[NSDateFormatter alloc] init];
 dataFormatter.dateFormat = @"MM/dd/yyyy HH:mm:ss.SSS";

 // Gets the time of the current system and converts it with the corresponding format 
 [dataFormatter stringFromDate:[NSDate date]];
 NSString *currentDayStr = [dataFormatter stringFromDate:[NSDate date]];
 NSDate *currentDate = [dataFormatter dateFromString:currentDayStr];

 // The end time of the offer is also converted in the same format 
 NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval/1000.0];
 NSString *deadlineStr = [dataFormatter stringFromDate:date];
 NSDate *deadlineDate = [dataFormatter dateFromString:deadlineStr];

 _timeInterval=[deadlineDate timeIntervalSinceDate:currentDate]*1000;

 if (_timeInterval!=0)
 {

  // The time interval is 100 Milliseconds, that is, 0.1 Seconds 
  _timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];

  [[NSRunLoop currentRunLoop] addTimer:_timer forMode:UITrackingRunLoopMode];
 }else{
  [countdownBackView removeFromSuperview];

 }

}

Implement a method that executes every 100 milliseconds to update the corresponding value on the countdown timer


//  Every interval 100 The millisecond timer triggers the execution of the method 
- (void)timerAction
{

 [self getTimeFromTimeInterval:_timeInterval] ;


 //  When the time interval is 0 Kill the timer when 
 if (_timeInterval-_passTime == 0)
 {
  [_timer invalidate] ;
  _timer = nil ;
 }
}

//  Calculate the specific time by time interval ( Hours , Points , Seconds , Milliseconds )
- (void)getTimeFromTimeInterval : (double)timeInterval
{

 //1s=1000 Milliseconds 
 _passTime += 100.f;// Milliseconds from 0-9 , so every time I go there, 100 Milliseconds 
 _tipLabel.text=@" Remaining: ";

 _label3.text=@".";
 _label2.text=@":";
 _label1.text=@":";

 // Hours 
 NSString *hours = [NSString stringWithFormat:@"%ld", (NSInteger)((timeInterval-_passTime)/1000/60/60)];
 // Minutes 
 NSString *minute = [NSString stringWithFormat:@"%ld", (NSInteger)((timeInterval-_passTime)/1000/60)%60];
 // Number of seconds 
 NSString *second = [NSString stringWithFormat:@"%ld", ((NSInteger)(timeInterval-_passTime))/1000%60];
 // Milliseconds 
 CGFloat sss = ((NSInteger)((timeInterval - _passTime)))%1000/100;


 NSString *ss = [NSString stringWithFormat:@"%.lf", sss];

 if (minute.integerValue < 10) {
  minute = [NSString stringWithFormat:@"0%@", minute];
 }


 self.hoursLabel.text = [NSString stringWithFormat:@"%@",hours];
 self.minutesLabel.text = [NSString stringWithFormat:@"%@",minute];
 self.secondsLabel.text = [NSString stringWithFormat:@"%@",second];
 self.millionSecondsLabel.text = [NSString stringWithFormat:@"%@",ss];

 if (timeInterval - _passTime <= 0) {
  [countdownBackView removeFromSuperview];
  [self removeFromSuperview];
 }

}

3. Assign a value to the countdown timer in ViewController. m to realize the countdown you want


- (void)viewDidLoad {
 [super viewDidLoad];

 msecView=[[MsecCountDownView alloc] initWithFrame:CGRectMake(50, 100, self.view.frame.size.width-100, 16)];
 [self.view addSubview:msecView];

 NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

 [formatter setDateStyle:NSDateFormatterMediumStyle];

 [formatter setTimeStyle:NSDateFormatterShortStyle];

 [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];


 NSDate* date = [formatter dateFromString:@"2017-04-11 15:10:00.000"];
 // Convert a date to a timestamp 
 NSInteger timeSp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue]*1000;

 msecView.timeInterval=timeSp;

}

In this way, the countdown function is realized. But you need to pay attention to 1 point when you leave the page, remember to pause the timer, and then start the countdown when you return to the page.

This can be achieved in the following two ways.


-(void)viewWillAppear:(BOOL)animated{

//  Turn on the timer when the page appears  
 [msecView.timer setFireDate:[NSDate distantPast]];

}
-(void)viewWillDisappear:(BOOL)animated{
//  Pause the prompter when the page disappears 
 [msecView.timer setFireDate:[NSDate distantFuture]];
}

If necessary, you can download demo in the following two ways

1: Download on GitHub

2: Download locally

Summarize


Related articles: