tutorial cocoa-touch

cocoa-touch - cocoa touch tutorial



Cómo crear backBarButtomItem con vista personalizada para un UINavigationController (14)

Tengo un UINavigationController en el que UINavigationController varias vistas. Dentro de viewDidLoad para una de estas vistas, deseo establecer self.navigationItem.backBarButtonItem en una vista personalizada (basada en una imagen personalizada). No sé por qué, pero parece que no funciona. En cambio, obtengo el botón estándar "Atrás".

UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 63, 30)]; [backButton setImage:[UIImage imageNamed:@"back_OFF.png"] forState:UIControlStateNormal]; [backButton setImage:[UIImage imageNamed:@"back_ON.png"] forState:UIControlStateSelected]; UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; self.navigationItem.backBarButtonItem = backButtonItem; [backButtonItem release]; [backButton release];

Probé con un título estándar y funcionó. ¿Qué está mal con el código anterior?

self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];

gracias por cualquier ayuda en esto.


Si su objetivo final es simplemente reemplazar la imagen utilizada para el botón Atrás, puede usar un nuevo método en UIBarButtonItem disponible en iOS 5.0:

setBackButtonBackgroundImage: forState: barMetrics:

Apple Docs: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html

Aquí hay un ejemplo simple que establece una imagen de fondo personalizada para todos los botones de retroceso en su aplicación:

UIImage *toolbarBackButtonBackgroundPortrait = [[UIImage imageNamed:@"toolbarBackButtonPortrait"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)]; UIImage *toolbarBackButtonBackgroundLandscape = [[UIImage imageNamed:@"toolbarBackButtonLandscape"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundPortrait forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundLandscape forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];


Estoy bastante seguro de que backBarButtonItem es una propiedad de solo lectura. En lugar de modificar backBarButtonItem , intente establecer un leftBarButtonItem personalizado y ocultar el backBarButtonItem :

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease]; self.navigationItem.hidesBackButton = YES;

También deberá asegurarse de conectar el botón personalizado para llamar a la acción de retorno en UINavigationBar .


Estaría dispuesto a apostar que esto es un error de Apple ya que me encuentro con el mismo problema. La razón es que puedo obtener un UIBarButtonItem personalizado para que aparezca, pero solo cuando no intento utilizar el método initWithCustomView: . Según la API, el controlador de navegación comprueba lo siguiente cuando se empuja un nuevo controlador de vista:

  1. Si el nuevo controlador de vista de nivel superior tiene un elemento personalizado de botón de barra izquierda, ese elemento se muestra. Para especificar un elemento de botón de barra izquierda personalizado, establezca la propiedad leftBarButtonItem del elemento de navegación del controlador de vista.
  2. Si el controlador de vista de nivel superior no tiene un elemento personalizado de botón de barra izquierda, pero el elemento de navegación del controlador de vista anterior tiene un elemento válido en su propiedad backBarButtonItem, la barra de navegación muestra ese elemento.
  3. Si alguno de los controles de vista no especifica un elemento de botón de barra personalizado, se utiliza un botón de retroceso predeterminado y su título se establece en el valor de la propiedad de título del controlador de vista anterior; es decir, el controlador de vista un nivel más bajo la pila. (Si solo hay un controlador de vista en la pila de navegación, no se muestra el botón Atrás).

Mi caso (y el tuyo) es 2. Especifiqué un código exactamente igual al tuyo (es decir, creando un UIButton , estableciendo sus propiedades de image para varios estados, creando un UIBarButtonItem , inicializándolo con el UIButton , luego configurando el controlador de mi vista actual propiedad backBarButtonItem para UIBarButtonItem ); sin embargo, cuando más tarde presiono mi controlador de vista, no aparece nada en el lado izquierdo de mi controlador de navegación. Extrañamente, puedo hacer clic donde debería estar el botón "Atrás", y aparece el controlador de vista.

Curiosamente, si creo un UIBarButtonItem utilizando el initWithTitle:style:target:action: lugar del método initWithCustomView: muestra un botón personalizado con un título personalizado. Además, como mencionó Travis, usar la propiedad leftBarButtonItem funciona bien. Preferiría adherirme a la lógica sancionada, sin embargo, especificando el botón "Atrás" para el controlador de vista actual, que se mostrará más adelante cuando se empuje un nuevo controlador de vista, en lugar de crear un botón izquierdo para el siguiente controlador de vista. , que, posiblemente, no debería preocuparse por nada relacionado con el controlador de vista que lo precedió. : - /


backBarButtonItem no es una propiedad de solo lectura. No estoy seguro de por qué se comporta de manera tan extraña, y lo anterior es una solución válida (aunque no tan ideal).

Se comporta de forma extraña porque la configuración de BackBarButtonItem de un vc no cambia nada sobre la apariencia del elemento de navegación del vc; en su lugar, cambia el botón que apunta BACK a la vc. Vea la actualización de la barra de navegación de Apple FMI.

