Search code examples
iosobjective-cselectornstimerunrecognized-selector

App crashes when passing an argument to a method in NSTimer


I'm trying to create an NSTimer where I can pass different UILabels inside of it that will change text color depending on the time. Here's a sample of the code I'm using in hopes of achieving that. I was sure it would work, but as soon as the timer starts my app crashes with the log below.

Why does this happen? I thought I had everything set up correctly.

.h

IBOutlet UILabel *titleColor;
int time;
NSTimer *timer;

.m

- (void)viewDidLoad {
    [super viewDidLoad];
    timer = [NSTimer scheduledTimerWithTimeInterval:animationDuration target:self selector:@selector(timeCycle:) userInfo:titleColor repeats:YES];
}

- (void)timeCycle:(UILabel *)label {
    time++;
    if (time == 10) {
        time = 0;
    }

    [self labelColorCycle:label];
}

- (void)labelColorCycle:(UILabel *)label {

    if (time == 0) {
        [label setTextColor:[UIColor colorWithRed:100
                                  green:50
                                   blue:75
                               alpha:1]];
    }
    else if (time == 1) {
         // sets another color
    }
}

Crash report

2015-02-09 17:17:51.454 Test App[404:30433] -[__NSCFTimer setTextColor:]: unrecognized selector sent to instance 0x14695bb0
2015-02-09 17:17:51.455 Test App[404:30433] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFTimer setTextColor:]: unrecognized selector sent to instance 0x14695bb0'
*** First throw call stack:
(0x228c849f 0x300c2c8b 0x228cd8b9 0x228cb7d7 0x227fd058 0xf70e5 0xf6f17 0x235d9ba1 0x2288ec87 0x2288e803 0x2288ca53 0x227da3c1 0x227da1d3 0x29bd80a9 0x25de87b1 0xf5d41 0x30642aaf)
libc++abi.dylib: terminating with uncaught exception of type NSException

Solution

  • The argument of timeCycle: is NSTimer not UILabel. Also why you need to pass the label ? It's a property right ?

    So change your method like:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        timer = [NSTimer scheduledTimerWithTimeInterval:animationDuration target:self selector:@selector(timeCycle:) userInfo:nil repeats:YES];
    }
    
    - (void)timeCycle:(NSTimer *)timer
    {
        time++;
        if (time == 10)
        {
            time = 0;
        }
    
        [self labelColorCycle:self.titleColor];
    }
    
    - (void)labelColorCycle:(UILabel *)label
    {
    
        if (time == 0)
        {
            [label setTextColor:[UIColor colorWithRed:100 green:50 blue:75 alpha:1]];
        }
        else if (time == 1)
        {
             // sets another color
        }
    }
    

    And if you need to pass the label as an argument use the userInfo property of NSTimer.

    In that case your code will be like:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        timer = [NSTimer scheduledTimerWithTimeInterval:animationDuration target:self selector:@selector(timeCycle:) userInfo:self.titleColor repeats:YES];
    }
    
    - (void)timeCycle:(NSTimer *)timer
    {
        time++;
        if (time == 10)
        {
            time = 0;
        }
    
        [self labelColorCycle:(UILabel *)[timer userInfo]];
    }
    
    - (void)labelColorCycle:(UILabel *)label
    {
    
        if (time == 0)
        {
            [label setTextColor:[UIColor colorWithRed:100 green:50 blue:75 alpha:1]];
        }
        else if (time == 1)
        {
             // sets another color
        }
    }