2009-07-28 9 views
6

Mam program UITabBarController utworzony programowo, który zarządza 4 podklasami UIViewController. Coś jak:Jak sprawić, aby kontrolery UITabBarController ładowały się leniwie?

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

Czy istnieje sposób, aby powiedzieć UITabBarController lejce załadować kontrolery widoku? ej, gdy użytkownik kliknie jeden z elementów paska kart lub gdy zostanie wywołany tabBarController setSelectedIndex?

Odpowiedz

12

Robię to bardzo w jednej z moich aplikacji. Sztuka polega na tym, aby kontroler widoku NIE był podklasą UITabBarController, ale raczej UIViewController i implementował UITabBarDelegate. Stworzyłem dla tego widok w IB, układając pasek zakładek (z odpowiednimi # przyciskami, obrazkami, znacznikami itp.) Oraz UIView z symbolem zastępczym, który służy do prawidłowego umieszczania subskrybowanych widoków, które są zamieniane i pomijane. Zobacz przełączanie odbywa się na umieszczenie zakładek: didSelectItem: Wygląda to mniej więcej tak:

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

Kod mogłaby z pewnością staną się bardziej sucha, ale masz pomysł.

+0

Miałem ten sam problem co OP i ten kod działał idealnie dla mnie. Dzięki!! Udało mi się obniżyć czas uruchamiania aplikacji o 2 pełne sekundy. –

+0

Właściwie robię więcej pracy i testów z tym i wpadłem na problem. Jeśli przedstawię kontroler widoku Modal, a następnie odrzuci ten modal, po powrocie do kontrolera kart jego interfejs zostanie zerwany. Mój UITabBar został przeniesiony, UIView, który jest podzbiorem niestandardowego kontrolera paska kart, został rozciągnięty, aby wypełnić więcej ekranu. Czy napotkasz na takie problemy, a jeśli tak, czy znalazłeś jakieś rozwiązanie? –

+0

@Kenny Wyland, Nie miałem żadnych takich problemów, ale nie sądzę, żebym kiedykolwiek próbował pokazać na nim modalny widok. Nie mogę sobie wyobrazić żadnego powodu z głowy, że źle by się tak zachował. – zpasternack

2

Kontroler UITabBarController wymaga, aby rzeczywiste kontrolery widoku były ustawione dla jego właściwości viewControllers - nie ma leniwego ładowania dostępnego w frameworkach Apple. Kontroler paska kart opiera się na pewnych aspektach kontrolerów, które ładuje dla swoich właściwości. Jednak nie wywołuje ona viewDidLoad, dopóki karta nie zostanie naciśnięta po raz pierwszy.

Zamiast tego można utworzyć własny kontroler widoku "zastępczy", który w metodach viewDidLoad lub viewWillAppear zastępuje się w kontrolce paska kart rzeczywistym kontrolerem widoku. W ten sposób można zminimalizować ilość pamięci używanej przez kontrolery widoku utrzymywane przez kontrolkę paska kart do momentu załadowania kontrolera widoku określonej karty i zastąpienia go bardziej kontrolerem intensywniej wykorzystującym pamięć i procesor.

Uwaga boczna: Należy bezpośrednio zmienić właściwość kontrolera paska tabulatora viewControllers, zamiast używać metody setViewControllers:animated:, aby użytkownicy nie widzieli animacji za każdym razem, gdy ładował nowy kontroler.

0

nie zawsze można użyć UITabBarController i zarządzać na umieszczenie zakładek urself, gdy ktoś wybiera kartę u popchnąć viewcontrollers zobaczyć w w metodzie didSelectIndex

4

Co chcesz załadować leniwie?

Jest to dość standardowa implementacja UITabBarController. Mam jedno bardzo podobne do tego w aplikacji, którą piszę. W moim kodzie viewDidLoad (metoda wywołana po tym, jak kontroler załadowała skojarzone widoki do pamięci) nie jest wywoływana, dopóki karta nie zostanie dotknięta.

Wierzę, że sposób, w jaki kodowałeś (poza wszystkimi autorejestrowanymi obiektami) jest preferowaną metodą tworzenia tego rodzaju interfejsu użytkownika.

+0

wypuszcza preferowany do autoreleasing? dlaczego? –

+0

Zwolnienie obiektu powoduje natychmiastowe zwolnienie pamięci, podczas gdy autorejestrowany obiekt musi poczekać, aż basen się rozładuje. Na komputerze stacjonarnym może to nie stanowić problemu, ale w świecie iPhone'a pamięć jest ograniczona. Jedno z wideo z WWDC '09 poświęcone zarządzaniu pamięcią szczególnie zaleca stosowanie autorelease w takich sytuacjach, w których nie ma innego powodu niż skrót kodu. – Lounges

+0

@Lounges Jestem nowy w rozwoju iOS ... czy możesz mi wyjaśnić, jak ten kontroler kart działa? jak i kiedy ładuje swoje widoki do pamięci i jak długo pozostaje w pamięci –

0

W niestandardowej metody UITabBarController za didSelectItem, należy po prostu po prostu zrobić to wezwanie:

[self setSelectedViewController :[self.viewControllers objectAtIndex:0]]; 
Powiązane problemy