2010-01-22 27 views
23

Estoy tratando de encontrar la "mejor" manera de usar un UISegmentedControl para una aplicación de iPhone. He leído algunas publicaciones aquí en stackoverflow y he visto las ideas de algunas personas, pero no puedo definir la mejor manera de hacerlo. Los mensajes que me refiero son:UISegmentedControl Best Practice

Changing Views from UISegmentedControl y How do I use a UISegmentedControl to switch views?

Parecería que las opciones son:

  • Añadir a cada uno de los puntos de vista de IB y las ponen a la cabeza de unos a otros y luego mostrarlos u ocultarlos
  • Cree cada una de las subvistas por separado en IB, luego cree un contenedor en la vista principal para completar con la subvista que necesita
  • Configurar un muy alto o muy amplia UIView y animar a la izquierda/derecha o arriba/abajo dependiendo del segmento seleccionado
  • Use un UITabBarController que cambiar las subvistas - parece tonto
  • Para las tablas, vuelva a cargar la mesa y en cellForRowAtIndex y rellenar la tabla de diferentes fuentes de datos o secciones en base a la opción segmento seleccionado (no es el caso para mi aplicación)

Entonces, ¿cuál es el mejor enfoque para los enfoques subvista/no-mesa? ¿Cuál es el más fácil de implementar? ¿Podría compartir algún código de muestra para el enfoque?

Gracias!

Respuesta

11

Me gustaría ir con la segunda opción que mencionas, crear las subvistas en IB e intercambiarlas dentro y fuera de una vista principal. Esta sería una buena oportunidad para usar UIViewController, sin clasificar: en su configuración inicial, cree un controlador usando -initWithNibName:bundle: (donde el primer parámetro es el nombre del NIB que contiene la subvista individual, y el segundo parámetro es nil) y agregue su view como una subvista de su vista principal según sea necesario. Esto ayudará a mantener baja la huella de la memoria: el comportamiento predeterminado de UIViewController al recibir una advertencia de memoria es liberar su vista si no tiene una vista superior. Siempre que elimine las vistas ocultas de la jerarquía de vistas, puede mantener los controladores en la memoria y no preocuparse por liberar nada.

(editado en respuesta a comentar :)

No es necesario subclase UIViewController, pero sí es necesario XIBs separadas para cada vista. Tampoco es necesario que agregue nada a la vista que contiene en IB.

Las variables de instancia, en la interfaz de cualquier clase está manejando todo esto:

UIViewController *controllerOne; 
UIViewController *controllerTwo; 

UIViewController *currentController; 

IBOutlet UIView *theContainerView; 

En su configuración (-applicationDidFinishLaunching: o lo que sea)

controllerOne = [[UIViewController alloc] initWithNibName:@"MyFirstView" bundle:nil]; 
controllerTwo = [[UIViewController alloc] initWithNibName:@"MySecondView" bundle:nil]; 

para cambiar a un controlador:

- (void)switchToController:(UIViewController *)newCtl 
{ 
     if(newCtl == currentController) 
      return; 
     if([currentController isViewLoaded]) 
      [currentController.view removeFromSuperview]; 

     if(newCtl != nil) 
      [theContainerView addSubview:newCtl.view]; 

     currentController = newCtl; 
} 

Luego simplemente llame con eso, por ejemplo,

[self switchToController:controllerOne]; 
+0

sólo para aclarar ... ¿Estás diciendo que para crear un .h UIViewController separada, .m, y el archivo .xib en IB para cada subvista, crear una instancia de cada uno de ellos en viewDidLoad de la vista "que contiene", agregar una UIView en IB a la vista que lo contiene, y luego ¿qué hacer? –

19

He encontrado este requisito también en una aplicación de iPad.

La solución llegué a era crear controladores de vista especializados para cada estilo de vista para manejar la lógica de negocio relacionada con los puntos de vista (es decir., Relativa a cada segmento), y programáticamente añadir/quitar ellos como subvistas a una Controlador "administrador" en respuesta a los cambios de índice del segmento seleccionados.

Para hacer esto, uno tiene que crear una subclase UIViewController adicional que gestione UISegmentedControl changes, y agrega/elimina las subvistas.

El siguiente código hace todo esto, también teniendo cuidado de unos pocos salvedades/extras:

  • viewWillAppear/viewWillDisappear/etc, no se ha pedido a las subvistas automáticamente, y necesitan que se les diga a través de la controlador 'gestión'
  • viewWillAppear/viewWillDisappear/etc, no son llamados en la 'gestión' controlador cuando está dentro de un controlador de navegación, por lo tanto, el controlador de navegación delegado
  • Si desea empujar en una pila de navegación desde dentro de un 01 En la subvista del segmento, debe volver a llamar a la vista 'administración' para hacerlo, ya que la subvista se ha creado fuera de la jerarquía de navegación , y no tendrá una referencia al controlador de navegación .
  • Si se utiliza dentro de un escenario de controlador de navegación, el botón Atrás es configurado automáticamente con el nombre del segmento.