Dicho esto, no he tenido mucha suerte para que funcione. Si mira alrededor de este sitio, encontrará algunos hilos que sugieren la colocación de un código muy similar a lo que ya tiene inmediatamente antes de la llamada para empujar una nueva vista en la pila. He tenido algo de suerte allí, pero desafortunadamente no cuando se trata de usar una imagen personalizada.


Aunque ya está respondido, esto funcionó para mí:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"backArrow.png"] style:UIBarButtonItemStyleBordered target:nil action:nil]; self.navigationItem.backBarButtonItem = backButton; [backButton release];

Por cierto: incluso en iOS4 inicializando mi botón de retroceso con initWithCustomView: no funcionó para mí. ; (


Yo también he tenido problemas con customView en un navigationItem.backBarButtonItem . Sospecho que probablemente solo se haya registrado en el SDK.

Si bien las soluciones descritas aquí funcionan, he encontrado una solución que es un poco más elegante: todavía usa navigationItem.leftBarButtonItem , pero se encarga de ello de manera automágicamente (ya no es necesario que los controladores de vista "secundarios" lo necesiten). saber algo sobre su ''padre'' y / o configurar manualmente navigationItem.leftBarButtonItem ).

Primero, UINavigationControllerDelegate una clase sea un UINavigationControllerDelegate para el UINavigationController cuyo botón de retroceso te interese. Luego, en esta clase, configura algo como el siguiente método de delegado willShowViewController :

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{ // reference to view controller stack NSArray *viewControllers = [ navigationController viewControllers ]; if( [ viewControllers count ] > 1 ){ // the view controller we''ll be linking to UIViewController *backViewController = [ viewControllers objectAtIndex: [ viewControllers count ] - 2 ]; // create custom UIBarButtonItem UIBarButtonItem *leftButton = [[ UIBarButtonItem alloc ] initWithCustomView: someCustomView ]; // set it as the leftBarButtonItem on the incoming viewcontroller viewController.navigationItem.leftBarButtonItem = leftButton; // tidy up [ leftButton release ]; } }

Tuve algunos problemas adicionales con esto; parece que UIBarButtonItem.action y UIBarButtonItem.target no funcionan cuando se trata de un navigationItem.leftBarButtonItem . Entonces, te queda un botón de retroceso personalizado que en realidad no retrocede. Dejaré de responder a los toques en su vista personalizada como un ejercicio para el lector (utilicé un UIButton), pero necesitará agregar este método a su clase de delegado:

-(void)onDummyBackButtonTapped{ [ someNavigationController popViewControllerAnimated: YES ]; }

y conéctelo para disparar cuando se toca su vista personalizada.


Nunca pude crear un UIBarButtonItem adecuado con vista personalizada y setBackBarButtonItem .

Aquí está la solución que encontré: ¡que net UINavigationControllerDelegate maneje todo! El truco aquí es llamar al método popViewControllerAnimated: de viewController.navigationController para que no tenga que crear ningún método personalizado.

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if([navigationController.viewControllers count ] > 1) { UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0,0,70,35)]; UIButton *myBackButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain]; [myBackButton setFrame:CGRectMake(0,0,70,35)]; [myBackButton setImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal]; [myBackButton setEnabled:YES]; [myBackButton addTarget:viewController.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside]; [backButtonView addSubview:myBackButton]; [myBackButton release]; UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithCustomView:backButtonView]; viewController.navigationItem.leftBarButtonItem = backButton; [backButtonView release]; [backButton release]; } }


BackBarButtonItem de navigationController se establece en el artículo cuyo título está intentando afectar.

es decir, en el controlador de la vista de la página 1, por ejemplo, viewdidLoad:

self.title = @"Page 1 of 4"; self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Page 1" style:UIBarButtonItemStyleBordered target:nil action:nil] autorelease];

Usted no anularía esto en la página 2.

Documentación para UINavigationItem: backBarButtonItem deja esto en claro:

Cuando este elemento es el elemento posterior de la barra de navegación, cuando es el siguiente elemento debajo del elemento superior, puede representarse como un botón Atrás en la barra de navegación. Use esta propiedad para especificar el botón Atrás. El objetivo y la acción del elemento del botón de la barra posterior que configuras deben ser nulos. El valor predeterminado es un elemento de botón de barra que muestra el título del elemento de navegación.


Creo que encontré la solución para esto. Simplemente configure el botón en el elemento de navegación en el controlador anterior (El que desea volver)

Entonces, si tengo, por ejemplo, un controlador raíz y presiono un segundo controlador y quiero personalizar el botón Atrás, debo hacer lo siguiente:

self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"ico.png"] style:UIBarButtonItemStyleBordered target:nil action:nil] autorelease];

Donde self es el controlador de vista raíz y no el segundo.


Establezca backBarButtonItem antes de presionar viewController con navigationController. Establecer el backBarButtonItem en viewDidLoad no funciona.

Digamos que tengo MyTableViewController. Cuando el usuario toca una fila en particular, quiero presionar AnotherViewController con el control de navegación. El código se ve así:

// in MyTableViewController''s tableView:didSelectRowAtIndexPath method... UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"yourImage.png"] style:UIBarButtonItemStyleBordered target:nil action:nil]; self.navigationItem.backBarButtonItem = backButton; [backButton release]; [self.navigationController pushViewController:anotherViewController];

