2009-07-28 8 views
6

Ich habe einen UITabBarController, der programmatisch erstellt wird, der 4 Unterklassen von UIViewController verwaltet. Etwas wie:Wie kann UITabBarController View Controller träge laden?

//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]; 

Gibt es eine Möglichkeit, die UITabBarController zu sagen lazily die Ansicht-Controller zu laden? ej, wenn der Benutzer auf eines der Registerkartenelemente klickt oder wenn tabBarController setSelectedIndex aufgerufen wird?

Antwort

12

Ich mache das gerade in einer meiner Apps. Der Trick besteht darin, dass Ihr View-Controller NICHT eine Unterklasse von UITabBarController ist, sondern UIViewController, und implementieren Sie UITabBarDelegate. Ich habe die Ansicht dafür in IB erstellt, indem ich die Tab-Leiste (mit der richtigen Anzahl von Knöpfen, Bildern, Tags usw.) und einen Platzhalter UIView eingerichtet habe, der verwendet wird, um die Subviews, die ausgetauscht werden, richtig zu platzieren. Ansicht Schalt geschieht auf tabBar: didSelectItem: Es sieht etwa so aus:

// 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 

Der Code könnte sicherlich mehr DRY gemacht werden, aber Sie bekommen die Idee.

+0

Ich hatte das gleiche Problem wie das OP und dieser Code funktionierte perfekt für mich. Vielen Dank!! Ich konnte die Startzeit meiner App um 2 Sekunden reduzieren. –

+0

Eigentlich mache ich noch etwas mehr Arbeit und teste damit und bin auf ein Problem gestoßen. Wenn ich einen Modal View Controller präsentiere und dieses Modal dann verwerfe, wenn ich zurück zum Tab Controller komme, ist seine Oberfläche total durcheinander. Meine UITabBar wurde verschoben, die UIView, die die Unteransicht des benutzerdefinierten Tab-Controllers ist, wurde erweitert, um mehr von dem Bildschirm zu füllen. Sind Sie auf solche Probleme gestoßen und haben Sie eine Lösung gefunden? –

+0

@Kenny Wyland, ich hatte keine Probleme wie diese, aber ich glaube nicht, dass ich jemals versucht habe, eine modale Ansicht darüber anzuzeigen. Ich kann mir keinen Grund vorstellen, dass es sich so benehmen würde. – zpasternack

2

Ein UITabBarController erfordert, dass die aktuellen View-Controller für die viewControllers-Eigenschaft festgelegt werden - Lazy Loading ist in Apples Frameworks nicht verfügbar. Der Tab-Bar-Controller beruht auf bestimmten Aspekten der Controller, die er für seine Eigenschaften lädt. Es wird jedoch nicht viewDidLoad aufgerufen, bis die Registerkarte zum ersten Mal gedrückt wird.

Sie können stattdessen einen eigenen "Platzhalter" -Sichtcontroller erstellen, der sich in den Methoden viewDidLoad oder viewWillAppear in der Registerkartensteuerung durch seinen aktuellen Ansichtscontroller ersetzt. Auf diese Weise können Sie den von den Ansichtscontrollern des Registerkartencontrollers verwendeten Speicher minimieren, bis Sie den Ansichtscontroller einer bestimmten Registerkarte laden und ihn durch Ihren speicher- und prozessorintensiven Controller ersetzen.

Hinweis: Sie sollten die Eigenschaft viewControllers des Tab-Bar-Controllers direkt ändern, anstatt die Methode setViewControllers:animated: zu verwenden, damit Ihre Benutzer nicht jedes Mal eine Animation sehen, wenn Sie einen neuen Controller laden.

0

Sie können immer keine UITabBarController verwenden und die urself verwalten Tabbar, wenn jemand eine Registerkarte u die Viewcontrollers in der Methode didSelectIndex sehen in drücken wählt

4

Was Sie versuchen, faul zu laden?

Dies ist eine ziemlich standardmäßige UITabBarController-Implementierung. Ich habe eine sehr ähnliche in einer Anwendung, die ich schreibe. In meinem Code wird die Methode viewDidLoad (die Methode, die aufgerufen wird, nachdem ein Controller seine zugeordneten Ansichten in den Speicher geladen hat) nicht aufgerufen, bis die Registerkarte berührt wird.

Ich glaube, die Art, wie Sie (abgesehen von allen der automatisch freigegebenen Objekte) codiert haben, ist die bevorzugte Methode zum Erstellen dieser Art von UI.

+0

autoreleasing bevorzugt ist die Freigabe? Warum das? –

+0

Das Freigeben eines Objekts gibt den Speicher sofort frei, während ein automatisch freigegebenes Objekt auf das Ablaufen des Pools warten muss. Auf einem Desktop-Computer ist das vielleicht kein Problem, aber in der Welt des iPhone ist Speicher ein begrenztes Gut. Eines der WWDC '09-Videos zur Speicherverwaltung empfiehlt ausdrücklich, in solchen Situationen, in denen es keinen anderen Grund als Code-Kurzschreibweise gibt, Autorelease zu verwenden. – Lounges

+0

@Lounges Ich bin neu in iOS-Entwicklung ... können Sie mir bitte erklären, wie dieser Tab-Controller tatsächlich funktioniert? wie und wann es seine Ansichten in den Speicher lädt und wie lange es im Speicher bleibt –

0

Im didSelectItem Methode des benutzerdefinierten UITabBarController, sollten Sie einfach nur diesen Anruf:

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