NSTimer问题

3

我正在尝试设置一个基本计时器,但是一直失败。基本上,我只希望在用户单击按钮时启动60秒的计时器,并使用剩余时间更新标签(类似倒计时)。我创建了标签和按钮并在 IB 中将它们连接起来。接下来,我为按钮创建了一个 IBAction。现在,当我尝试根据计时器更新标签时,我的应用程序出错了。这是我的代码:

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 1
                      target: self
                      selector:@selector(updateLabelDisplay)
                      userInfo: nil repeats:YES];

我还有一个 updateLabelDisplay 函数,它会确定计时器已运行的次数,然后将该数字从60中减去,并在倒计时标签中显示该数字。有人能告诉我我在做什么错了吗?

你能详细说明一下“我的应用程序出问题了”吗?你能观察到发生了什么? - Darryl H. Thomas
根据您的描述,似乎您有一个下限(从60开始倒计时),因此您可能希望在某个时候使计时器失效。虽然这不是您的问题,但以后您需要引用计时器来使其失效。也许您稍后会在代码中执行此操作,但由于您最初使用了本地变量,我认为值得提醒您,以免日后出现问题。 - Darryl H. Thomas
基本上标签没有更新。 - Roosh
你能展示更多的代码吗?例如,我们无法知道updateLabelDisplay的声明,它需要任何参数吗?如果需要,假设它需要一个参数(类型为NSTimer),那么你需要使用@selector(updateLabelDisplay:)。这可能是最好的方式,因为updateLabelDisplay将看到NSTime实例的本地声明,并在时间到达时负责发送-invalidate消息。我下面发布的代码可能会有所帮助。 - Ken
2个回答

11

首先,如果你还没有看过的话,请查看这个链接:官方 Apple 关于使用计时器的文档

根据你的描述,你可能想要的代码看起来像这样。我对行为做了一些假设,但你可以根据自己的喜好进行调整。

这个示例假设你想保留对计时器的引用,以便暂停或其他操作。如果不是这种情况,你可以修改 handleTimerTick 方法,使其接受一个 NSTimer* 作为参数,并在计时器到期后使用它来使计时器失效。

@interface MyController : UIViewController
{
  UILabel * theLabel;

  @private
  NSTimer * countdownTimer;
  NSUInteger remainingTicks;
}

@property (nonatomic, retain) IBOutlet UILabel * theLabel;

-(IBAction)doCountdown: (id)sender;

-(void)handleTimerTick;

-(void)updateLabel;

@end

@implementation MyController
@synthesize theLabel;

// { your own lifecycle code here.... }

-(IBAction)doCountdown: (id)sender
{
  if (countdownTimer)
    return;


  remainingTicks = 60;
  [self updateLabel];

  countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector(handleTimerTick) userInfo: nil repeats: YES];
}

-(void)handleTimerTick
{
  remainingTicks--;
  [self updateLabel];

  if (remainingTicks <= 0) {
    [countdownTimer invalidate];
    countdownTimer = nil;
  }
}

-(void)updateLabel
{
  theLabel.text = [[NSNumber numberWithUnsignedInt: remainingTicks] stringValue];
}


@end

3
值得注意的是,一种更好的方法是使用时间间隔来更新标签,基于经过的时间而不是依赖于一个计数器。由于循环运行的特性,您无法保证计时器触发的精度。但上述示例代码已经足够好了。 - Darryl H. Thomas
非常感谢!只有一个问题,doCountdown会连接到我的UIButton上,对吗? - Roosh
没错。doCountdown:是要连接到您的按钮的操作。 - Darryl H. Thomas

1

也许现在回答这个问题有点晚了,但我一直在寻找一个好的地方来发布自己对这个问题的解决方案。如果对任何人有用的话,这里是我的解决方案。它会触发8次,但当然可以根据您的需要进行定制。计时器在时间到达后自行释放。

我喜欢这种方法,因为它将计数器与计时器集成在一起。

要创建一个实例,请调用类似于以下内容的代码:

SpecialKTimer *timer = [[SpecialKTimer alloc] initWithTimeInterval:0.1 
                                                         andTarget:myObject
                                                       andSelector:@selector(methodInMyObjectForTimer)];

无论如何,这里是头文件和方法文件。

//Header

#import <Foundation/Foundation.h>

@interface SpecialKTimer : NSObject {

    @private

    NSTimer *timer;

    id target;
    SEL selector;

    unsigned int counter;

}

- (id)initWithTimeInterval:(NSTimeInterval)seconds
                 andTarget:(id)t
               andSelector:(SEL)s;
- (void)dealloc;

@end

//Implementation

#import "SpecialKTimer.h"

@interface SpecialKTimer()

- (void)resetCounter;
- (void)incrementCounter;
- (void)targetMethod;

@end

@implementation SpecialKTimer

- (id)initWithTimeInterval:(NSTimeInterval)seconds
                 andTarget:(id)t
               andSelector:(SEL)s {

    if ( self == [super init] ) {

        [self resetCounter];

        target = t;
        selector = s;

        timer = [NSTimer scheduledTimerWithTimeInterval:seconds
                                                 target:self
                                               selector:@selector(targetMethod)
                                               userInfo:nil
                                                repeats:YES];

    }

    return self;

}

- (void)resetCounter {

    counter = 0;

}

- (void)incrementCounter {

    counter++;

}

- (void)targetMethod {

    if ( counter < 8 ) {

        IMP methodPointer = [target methodForSelector:selector];
        methodPointer(target, selector);

        [self incrementCounter];

    }

    else {

        [timer invalidate];
        [self release];

    }

}

- (void)dealloc {

    [super dealloc];

}

@end

在自己身上调用-dealloc是不好的做法。请参考http://stackoverflow.com/questions/3819603/suicide-objective-c-objects-calling-their-own-dealloc-methods-on-themselves。 - Ken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接