0

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 :)

FormigaNinja
  • 1,571
  • 1
  • 24
  • 36
Simon Degn
  • 901
  • 1
  • 12
  • 40
  • Post surrounding code of where the timer is made. – Schemetrical May 25 '15 at 09:30
  • The NSLog is doing perfect, so it's somehow the connection to the interface. The NSLog(@"%@",orangeDot); also prints the orangeDot as fx alpha=1.0 when it should be, but it's actually hidden in the interface. – Simon Degn May 25 '15 at 09:33
  • 1
    Probably the timer triggered code is not executed on the main (UI) thread. I suggest you post the orangeDot method call as a block to the main queue. – Rudi Angela May 25 '15 at 09:35
  • Exactly what @RudiAngela said, that's why I want you to post surrounding code. You can `dispatch_async(dispatch_get_main_queue(), ^{//wrap contents of -checkForNews in here}` to use the main thread. – Schemetrical May 25 '15 at 09:37

2 Answers2

1

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.

FormigaNinja
  • 1,571
  • 1
  • 24
  • 36
0

Thanks for the help so far!

A better overview over the code:

...
@synthesize orangeDot; 
... 

- (void) viewDidLoad {
   ...
   [self reloader];
   ...
}

- (void) reloader{
    if (refreshAuto == nil) {
    refreshAuto = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self selector:@selector(checkForNews) userInfo: nil repeats:YES];
    }    
}

 - (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);
    });
}

The log would some times look like this when the orangeDot is shown:

...
2015-05-25 12:02:23.788 Teazr[1314:442396] nothing new
2015-05-25 12:02:23.789 Teazr[1314:442396] <UIButton: 0x154520f40; frame = (322 47; 11 11); alpha = 0; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x17422a740>>
...

The @synthesizing is a personal preferring and not required. But it works the same way with self.orangeDot and no synthesizing..

Simon Degn
  • 901
  • 1
  • 12
  • 40
  • Please check your data type: `BOOL` is different from `bool` change your `bool news` to `BOOL news` BOOL - YES/NO and bool - true/false. just try changing it.. sometimes it causes a problem like this.. since you are using `YES : NO` instead of `true : false`. – 0yeoj May 25 '15 at 10:31
  • hmm but the bool works fine. It always logs the right "answer" from NSLog in the loop.. – Simon Degn May 25 '15 at 10:33
  • Where is this `orangeDot` exactly? in a table? – 0yeoj May 25 '15 at 10:48
  • No it's just placed on the main UIView in a UIViewController. And I got the same result when I placed it as [self.view addSubview:orangeDot]; .. :( – Simon Degn May 25 '15 at 10:51
  • try placing this `- (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; }` haha.. i as well cant figure out what going on with it looks fully functional for me.. hahaha.. i try this later and let you know what i've found.. :) – 0yeoj May 25 '15 at 11:05