6

我有一个 UILabel,我想显示当前时间 (HH:mm)(与状态栏中的时间相同)。

如何更新标签以更改为新时间?如果我安排一个间隔为 60 秒的 NSTimer,那么如果计时器在系统时间的分钟更改之前触发,那么标签可能会超时一分钟?

可以将计时器的间隔设置为 1 秒,还是会使用比必要更多的资源?还是有另一种方法来确保标签与状态栏时钟保持同步(最好是准确的,但 1 秒 lee 方式可以)?

4

7 回答 7

5

Dispatch 是你的朋友:

void runBlockEveryMinute(dispatch_block_t block)
{
    block(); // initial block call

    // get the current time
    struct timespec startPopTime;
    gettimeofday((struct timeval *) &startPopTime, NULL);

    // trim the time
    startPopTime.tv_sec -= (startPopTime.tv_sec % 60);
    startPopTime.tv_sec += 60;

    dispatch_time_t time = dispatch_walltime(&startPopTime, 0);

    __block dispatch_block_t afterBlock = ^(void) {
        block();

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60), dispatch_get_main_queue(), afterBlock);
    };

    dispatch_after(time, dispatch_get_main_queue(), afterBlock); // start the 'timer' going
}

这将同步到纳秒,并且仅在分钟更改时调用。我相信这是适合您情况的最佳解决方案。

于 2012-07-19T19:03:21.547 回答
1

可以将计时器的间隔设置为 1 秒,还是会使用比必要更多的资源?

取决于你在做什么。如果您要计算 pi 的前一百万位数字,或渲染数百个 3-D 对象,您将需要每个可以节省的处理器周期。如果 CPU 大部分时间都处于空闲状态,您不妨使用这些周期来使您的界面看起来不错。

于 2012-07-19T18:56:43.043 回答
1
//Create a timer...
timer = [NSTimer scheduledTimerWithTimeInterval:0.25
                                                 target:self
                                               selector:@selector(tick:)
                                               userInfo:NULL
                                                repeats:YES];
//Function to update time
- (void)tick:(NSTimer*)t
{
    NSDate *now = [NSDate date];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"HH:mm:ss"];
    NSString *timeString = [dateFormatter stringFromDate:now];
    [uilabel setText:timeString];
}

您的计时器将始终“延迟”一段时间,因为没有“委托”功能可以调用来实现此类功能。

我会坚持使用计时器,但 Richard J. Ross III 提到的调度也是有效的。

于 2012-07-19T19:41:58.880 回答
0

没有 gettimeofday 或块的秒精度同步更新。弱指针并dispatch_async防止保留循环。

- (void)updateTimeLabel
{
    if (!timeFormatter) {
        timeFormatter = [NSDateFormatter new];
        timeFormatter.dateStyle = NSDateFormatterNoStyle;
        timeFormatter.timeStyle = NSDateFormatterShortStyle;
    }
    NSDate *currentTime = [NSDate date];
    NSTimeInterval delay = [[currentTime nextMinute] timeIntervalSinceDate:currentTime];

    timeLabel.text = [timeFormatter stringFromDate:currentTime];

    __weak id weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf performSelector:@selector(updateTimeLabel) withObject:nil afterDelay:delay];
    });
}

用 0 秒获得下一分钟。

@implementation NSDate (Utils)
- (NSDate *)nextMinute {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *comps = [calendar components:(NSCalendarUnit) NSUIntegerMax fromDate:self];
    comps.minute += 1;
    comps.second = 0;

    return [calendar dateFromComponents:comps];
}
@end

我认为理查德的回答中有一个带有块的保留周期(它可以用 __weak+dispatch_async 修复)

于 2014-01-29T14:57:39.420 回答
0

我想我会在后台线程中运行一个延迟 1 秒的 NSTimer,让它运行的方法检查当前时间 (HH:mm) 到标签中显示的当前时间。如果匹配就扔掉,如果是新的,更新标签。

如果您担心性能,在您第一次返回后,找出距离下一分钟还有多少秒,并让它运行计时器运行那么长时间。然后在第一次更新后让计时器以 60 秒的间隔运行。

于 2012-07-19T18:55:38.650 回答
0

一秒钟是正确的答案。

于 2012-07-19T18:55:46.273 回答
0

也许你可以设置两个定时器。第一个(触发一次)用于同步第二个定时器,间隔为 60s,使其在系统时间到达 HH:00 时触发;

于 2012-07-19T19:05:57.593 回答