开发者

Pre-render UITabBarController views?

I have an app with a UITabBarController that manages some UINavigationControllers, which in turn manage various UIViewControllers.

On 3G phones, the first time I view any particular view via a TabBar button, it's laggy, but thereafter, it's snappy. This isn't noticeable with 3GS phones. My question is how can I force these views to pre-render? I have tried triggering the loadView functions by calling them in a different thread on start-up, but this doesn't do an开发者_StackOverflow中文版ything I don't think.

For clarity, here is an abbreviated snippet from my code to show what I'm doing. I have five view controllers, but I'm showing just the code for two. The poiVC is just a standard UITableViewController subclass - I don't even have a custom init or loadView function for it.

- (void)applicationDidFinishLaunching:(UIApplication *)application {

  self.mapVC = [[MapViewController alloc] init];
  NavControlBar * mapNavBar = [[[NavControlBar alloc] initWithViewController:mapVC 
                             withControlBar:[mapVC initBar]]autorelease];
  self.poiVC = [[POIViewController alloc] init];
  NavControlBar * poiNavBar = [[[NavControlBar alloc] initWithViewController:poiVC 
                             withControlBar:[poiVC initBar]]autorelease];

  NSArray *tabViewControllerArray = [NSArray arrayWithObjects:mapNavBar, poiNavBar, nil];
  self.tbc.viewControllers = tabViewControllerArray;
  self.tbc.delegate = self;

  [mapVC release];
  [poiVC release];
  [window addSubview:tbc.view];

}

Can I get the poiVC to render while the user is looking at the first screen, so the transition will be fast?


 [self.mapVC view];
 [self.poiVC view];

You can simply ask for the view of the view controller and not do anything with it. This will return the view, and hence load it for you if needed. The disadvantage is of course that you'll increase your startup time. Also note that your view may be unloaded when memory runs low, which causes lagginess again when switching to those tabs, but (at least tries to) keep your app running (generally considered a good thing).


I've been struggling with the same issue. All of the answers I've seen talk about trying to preload the view by making a call viewController.view, but that's not the main bottleneck, and it only saves a few hundred milliseconds at best. The majority of the actual rendering work happens between viewWillAppear and viewDidAppear, so you need to trigger a render if you want to avoid the lag during your initial transition.

To do this, you need to add your view controllers as child view controllers inside your main view. I found that the best place to do this was in the viewDidAppear callback on your main view controller, because this will not slow down the loading of your main view. And even though you're loading and adding the child views on the main thread, it doesn't seem to block the UI. (I've been testing on a view that has a background video playing.)

Here's the Swift code I've written to pre-render some view controllers:

enum ViewPreloadingState {
    case Pending, Preloading, Loaded
}
var viewPreloadingState = ViewPreloadingState.Pending

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    if viewPreloadingState == .Pending {
        viewPreloadingState = .Preloading

        addChildViewController(loginViewController)
        view.addSubview(loginViewController.view)
        loginViewController.didMoveToParentViewController(self)

        addChildViewController(signupViewController)
        view.addSubview(signupViewController.view)
        signupViewController.didMoveToParentViewController(self)

        // Place view controllers off screen
        let f = view.frame,
            offScreenFrame = CGRect(x: f.width, y: 0, width: f.width, height: f.height)
        loginViewController.view.frame  = offScreenFrame
        signupViewController.view.frame = offScreenFrame
    }
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if viewPreloadingState == .Preloading {
        viewPreloadingState = .Loaded

        loginViewController.willMoveToParentViewController(nil)
        loginViewController.view.removeFromSuperview()
        loginViewController.removeFromParentViewController()

        signupViewController.willMoveToParentViewController(nil)
        signupViewController.view.removeFromSuperview()
        signupViewController.removeFromParentViewController()
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