Botón trasero personalizado iOS 7

Quiero usar un botón de retroceso personalizado. en iOS 6 todo es perfecto, pero iOS 7 es extraño.

 [[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:@"back_button_normal"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0, 0, 12.0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; 

primero, no tiene una flecha de iOS 7 ni una imagen de fondo.

(Localidad rusa)

estado inicial

luego, si presiona el botón aparece la imagen de fondo. También tuve una imagen de fondo configurada para el estado UIControlStateHighlighted y cuando mantienes presionado el botón, también aparece la imagen resaltada. Después de presionar cualquier botón de retroceso, todos los botones de retroceso tienen una imagen de fondo.

una vez presionado

¡PERO! Si presenta el controlador de vista modal, deséchelo y luego presione cualquier controlador de vista; la flecha iOS 7 aparecerá en cada botón de retroceso.

Yo uso DP5. ¿Es eso un error de UIKit?

PD: También traté de crear el botón Atrás manualmente, usando UIBarButtonItem , estableciendo una imagen de fondo en él, luego self.navigationItem.backBarButtonItem = barButtonItem; No ayudó. Luego traté de establecer la imagen de fondo en estado deshabilitado y cambiar la propiedad habilitada de mi elemento de botón de barra, tampoco ayudé.

enter image description here

Esto no es un error, así es como Back button ve el Back button en iOS 7. Por ejemplo:

enter image description here

Probablemente deberías usar el nuevo concepto para tu aplicación y no establecer una imagen de fondo para el botón Atrás en iOS 7.

Si aún desea que su botón de atrás tenga el mismo aspecto en iOS6 de lo que probablemente debería crear esos botones de retroceso manualmente:

 - (void)loadView { [super loadView]; UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)]; UIImage *backImage = [[UIImage imageNamed:@"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)]; [backButton setBackgroundImage:backImage forState:UIControlStateNormal]; [backButton setTitle:@"Back" forState:UIControlStateNormal]; [backButton addTarget:self action:@selector(popBack) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; self.navigationItem.leftBarButtonItem = backButtonItem; } -(void) popBack { [self.navigationController popViewControllerAnimated:YES]; } 

Editar : no para romper el gesto de deslizar ( Aquí hay una fuente)

 self.navigationController.interactivePopGestureRecognizer.delegate = (id)self; 

La imagen de fondo personalizada que no aparece en el primer impulso se corrigió en iOS 7 GM.

Para ocultar el indicador trasero estándar, use este código:

 if ([UINavigationBar instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { // iOS 7 [navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:@"transparent_1px"]]; [navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"transparent_1px"]]; } 

La imagen de fondo personalizada que no aparece inicialmente no se corrigió en iOS7 GM o final, por lo que yo sé. Veo el mismo problema Parece ser un error de Apple; la vista privada que usa Apple simplemente no recibe una llamada setNeedsDisplay cuando lo necesita en la pantalla inicial. Hacer cualquier cosa que provoque esa llamada debería arreglarlo, como presionarlo (lo que probablemente cambie el estado interno para que llame a setNeedsDisplay sobre sí mismo), o subir un modal (lo que probablemente obligue a una nueva visualización de toda la jerarquía de vistas en el siguiente viewWillAppear: llamar).

Usar leftBarItems en su lugar también puede funcionar, pero eso puede ocasionar muchos problemas de mantenimiento con el código existente (algunas pantallas pueden tener sus propios elementos a la izquierda, esperando que cuando se vuelvan a poner a cero restauren el elemento posterior original, por ejemplo).

Como se mencionó, lo ideal sería que pueda cambiar a una apariencia sin bordes en iOS7, lo que significa que el error no es realmente evidente (ya que no hay una imagen de fondo). Sin embargo, para algunas situaciones de transición de iOS6 / iOS7, puede ser difícil (muchas pantallas, y / o la necesidad de admitir versiones anteriores de iOS por un tiempo y demasiado difícil para implementar dos miradas, y no se ve bien sin bordes sin otro cambios). Si ese es el caso, el siguiente parche debería funcionar:

 #import  @implementation UINavigationBar (BackButtonDisplayFix) + (void)load { if ([UIDevice currentDevice].systemVersion.intValue >= 7) { /* * We first try to simply add an override version of didAddSubview: to the class. If it * fails, that means that the class already has its own override implementation of the method * (which we are expecting in this case), so use a method-swap version instead. */ Method didAddMethod = class_getInstanceMethod(self, @selector(_displaybugfixsuper_didAddSubview:)); if (!class_addMethod(self, @selector(didAddSubview:), method_getImplementation(didAddMethod), method_getTypeEncoding(didAddMethod))) { Method existMethod = class_getInstanceMethod(self, @selector(didAddSubview:)); Method replacement = class_getInstanceMethod(self, @selector(_displaybugfix_didAddSubview:)); method_exchangeImplementations(existMethod, replacement); } } } - (void)_displaybugfixsuper_didAddSubview:(UIView *)subview { [super didAddSubview:subview]; [subview setNeedsDisplay]; } - (void)_displaybugfix_didAddSubview:(UIView *)subview { [self _displaybugfix_didAddSubview:subview]; // calls the existing method [subview setNeedsDisplay]; } @end 

Nota: UINavigationBar actualmente tiene una anulación del método en cuestión, por lo que espero que se use el estilo method_exchangeImplementations. Acabo de agregar las otras cosas por seguridad en caso de que Apple cambie su código. Es posible que no tengamos fronteras, pero encontré que este enfoque funcionó como una opción (hasta un levantamiento UI más completo), al menos.

Nota adicional: este error parece estar solucionado en iOS 7.1. Por lo tanto, el parche podría estar condicionado para que solo instale los métodos si se ejecuta> = 7.0 y <7.1.

Hay una mejor solución que no involucra el método swizzling.

Necesita agregar el método UINavigationViewControllerDelegate en algún lugar de su aplicación.

 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { dispatch_async(dispatch_get_main_queue(), ^{ [[navigationController.navigationBar subviews] makeObjectsPerformSelector:@selector(setNeedsDisplay)]; }); 

}

Mi solución es para iOS 7 y superior.

Al principio, haga que el botón de retroceso predeterminado sea invisible.

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

luego, configure backIndicatorImage predeterminado del botón Atrás usando una imagen personalizada.

 [UINavigationBar appearance].backIndicatorImage = [[UIImage imageNamed:@"topbar_icon_back_n.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; [UINavigationBar appearance].backIndicatorTransitionMaskImage = [[UIImage imageNamed:@"topbar_icon_back_p.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; 

En este punto, UINavigationBar personalizado para redimensionar _UINavigationBarBackIndicatorView que contiene la imagen de arriba de backIndicatorImage .

 const CGPoint SANavigationBarOffset = {-8, 11.5}; @implementation SANavigationBar - (void)layoutSubviews { [super layoutSubviews]; // set back button position NSArray *classNamesToReposition = @[@"_UINavigationBarBackIndicatorView"]; for (UIView *view in [self subviews]) { if ([classNamesToReposition containsObject:NSStringFromClass([view class])]) { CGRect frame = [view frame]; frame.origin.x = 0; frame.origin.y = 0; [view setFrame:frame]; } } } @end 

luego, configúralo como mi barra de navegación

 // set custom NavagationBar for back button position [self.navigationController setValue:[[SANavigationBar alloc] init] forKey:@"navigationBar"]; 

Agregar botón como elemento de navegación en ios7 como se muestra a continuación

  UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)]; [btnAdd setContentMode:UIViewContentModeScaleAspectFit]; [btnAdd setBackgroundImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal]; [btnAdd addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView]; self.navigationItem.rightBarButtonItem = btnAdd; 

Usando Swift puedes simplemente agregar una extensión:

 extension UIViewController: UIGestureRecognizerDelegate { func popBack() { self.navigationController?.popViewControllerAnimated(true) } func enableCustomBackButtom() { self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon-back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack") self.navigationController?.interactivePopGestureRecognizer.delegate = self } } 

Y en tu UIViewController utiliza así:

 self.enableCustomBackButtom() 

Simplemente lo hice proporcionando el mismo comportamiento que en iOS6 (observe que navigationBar es UINavigationBar), asegúrese de que navigationBar tenga un topItem

 UINavigationItem *topItemNavigation = [navigationBar topItem]; UIBarButtonItem *barButtonTopItemNavigation = [[UIBarButtonItem alloc] initWithTitle:topItemNavigation.title style:UIBarButtonItemStyleBordered target:nil action:nil]; [barButtonTopItemNavigation setBackButtonBackgroundImage:YOUR_IMAGE_BACKGROUND forState:UIControlStateNormal barMetrics:UIBarMetricsDefault ]; [topItemNavigation setBackBarButtonItem: barButtonTopItemNavigation]; } 

Mi solución fue escribir una categoría en UINavigationItem. Esto es para iOS7.

 - (void)mdSetCustomBackButton:(UINavigationController *)navigationController { MDBackButton *backButton = [[MDBackButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0) navigationController:navigationController]; [backButton addTarget:self action:@selector(popBack:) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; [self setLeftBarButtonItem:barButtonItem]; [navigationController.interactivePopGestureRecognizer setDelegate:(id)self]; } - (void)popBack:(MDBackButton *)sender { [sender.navigationController popViewControllerAnimated:YES]; } 

Y subclase UIButton para agregar una propiedad UINavigationController (para abrir y cerrar el delegado deslizar hacia atrás).

 @property (nonatomic, weak) UINavigationController *navigationController; @implementation MDBackButton - (id)initWithFrame:(CGRect)frame navigationController:(UINavigationController *)navigationController { self = [super initWithFrame:frame]; if(self){ _navigationController = navigationController; [self setImage:[UIImage imageNamed:@"back_button"] forState:UIControlStateNormal]; } return self; } 

Esto es trabajo para mí:

 - (void)setCustomNavigationBackButton { self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; UIImage *myIcon = [self imageWithImage:[UIImage imageNamed:@"backbutton.png"] scaledToSize:CGSizeMake(20, 20)]; self.navigationController.navigationBar.backIndicatorImage = myIcon; self.navigationController.navigationBar.backIndicatorTransitionMaskImage = myIcon; } - (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize { //UIGraphicsBeginImageContext(newSize); // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution). // Pass 1.0 to force exact pixel size. UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

Además, fuente personalizada con color personalizado:

 //self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes: @{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont fontWithName:@"Signika-Bold" size:20]} forState:UIControlStateNormal]; 

Referencia: https://stackoverflow.com/a/2658801/1371949

Uso estos códigos a continuación, que funcionan en iOS 8

 UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.translatesAutoresizingMaskIntoConstraints = NO; button.exclusiveTouch = YES; button.titleLabel.font = [UIFont systemFontOfSize:14.0]; [button setTitleColor:kWhiteColor forState:UIControlStateNormal]; [button setTitleColor:[UIColor colorWithRed:1/255.0 green:36/255.0 blue:60/255.0 alpha:1.0] forState:UIControlStateHighlighted]; [button setTitle:@"Back" forState:UIControlStateNormal]; [button setImage:[UIImage imageNamed:@"barbutton_back"] forState:UIControlStateNormal]; [button setImageEdgeInsets:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)]; CGSize fontSize = [button.titleLabel sizeThatFits:CGSizeMake(100.0, 30.0)]; button.frame = CGRectMake(0.0, 0.0, button.imageView.image.size.width+fontSize.width, 30.0); UIBarButtonItem *barbtn = [[UIBarButtonItem alloc] initWithCustomView:button]; //fix iOS 7 left margin UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; negativeSpacer.width = -10; self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:negativeSpacer,barbtn, nil]; 
 -(void) viewWillAppear:(BOOL)animated { UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; [btn setFrame:CGRectMake(0, 0, 30, 44)]; [btn setImage:[UIImage imageNamed:@"btnBack.png"] forState:UIControlStateNormal]; [btn addTarget:self action:@selector(PopToView) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *btnBack = [[UIBarButtonItem alloc] initWithCustomView:btn]; [btnBack setTintColor:[UIColor whiteColor]]; [[self.navigationController navigationItem] setLeftBarButtonItem:btnBack]; }