4

I am using setNeedsDisplay on my GUI, but there update is sometimes not done. I am using UIPageControllView, each page has UIScrollView with UIView inside.

I have the following pipeline:

1) application comes from background - called applicationWillEnterForeground

2) start data download from server

2.1) after data download is finished, trigger selector

3) use dispatch_async with dispatch_get_main_queue() to fill labels, images etc. with new data

3.1) call setNeedsDisplay on view (also tried on scroll view and page controller)

Problem is, that step 3.1 is called, but changes apper only from time to time. If I swap pages, the refresh is done and I can see new data (so download works correctly). But without manual page turn, there is no update.

Any help ?

Edit: code from step 3 and 3.1 (removed _needRefresh variables pointed in comments)

 -(void)FillData {
    dispatch_async(dispatch_get_main_queue(), ^{

    NSString *stateID = [DataManager  ConvertStateToStringFromID:_activeCity.actual_weather.state];

    if ([_activeCity.actual_weather.is_night boolValue] == YES)
    {
          self.contentBgImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"bg_%@_noc", [_bgs objectForKey:stateID]]];

        if (_isNight == NO)
        {
            _bgTransparencyInited = NO;
        }

        _isNight = YES;

    }
    else
    {
        self.contentBgImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"bg_%@", [_bgs objectForKey:stateID]]];

        if (_isNight == YES)
        {
            _bgTransparencyInited = NO;
        }

        _isNight = NO;

    }
    [self.contentBgImage setNeedsDisplay]; //refresh background image

    [self CreateBackgroundTransparency]; //create transparent background if colors changed - only from time to time


    self.contentView.parentController = self;
    [self.contentView FillData]; //Fill UIView with data - set labels texts to new ones

    //_needRefresh is set to YES after application comes from background

    [self.contentView setNeedsDisplay]; //This do nothing ?

    [_grad display]; //refresh gradient

}); 
}

And here is selector called after data download (in MainViewController)

-(void)FinishDownload:(NSNotification *)notification
{
    dispatch_async(dispatch_get_main_queue(), ^{

        [_activeViewController FillData]; //call method shown before

        //try call some more refresh - also useless
        [self.pageControl setNeedsDisplay];

         //[self reloadInputViews];
         [self.view setNeedsDisplay];


    });

} 

In AppDelegate I have this for application comes from background:

-(void)applicationWillEnterForeground:(UIApplication *)application
{
    MainViewController *main = (MainViewController *)[(SWRevealViewController *)self.window.rootViewController frontViewController];


    [main UpdateData];
} 

In MainViewController

-(void)UpdateData
{

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(FinishForecastDownload:) name:@"FinishDownload" object:nil]; //create selector

    [[DataManager SharedManager] DownloadForecastDataWithAfterSelector:@"FinishDownload"]; //trigger download
}
Martin Perry
  • 9,232
  • 8
  • 46
  • 114
  • Do you call the setNeedsDisplay within the dispatch_async block? If that is the case: setNeedsDisplay needs to be called on the main thread! – Sebastian Borggrewe Jul 07 '14 at 13:52
  • @SebastianBorggrewe I call him in dispatch_async with dispatch_get_main_queue() – Martin Perry Jul 07 '14 at 13:54
  • Could you post the code of the dispatch block? – Sebastian Borggrewe Jul 07 '14 at 13:56
  • @SebastianBorggrewe Added code snipset – Martin Perry Jul 07 '14 at 14:02
  • Ok just for clarification. You have ensured, that [self.contentView setNeedsDisplay]; and [self.pageControl setNeedsDisplay]; are in fact called (breakpoint or NSLog) and that both (in case you use storyboards and/or nib files) are linked outlets – Sebastian Borggrewe Jul 07 '14 at 14:16
  • @SebastianBorggrewe If I add breakpoint to the code, its triggered. But with breakpoint, GUI is updated almost everytime. Plus weird is, that sometimes (without any breakpoints, test on device with unpluged XCode), it works just fine (its about 40% coretly updated on iPhone, on iPad on the other hand almost never). I tested it .. update correct - 20 minute later (new data download initiated) - update correct - 20 minutes later - incorrect ... etc- – Martin Perry Jul 07 '14 at 14:22
  • Please can you paste more context, like calling the FillData method, part of your async codw – AntonijoDev Jul 07 '14 at 14:40
  • @AntonijoDev There is essentially nothing more. This is the base part, in FillData only UILabels are populated. – Martin Perry Jul 07 '14 at 15:39
  • Where are the variables _needRefresh and _needRefreshDwnl set to yes? – Sebastian Borggrewe Jul 07 '14 at 15:47
  • From applicationWillEnterForeground, I call setter (set _needRefreshDwnl) and this setter calls another one for inner view (set _needRefresh) – Martin Perry Jul 07 '14 at 16:08
  • Can you please post the code? – Sebastian Borggrewe Jul 07 '14 at 16:41
  • Trie removing dispatch_async(dispatch_get_main_queue(), ^{ from FillData, cause from this code you dont need it there, dont think this will resolve your issue, but keeping it simple and reducing your code to minimal factor is the way to debug your issue – AntonijoDev Jul 07 '14 at 16:52
  • @AntonijoDev GUI must be updated from main thread, so this should be there. – Martin Perry Jul 07 '14 at 17:26
  • Your call to this function already ensures that it will be executed on the main thread – AntonijoDev Jul 07 '14 at 17:43
  • Now I reviewed your code more closely. Dispatch breaks the line of the execution by throwing a message in the message loop, so you can really be sure that it will execute when you want it to. you really should reduce dispatches at the points where you really need them cause in this case the page control will refresh before the view in the FillData method. – AntonijoDev Jul 07 '14 at 17:54
  • where do you set _needRefresh? – Sebastian Borggrewe Jul 07 '14 at 21:20
  • @SebastianBorggrewe In [_activeViewController SetNeedRefresh] – Martin Perry Jul 08 '14 at 06:05
  • I have also tried to remove both _needRefresh variablesAnd call setNeedsDisplay ever time. Same effect – Martin Perry Jul 08 '14 at 12:00

3 Answers3

1

try this:

[self.view performSelectorOnMainThread:@selector(setNeedsLayout) withObject:nil waitUntilDone:NO];

or check this link:

http://blackpixel.com/blog/2013/11/performselectoronmainthread-vs-dispatch-async.html

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
0

setNeedsDisplay triggers drawRect: and is used to "redraw the pixels" of the view , not to configure the view or its subviews.

You could override drawRect: and modify your labels, etc. there but that's not what it is made for and neither setNeedsLayout/layoutSubviews is.

You should create your own updateUI method where you use your fresh data to update the UI and not rely on specialized system calls meant for redrawing pixels (setNeedsDisplay) or adjusting subviews' frames (drawRect:).


You should set all your label.text's, imageView.image's, etc in the updateUI method. Also it is a good idea to try to only set those values through this method and not directly from any method.

Rivera
  • 10,792
  • 3
  • 58
  • 102
  • What should i put inside my own updateUI? I have alreday changed all labels by myself, but only thing that is missing is those new values to be visible. I still see old ones. – Martin Perry Jul 10 '14 at 08:08
0

None of proposed solutions worked. So at the end, I have simply remove currently showed screen from UIPageControllView and add this screen again. Something like changing the page there and back again programatically.

Its a bit slower, but works fine.

Martin Perry
  • 9,232
  • 8
  • 46
  • 114