Interfaz:

@interface SegmentManagingViewController : UIViewController <UINavigationControllerDelegate> { 
    UISegmentedControl * segmentedControl; 
    UIViewController  * activeViewController; 
    NSArray    * segmentedViewControllers; 
} 

@property (nonatomic, retain) IBOutlet UISegmentedControl * segmentedControl; 
@property (nonatomic, retain) UIViewController   * activeViewController; 
@property (nonatomic, retain) NSArray      * segmentedViewControllers; 

@end 

Implementación:

@interface SegmentManagingViewController() 
- (void)didChangeSegmentControl:(UISegmentedControl *)control; 
@end 

@implementation SegmentManagingViewController 

@synthesize segmentedControl, activeViewController, segmentedViewControllers; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    UIViewController * controller1 = [[MyViewController1 alloc] initWithParentViewController:self]; 
    UIViewController * controller2 = [[MyViewController2 alloc] initWithParentViewController:self]; 
    UIViewController * controller3 = [[MyViewController3 alloc] initWithParentViewController:self]; 

    self.segmentedViewControllers = [NSArray arrayWithObjects:controller1, controller2, controller3, nil]; 
    [controller1 release]; 
    [controller2 release]; 
    [controller3 release]; 

    self.navigationItem.titleView = self.segmentedControl = 
    [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Seg 1", @"Seg 2", @"Seg 3", nil]]; 
    self.segmentedControl.selectedSegmentIndex = 0; 
    self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar; 

    [self.segmentedControl addTarget:self action:@selector(didChangeSegmentControl:) forControlEvents:UIControlEventValueChanged]; 

    [self didChangeSegmentControl:self.segmentedControl]; // kick everything off 
} 

- (void)viewWillAppear:(BOOL)animated { 
    [super viewWillAppear:animated]; 
    [self.activeViewController viewWillAppear:animated]; 
} 

- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
    [self.activeViewController viewDidAppear:animated]; 
} 

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    [self.activeViewController viewWillDisappear:animated]; 
} 

- (void)viewDidDisappear:(BOOL)animated { 
    [super viewDidDisappear:animated]; 
    [self.activeViewController viewDidDisappear:animated]; 
} 

#pragma mark - 
#pragma mark UINavigationControllerDelegate control 

// Required to ensure we call viewDidAppear/viewWillAppear on ourselves (and the active view controller) 
// inside of a navigation stack, since viewDidAppear/willAppear insn't invoked automatically. Without this 
// selected table views don't know when to de-highlight the selected row. 

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 
    [viewController viewDidAppear:animated]; 
} 

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { 
    [viewController viewWillAppear:animated]; 
} 

#pragma mark - 
#pragma mark Segment control 

- (void)didChangeSegmentControl:(UISegmentedControl *)control { 
    if (self.activeViewController) { 
     [self.activeViewController viewWillDisappear:NO]; 
     [self.activeViewController.view removeFromSuperview]; 
     [self.activeViewController viewDidDisappear:NO]; 
    } 

    self.activeViewController = [self.segmentedViewControllers objectAtIndex:control.selectedSegmentIndex]; 

    [self.activeViewController viewWillAppear:NO]; 
    [self.view addSubview:self.activeViewController.view]; 
    [self.activeViewController viewDidAppear:NO]; 

    NSString * segmentTitle = [control titleForSegmentAtIndex:control.selectedSegmentIndex]; 
    self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:segmentTitle style:UIBarButtonItemStylePlain target:nil action:nil]; 
} 

#pragma mark - 
#pragma mark Memory management 

- (void)dealloc { 
    self.segmentedControl = nil; 
    self.segmentedViewControllers = nil; 
    self.activeViewController = nil; 
    [super dealloc]; 
} 

@end 

Espero que esto ayude.

+1

¡Votado! Hay una pequeña pérdida de memoria en el método -didChangeSegmentedControl :. La última instrucción debería ser: self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle: segmentTitle style: UIBarButtonItemStylePlain target: nil action: nil] autorelease]; Una pérdida de memoria similar está en viewDidLoad: self.navigationItem.titleView = self.segmentedControl = [[[UISegmentedControl alloc] initWithItems: [NSArray arrayWithObjects: @ "Seg 1", @ "Seg 2", @ "Seg 3", nil ]] liberación automática]; – Mustafa

+0

Agregando un enlace a una nueva actualización que he publicado con respecto a este patrón - utilizando un controlador de vista UINavigationController en lugar de un contenedor. http: // redartisan.com/2010/6/27/uisegmented-control-view-switching-revisited – crafterm

+0

Por alguna razón, cuando uso esto en un iPad, la sección de detalles no cambia de tamaño correctamente, es como si las vistas se dibujaran en un retrato completo de iPad. – Convolution