Cuando se muestra otro ViewController, el botón Atrás en la barra de navegación tendrá @"yourImage.png" y el estilo de botón de retroceso predeterminado (flecha rectangular). También tenga en cuenta que está bien pasar nil como target . El botón se comporta como el botón Atrás.


A partir de iOS5, tenemos una nueva forma excelente de personalizar la apariencia de casi cualquier control utilizando el protocolo UIAppearance , es decir, [UIBarButtonItem appearance] . El proxy de apariencia le permite crear cambios de toda la aplicación a la apariencia de los controles. A continuación se muestra un ejemplo de un botón de retroceso personalizado creado con el proxy de apariencia.

Utilice el siguiente código de ejemplo para crear un botón Atrás con imágenes personalizadas para los estados normal y resaltado. Llame al siguiente método desde la application:didFinishLaunchingWithOptions: de su application:didFinishLaunchingWithOptions:

- (void) customizeAppearance { UIImage *i1 = [[UIImage imageNamed:@"custom_backButton_30px"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)]; UIImage *i2 = [[UIImage imageNamed:@"custom_backButton_24px"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1 forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2 forState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone]; }

Este es solo un ejemplo rápido. Normalmente, le conviene tener imágenes separadas para el estado normal y resaltado (presionado).

Si está interesado en personalizar la apariencia de otros controles, algunos buenos ejemplos se pueden encontrar aquí: http://ios.biomsoft.com/2011/10/13/user-interface-customization-in-ios-5/


Así es como creo un botón cuadrado personalizado con una flecha en lugar del texto habitual.

Simplemente configuro un delegado para mi UINavigationController. Utilizo el delegado de la aplicación para eso porque el controlador de la vista de la raíz de la ventana es el UINavigationController que quiero controlar.

Entonces AppDelegate.m (ARC):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ((UINavigationController*)_window.rootViewController).delegate = self; return YES; } #pragma mark - UINavigationControllerDelegate - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if(navigationController.viewControllers.count > 1) { UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button setImage:[UIImage imageNamed:@"back-arrow.png"] forState:UIControlStateNormal]; [button setBackgroundColor:[UIColor grayColor]]; button.frame = CGRectMake(0, 0, 44, 44); viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button]; [button addEventHandler:^(id sender) { [navigationController popViewControllerAnimated:YES]; } forControlEvents:UIControlEventTouchUpInside]; } }

Estoy usando BlocksKit para atrapar el evento de toque de botón. Es muy conveniente para cosas como esta pero también puedes usar el método addTarget: action: forControlEvents regular:


Mi lógica

  1. Crear un botón personalizado (también conocido como una subclase de vista personalizada)
  2. Inicialice un elemento del botón de barra con el botón / vista personalizada
  3. Agregue una acción que nos permita "volver" a nuestro controlador de vista anterior
  4. Establezca el elemento del botón de la barra izquierda en este elemento del botón de la barra personalizada que creó
  5. Oculta el elemento del botón de la barra trasera del controlador de vista al que estás presionando

El paso 3 fue importante. Pensé que la manera más limpia de simular el "volver atrás" era simplemente utilizar el método UINavigationController (popViewControllerAnimated :). Entonces, simplemente agrego esa acción al navigationController del viewController que estoy presionando (viewControllerToPush) así:

[navItemButton addTarget:viewControllerToPush.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];

Ejemplo:

UIViewController *viewControllerToPush = [[UIViewController alloc] init]; UIImage *navImage = [UIImage imageNamed:@"your_back_button_image"]; UIButton *navItemButton = [UIButton buttonWithType:UIButtonTypeCustom]; [navItemButton setImage:navImage forState:UIControlStateNormal]; [navItemButton setImage:navImage forState:UIControlStateHighlighted]; [navItemButton setFrame:CGRectMake(0, 0, navImage.size.width, navImage.size.height)]; [navItemButton addTarget:viewControllerToPush.navigationController action:@selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:navItemButton]; viewControllerToPush.navigationItem.leftBarButtonItem = barButtonItem; viewControllerToPush.navigationItem.hidesBackButton = YES; [self.navigationController pushViewController:viewControllerToPush animated:YES];


Puede usar leftBarButtonItem en lugar de elemento de botón de retroceso. Y para eliminar el ítem predeterminado del botón Atrás, configúrelo a cero de la siguiente manera;

navigationController?.navigationBar.backIndicatorImage = nil navigationController?.navigationBar.backIndicatorTransitionMaskImage = nil let button = UIButton.init(type: .custom) button.imageView?.contentMode = UIViewContentMode.scaleAspectFit button.setImage(UIImage.init(named: "top_back"), for: UIControlState.normal) button.frame = CGRect.init(x: 0, y: 0, width: 75, height: 50) button.addTarget(self, action: #selector(handleBackButton), for: .touchUpInside) let barButton = UIBarButtonItem.init(customView: button) self.navigationItem.leftBarButtonItem = barButton