【问题标题】:How to make UITabBarController load view controllers lazily?如何让 UITabBarController 懒加载视图控制器?
【发布时间】:2009-07-28 22:33:14
【问题描述】:

我有一个以编程方式创建的 UITabBarController,它管理 UIViewController 的 4 个子类。比如:

//Create Controller 1
    self.terminal = [[[TerminalController alloc] initWithNibName:@"TerminalView" bundle:nil] autorelease];
    UINavigationController* navTerminal = [[[UINavigationController alloc] initWithRootViewController:terminal] autorelease];
    navTerminal.title = __(@"Terminal");
    navTerminal.navigationBar.barStyle = UIBarStyleBlackOpaque;
    navTerminal.tabBarItem.image = [UIImage imageNamed:@"tab_terminal.png"];        

    //Create Controller 2
    self.history = [[[HistoryController alloc] initWithNibName:@"HistoryView" bundle:nil] autorelease];
    UINavigationController* navHistory =  [[[UINavigationController alloc] initWithRootViewController:history] autorelease];
    navHistory.title = __(@"History");
    navHistory.navigationBar.barStyle = UIBarStyleBlackOpaque;
    navHistory.tabBarItem.image = [UIImage imageNamed:@"tab_history.png"];

    //Create Controller 3
    self.settings = [[[SettingsController alloc] initWithNibName:@"SettingsView" bundle:nil] autorelease];
    UINavigationController* navSettings =  [[[UINavigationController alloc] initWithRootViewController:settings] autorelease];
    navSettings.title = __(@"Settings");
    navSettings.navigationBar.barStyle = UIBarStyleBlackOpaque;
    navSettings.tabBarItem.image = [UIImage imageNamed:@"tab_settings.png"];

    //Create Controller 4
    HelpController* help = [[[HelpController alloc] initWithNibName:@"HelpView" bundle:nil] autorelease];
    UINavigationController* navHelp =  [[[UINavigationController alloc] initWithRootViewController:help] autorelease];
    navHelp.title = __(@"Help");
    navHelp.navigationBar.barStyle = UIBarStyleBlackOpaque;
    navHelp.tabBarItem.image = [UIImage imageNamed:@"tab_help.png"];

    //Create Tab Bar an add it's view to window.
    self.tabBar = [[[UITabBarController alloc] initWithNibName:nil bundle:nil] autorelease];
    tabBar.viewControllers = [[[NSArray alloc] initWithObjects:navTerminal, navHistory, navSettings, navHelp, nil] autorelease];
    tabBar.delegate = self;     

    [window addSubview:tabBar.view];

有没有办法告诉 UITabBarController 懒加载视图控制器? ej,当用户单击选项卡栏项之一或调用 tabBarController setSelectedIndex 时?

