Search code examples
objective-cxcodeuiviewalphaiboutlet

UIView is not responding when changed in NSTimer method


I have this NSTimer repeating loop that will check if there is any news in my app. The checking part is irrelevant and it works fine, but to show if there's news or not, I made a UIView called orangeDot. This should be orangeDot.alpha = 1.0 if there is news and orangeDot orangeDot.alpha = 0.0 if not. But it doesn't always work. Sometimes the orangeDot is hidden (alpha=0) when there's news and sometimes the other way around. This is the code:

-(void) checkForNews {
    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
    bool news = (([defaults boolForKey:@"newsFromChat"]) || ([defaults boolForKey:@"newsFromAwaiting"])) ? YES : NO;

    if (news) {
        NSLog(@"yay news");
        [orangeDot setAlpha:1.0f];
    } else {
        NSLog(@"nothing new");
        [orangeDot setAlpha:0.0f];
    }

    NSLog(@"%@",orangeDot);
}

The NSLog works perfect and there's never problems with that part, but even though the log says "yay news" the orangeDot could be hidden and again the other way around. But sometimes it works... I haven't found a pattern in the bug yet. I have to mention that neither .alpha nor .hidden works right.

The orangeDot is sat up like this:

@property (nonatomic, retain) IBOutlet UIView * orangeDot;

and connected in the storyboard.

Please help :)


Solution

  • Based on your code, I have two things in mind:

    1. UIView updates should be made on the main thread. Check if you are calling this method from the main thread, or just call this method from main thread.

      - (void)checkForNews {
      
          dispatch_async(dispatch_get_main_queue(), ^{
      
              NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
              bool news = (([defaults boolForKey:@"newsFromChat"]) || ([defaults boolForKey:@"newsFromAwaiting"])) ? YES : NO;
      
              if (news) {
                  NSLog(@"yay news");
                  [orangeDot setAlpha:1.0f];
              } else {
                  NSLog(@"nothing new");
                  [orangeDot setAlpha:0.0f];
              }
      
              NSLog(@"%@",orangeDot);
          });
      }
      
    2. Your orangeDot declaration seems a little suspicious. Have you declared some other variable with the same name? Try to call self.orangeDot instead of only orangeDot. Normally Xcode don't let us access the IBOutlet the way you done. If you declared it once and correctly, you will be able to access the object via self.orangeDot or just _orangeDot (with an initial underline character).

      //using .self
      [self.orangeDot setAlpha:1.0f];
      
      //or using underline to access the var. without setter
      [_orangeDot setAlpha:1.0f];
      

    Update:

    Do you really really need to loop to check for updates on your user defaults? How about forget about this timer stuff and implement NSNotification pattern (or Delegates, or whatever)? You just need to send a notification event and listen for it on your relative view controller: I expect you already know how to implement it, but I will leave a little example here:

    //Somewhere ... just after you update your news data.
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NewChatDataAvailable" object:nil];
    

    At this point, you are telling your entire app that you have new chat data available, without looping unnecessarily checking for it. Now all we need to do is register to receive this notifications:

    //In this example I will register on viewDidLoad:
    
    - (void)viewDidLoad:(BOOL)animated {
    
        [super viewDidLoad:animated];
    
        //Now, your method will be called on when necessary (when data arrives)
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(checkForNews)
                                                     name:@"NewChatDataAvailable"
                                                   object:nil];
    }
    

    Now, let's improve a little bit your code:

    - (void)checkForNews {
    
        dispatch_async(dispatch_get_main_queue(), ^{
    
            NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
    
            //You don't need to put the ? YES : NO stuff, and BOOL in capital letters
            BOOL hasNews = (([defaults boolForKey:@"newsFromChat"]) ||
                             ([defaults boolForKey:@"newsFromAwaiting"]));
    
            if (hasNews) {
                NSLog(@"yay news");
                [self.orangeDot setAlpha:1.0f];
            } else {
                NSLog(@"nothing new");
                [self.orangeDot setAlpha:0.0f];
            }
    
            NSLog(@"%@",self.orangeDot);
        });
    }
    

    I hope it helps you. And ... when you update your question with code or something, edit your question and insert the code there, don't post it as an answer.