0

I have the following code :

@property (weak, nonatomic) IBOutlet UIView *loadingView;

-(void) hideLoadingScreen{
    self.loadingViewRect = self.loadingView.frame;
    [self.loadingView setHidden:YES];
    [self.loadingView setFrame:CGRectMake(0,0,0,0)];
}
- (void)viewDidAppear:(BOOL)animated{
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    self.data = [self.dbAPI getTodayVisits];
                    [[self tableView] reloadData];
                    [self hideLoadingScreen];
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            self.data = [self.dbAPI getTodayVisits];
                            [[self tableView] reloadData];
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            self.data = [self.dbAPI getTodayVisits];
            [[self tableView] reloadData];
        }
    }];
}

The hideLoadingScreen method won't execute (I mean it gets executed, but the UI doesn't update).

I tried all things to make it work (including dispatching [self hideLoadingScreen] to the main thread via GCD and performSelectorOnMainThread / making a __block BOOL variable isLoading and sleeping on the main thread till that variable was changed etc.). I also called the hideLoadingView method on the viewDidAppear method and it works, but I want it to hide when the callback is executed. Unfortunately, I couldn't find a solution by searching on stackoverflow, neither on google (I must say I tried all the solutions I found).

L.E. I logged self.loadingView as Rob Napier suggested

New code:

-(void) hideLoadingScreen{
    self.loadingViewRect = self.loadingView.frame;
    NSLog(@"hideLoadingScreen before: %@",self.loadingView);
    [self.loadingView setHidden:YES];
    [self.loadingView setFrame:CGRectMake(0,0,0,0)];
    NSLog(@"hideLoadingScreen after: %@",self.loadingView);
}

- (void)viewDidAppear:(BOOL)animated{
    NSLog(@"%@",self.loadingView);
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.data = [self.dbAPI getTodayVisits];
                        [[self tableView] reloadData];
                        NSLog(@"async before: %@",self.loadingView);
                        [self hideLoadingScreen];
                        NSLog(@"async after: %@",self.loadingView);
                    });
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            self.data = [self.dbAPI getTodayVisits];
                            [[self tableView] reloadData];
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            self.data = [self.dbAPI getTodayVisits];
            [[self tableView] reloadData];
        }
    }];
}

Logs:

2016-01-08 16:22:25.973 sunwaves.reporting[4566:282042] async before: <UIView: 0x7fa681e745d0; frame = (0 0; 1024 650); autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.973 sunwaves.reporting[4566:282042] hideLoadingScreen before: <UIView: 0x7fa681e745d0; frame = (0 0; 1024 650); autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.974 sunwaves.reporting[4566:282042] hideLoadingScreen after: <UIView: 0x7fa681e745d0; frame = (0 0; 0 0); hidden = YES; autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>
2016-01-08 16:22:25.974 sunwaves.reporting[4566:282042] async after: <UIView: 0x7fa681e745d0; frame = (0 0; 0 0); hidden = YES; autoresize = RM+H+BM; layer = <CALayer: 0x7fa681e291c0>>

1 Answer 1

2

The majority of UIKit calls must be made on the main queue. You should use dispatch_async(dispatch_get_main_queue(),... to do this.

This includes your calls to reloadData at a minimum.

You assignments to self.data are also likely not thread-safe (unless you've done something special in the setter). So those need to be on the main queue.

And of course your calls to hideLoadingScreen.

I assume that most of these blocks execute off the main queue, so that means putting in dispatch_async in several places.

- (void)viewDidAppear:(BOOL)animated{
    [self.apiClient checkLoggedInWithCallback:^(int status){
        if(status == ONLINE_LOGGED_IN){
            [self.dbAPI isPullSynchronized:^(BOOL isPullSynced){
                if(isPullSynced){
                    dispatch_async(dispatch_get_main_queue(), ^{
                        self.data = [self.dbAPI getTodayVisits];
                        [[self tableView] reloadData];
                        [self hideLoadingScreen];
                    });
                } else {
                    [self.apiClient getTodayVisits:^(NSArray* visits){
                        [self.dbAPI insertTodayVisits:visits withCallback:^(int status){
                            dispatch_async(dispatch_get_main_queue(), ^{
                                self.data = [self.dbAPI getTodayVisits];
                                [[self tableView] reloadData];
                            });
                        }];
                    }];
                }
            }];
        } else if(status == ONLINE_NOT_LOGGED_IN || status == ONLINE_INVALID_TOKEN || status == OFFLINE_NOT_LOGGED_IN) {
            //Redirect to login
        } else {
            //Get from Local DB
            dispatch_async(dispatch_get_main_queue(), ^{
                self.data = [self.dbAPI getTodayVisits];
                [[self tableView] reloadData];
            });
        }
    }];
}
Sign up to request clarification or add additional context in comments.

6 Comments

self.data gets assigned and the reloadData call is working, but the view won't hide. I understand that both self.data assignment and reloadData should be called on the main thread, but the hideLoadingScreen method is still not updating the UI. I tried using dispatch_async as in your answer with no success. The method hideLoadingScreen is correct and does what is expected (i tried running it on viewDidAppear method). Thanks though, I appreciate it!
Add logging. Ensure that (a) hideLoadingScreen actually run, (b) self.loadingView is non-nil. Note that setting the value to hidden and also setting the frame to zero is odd. Hiding it should be sufficient.
Thanks again! I modified my question with your suggestions. As you can see the method and the callback are reffering to different UIView's. Any idea why?
Oops, I'm wrong. They are reffering the same UIView (I mistake CALayer with the UIView). But the UI still doesn't update.
If the view does not hide after this code, then self.loadingView probably points to something other than what you think it does. For example, you may have added multiple loading views to the superview, such that self.loadingView only refers to one of them. That in particular is a very common mistake.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.