【问题讨论】:

    标签: iphone


    【解决方案1】:

    我正在我的一个应用程序中做这件事。诀窍是让您的视图控制器不是 UITabBarController 的子类,而是 UIViewController,并实现 UITabBarDelegate。我在 IB 中为此创建了视图,布置了标签栏(带有正确的按钮、图像、标签等)和一个占位符 UIView,用于正确放置被换入和换出的子视图。视图切换发生在 tabBar:didSelectItem: 它看起来像这样:

    // MyTabBarController.h
    @class MyFirstViewController;
    @class MySecondViewController;
    @class MyThirdViewController;
    
    @interface MyTabBarController : UIViewController <UITabBarDelegate> {
        IBOutlet UIView *placeholderView;
        IBOutlet UITabBar *tabBar;
        MyFirstViewController *firstViewController;
        MySecondViewController *secondViewController;
        MyThirdViewController *thirdViewController;
        UIViewController *currentViewController;
    }
    @property (nonatomic, retain) MyFirstViewController *firstViewController;
    @property (nonatomic, retain) MySecondViewController *secondViewController;
    @property (nonatomic, retain) MyThirdViewController *thirdViewController;
    
    - (void) switchToView:(UIViewController*)aViewController;
    @end
    
    
    //  MyTabBarController.m
    #import "MyTabBarController.h"
    #import "MyFirstViewController.h"
    #import "MySecondViewController.h"
    #import "MyThirdViewController.h"
    
    enum {
        kView_First = 1,
        kView_Second,
        kView_Third
    };
    
    @implementation MyTabBarController
    
    @synthesize firstViewController, secondViewController, thirdViewController;
    
    - (void) viewDidLoad {
        // Default to first view.
        tabBar.selectedItem = [tabBar.items objectAtIndex:0];
        MyFirstViewController *viewController = [[MyFirstViewController alloc] initWithNibName:@"FirstView" bundle:nil];
        self.firstViewController = viewController;
        [viewController release];
        [self switchToView:firstViewController];
    }
    
    - (void)viewWillAppear:(BOOL)animated {
        // Tell our subview.
        if( currentViewController != nil ) {
            [currentViewController viewWillAppear:animated];
        }
    }
    
    - (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item {
        switch (item.tag) {
            case kView_First: {
                if (firstViewController == nil) {
                    MyFirstViewController *viewController = [[MyFirstViewController alloc]
                        initWithNibName:@"FirstView" bundle:nil];
                    self.firstViewController = viewController;
                    [viewController release];
                }
    
                [self switchToView:firstViewController];
            }
            break;
    
            case kView_Second:
                if (secondViewController == nil) {
                    MySecondViewController *viewController = [[MySecondViewController alloc]
                    initWithNibName:@"SecondView" bundle:nil];
                    self.secondViewController = viewController;
                    [viewController release];
                }
    
                [self switchToView:secondViewController];
                break;
    
            case kView_Third: {
                if (timesViewController == nil) {
                    MyThirdViewController *viewController = [[MyThirdViewController alloc]
                    initWithNibName:@"ThirdView" bundle:nil];
                    self.thirdViewController = viewController;
                    [viewController release];
                }
    
                [self switchToView:thirdViewController];
            }
            break;              
        }
    }
    
    - (void) switchToView:(UIViewController*)aViewController {
        if( aViewController == currentViewController ) return;
    
        UIView *aView= aViewController.view;                
        [aViewController viewWillAppear:NO];
        if( currentViewController != nil ) {
            [currentViewController viewWillDisappear:NO];
            [currentViewController.view removeFromSuperview];       
        }
        aView.frame = placeholderView.frame;
        [self.view insertSubview:aView aboveSubview:placeholderView];
        if( currentViewController != nil ) {
            [currentViewController viewDidDisappear:NO];
        }
        [aViewController viewDidAppear:NO];
        currentViewController = aViewController;
    }
    @end
    

    代码当然可以做得更干,但你明白了。

    【讨论】:

    • 我遇到了与 OP 相同的问题,这段代码对我来说非常有效。谢谢!!我能够将我的应用启动时间缩短 2 秒。
    • 实际上,我正在做更多的工作和测试,但遇到了问题。如果我呈现一个模态视图控制器,然后关闭该模态,当我回到选项卡控制器时,它的界面就完全搞砸了。我的 UITabBar 已移动,作为自定义标签栏控制器的子视图的 UIView 已拉伸以填充更多屏幕。您是否遇到过这样的问题,如果遇到过,您是否找到了解决方案?
    • @Kenny Wyland,我没有遇到过这样的麻烦,但我认为我从未尝试过在它上面显示模式视图。但是,我想不出有什么理由会像那样行为不端。
    • +1 我需要为一个项目滚动我自己的类似 UITabbarController 的根控制器,我不确定如何管理子视图控制器,这足以让我继续前进,谢谢!
    【解决方案2】:

    你想延迟加载什么?

    这是一个非常标准的 UITabBarController 实现。我正在编写的应用程序中有一个与它非常相似的应用程序。在我的代码中,viewDidLoad(在控制器将其关联视图加载到内存后调用的方法)在标签被触摸之前不会被调用。

    我确实相信您的编码方式(除了所有自动释放的对象)是创​​建这种 UI 的首选方法。

    【讨论】:

    • 发布优先于自动发布吗?这是为什么呢?
    • 释放对象会立即释放内存,而自动释放的对象必须等待池耗尽。在台式计算机上,这可能不是问题,但在 iPhone 的世界中,内存是一种有限的商品。一个关于内存管理的 WWDC '09 视频特别建议不要在这种除了代码速记之外没有其他原因的情况下使用自动释放。
    • @Lounges 我是 iOS 开发的新手……你能解释一下这个标签控制器是如何工作的吗?将视图加载到内存的方式和时间以及在内存中保留多长时间
    【解决方案3】:

    UITabBarController 要求为其viewControllers 属性设置实际的视图控制器——Apple 的框架不提供延迟加载。标签栏控制器依赖于它加载的控制器的某些方面的属性。但是,在第一次按下选项卡之前,它不会调用viewDidLoad

    您可以做的是创建自己的“占位符”视图控制器,在其viewDidLoadviewWillAppear 方法中,将标签栏控制器中的自身替换为其实际视图控制器。这样,您可以最大限度地减少选项卡栏控制器持有的视图控制器使用的内存,直到您加载某个选项卡的视图控制器并将其替换为更占用内存和处理器的控制器。

    旁注:您需要直接更改标签栏控制器的viewControllers 属性,而不是使用setViewControllers:animated: 方法,这样您的用户就不会在每次加载新控制器时看到动画。

    【讨论】:

      【解决方案4】:

      你总是不能使用 UITabBarController 并自己管理标签栏,当有人选择一个标签时,你在 didSelectIndex 方法中推送 viewcontrollers 视图

      【讨论】:

        【解决方案5】:

        在自定义 UITabBarController 的 didSelectItem 方法中,您只需进行以下调用:

        [self setSelectedViewController :[self.viewControllers objectAtIndex:0]];
        

        【讨论】:

          猜你喜欢
          • 2016-10-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-06
          • 1970-01-01
          • 2013-12-01
          相关资源
          最近更新 更多