Display multiple views serially using GCD

2019-06-10 06:46发布

I have 4 tabs and all have UIWebView. I want that the first tab should load and displayed immediately without waiting for others to load. For which I did this in UITabBarController class:

for(UIViewController * viewController in  self.viewControllers){
        if(![[NSUserDefaults standardUserDefaults]boolForKey:@"isSessionExpired"])
        {
            if((int)[self.viewControllers indexOfObject:viewController] != 4)
            {
                viewController.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:viewController];
                [viewController view];
            }
        }
    }

But the main thread waits for all tabs to load.
I tried this with GCD using dispatch_async

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
        dispatch_async(dispatch_get_main_queue(), ^{
        UIViewController *firstContrl = [self.viewControllers objectAtIndex:0];
        firstContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:firstContrl];
        [firstContrl view];
        dispatch_async(dispatch_get_main_queue(), ^{ // 2
            UIViewController *secContrl = [self.viewControllers objectAtIndex:1];
            secContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:secContrl];
            [secContrl view];
            dispatch_async(dispatch_get_main_queue(), ^{ // 3
                UIViewController *thirdContrl = [self.viewControllers objectAtIndex:2];
                thirdContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:thirdContrl];
                [thirdContrl view];
                dispatch_async(dispatch_get_main_queue(), ^{ // 4
                    UIViewController *fourthContrl = [self.viewControllers objectAtIndex:3];
                    fourthContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:fourthContrl];
                    [fourthContrl view];
                });
            });
        });
    });
}); 

But this doesn't work either. It takes the same time to display the first tab.
How can this be fixed?

1条回答
Ridiculous、
2楼-- · 2019-06-10 07:21

Ehm.. GCD and UIWebView's -loadRequest: doesn't work as you think. All the code above is pretty equal to:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        UIViewController *firstContrl = [self.viewControllers objectAtIndex:0];
        firstContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:firstContrl];
        [firstContrl view];

        UIViewController *secContrl = [self.viewControllers objectAtIndex:1];
        secContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:secContrl];
        [secContrl view];

        UIViewController *thirdContrl = [self.viewControllers objectAtIndex:2];
        thirdContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:thirdContrl];
        [thirdContrl view];

        UIViewController *fourthContrl = [self.viewControllers objectAtIndex:3];
        fourthContrl.tabBarItem.tag = (int)[[self viewControllers] indexOfObject:fourthContrl];
        [fourthContrl view];

    });
});

To solve your problem you need to implement queue depends on - (void)webViewDidFinishLoad:(UIWebView *)webView. The idea is:

  1. First view controller's webView starts loading request asynchronously;
  2. When request is loaded (or failed), webView will notify your delegate;
  3. Notify all other view controllers with webView, to let them start loading their requests;

If I were you, I would implement it in next way:

// FirstViewController.h
extern NSString * const FirstViewControllerDidLoadWebView;

// FirstViewController.m
NSString * const FirstViewControllerDidLoadWebView=@"FirstViewControllerDidLoadWebView";
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// notify other controllers, to let them load their web views:
[[NSNotificationCenter defaultCenter] postNotificationName:FirstViewControllerDidLoadWebView
 object:nil];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    // loading failed. Try to load it few more times but anyway notify other controllers after:
[[NSNotificationCenter defaultCenter] postNotificationName:FirstViewControllerDidLoadWebView
 object:nil];
}

After that, 'subscribe' all other(only tabs) view controllers for FirstViewControllerDidLoadWebView notification and load their WebView after it arrives.

For more details check NSNotificationCenter and UIWebViewDelegate

查看更多
登录 后发表回答