ios ios7 uibarbuttonitem navigationbar

iOS 7: ¿Cómo configurar UIBarButtonItem backButtonBackgroundImage para UIControlStateHighlighted?



ios7 navigationbar (3)

Actualmente, Apple tiene errores en interactivePopGestureRecognizer (lo que hace que congelar la vista del controlador de navegación después de deslizar hacia atrás la animación push, verá nested pop animation can result in corrupted navigation bar advertencia de nested pop animation can result in corrupted navigation bar en la consola), por cierto, podemos hacer un pequeño truco para trabajar alrededor de ese error.

Aquí hay una solución que funciona bien para mí,

Subclasifica una clase NavigationController y haz que delegue el gesto

@interface CBNavigationController : UINavigationController @end @implementation CBNavigationController - (void)viewDidLoad { __weak CBNavigationController *weakSelf = self; if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.interactivePopGestureRecognizer.delegate = weakSelf; self.delegate = weakSelf; } } // Hijack the push method to disable the gesture - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) self.interactivePopGestureRecognizer.enabled = NO; [super pushViewController:viewController animated:animated]; } #pragma mark UINavigationControllerDelegate - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate { // Enable the gesture again once the new controller is shown if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) self.interactivePopGestureRecognizer.enabled = YES; } @end

Cuando el usuario comienza a deslizar hacia atrás en el medio de una transición, los eventos pop se acumulan y "corrompen" la pila de navegación. Mi solución es desactivar temporalmente el reconocedor de gestos durante las transiciones de inserción y habilitarlo nuevamente cuando se cargue el nuevo controlador de visualización. Nuevamente, esto es más fácil con una subclase UINavigationController.

Después de esto, puede usar con calma item.leftBarButtonItem y UIButton como vista personalizada.

Estoy tratando de establecer la imagen de fondo para el botón Atrás en los estados normal y resaltado.

- (void)configureBackButtonInNavigationItem:(UINavigationItem *)item { UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"back" style:UIBarButtonItemStyleBordered target:nil action:NULL]; [backBarButtonItem setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]} forState:UIControlStateNormal]; [backBarButtonItem setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor orangeColor]} forState:UIControlStateHighlighted]; // white arrow image UIImage *normalImage = [[[UIImage imageNamed:@"btn_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] resizableImageWithCapInsets:UIEdgeInsetsMake(0.f, 17.f, 0.f, 0.f)]; // orange arrow image UIImage *pressedImage = [[[UIImage imageNamed:@"btn_on_press"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] resizableImageWithCapInsets:UIEdgeInsetsMake(0.f, 17.f, 0.f, 0.f)]; [backBarButtonItem setBackButtonBackgroundImage:normalImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [backBarButtonItem setBackButtonBackgroundImage:pressedImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; [backBarButtonItem setBackgroundImage:normalImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [backBarButtonItem setBackgroundImage:pressedImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; NSLog(@"NORMAL: %@ HIGHLIGHTED: %@", [backBarButtonItem backButtonBackgroundImageForState:UIControlStateNormal barMetrics:UIBarMetricsDefault], [backBarButtonItem backButtonBackgroundImageForState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]); item.backBarButtonItem = backBarButtonItem; NSLog(@"NORMAL: %@ HIGHLIGHTED: %@", [backBarButtonItem backButtonBackgroundImageForState:UIControlStateNormal barMetrics:UIBarMetricsDefault], [backBarButtonItem backButtonBackgroundImageForState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]); }

La salida es siguiente:

NORMAL: <_UIResizableImage: 0x16b55e10> HIGHLIGHTED: <_UIResizableImage: 0x16b593d0> NORMAL: <_UIResizableImage: 0x16b55e10> HIGHLIGHTED: <_UIResizableImage: 0x16b593d0>

Pero el resultado observado para el estado destacado es solo la atenuación de lo que se estableció en el estado normal en lugar de utilizar la imagen realzada correcta.

Normal:

Resaltado (la flecha sigue siendo blanca, el botón se atenúa inesperadamente):

No publique respuestas sobre el uso de leftBarButtonItem o UIButton como vista personalizada. Ambos enfoques evitan el comportamiento de deslizamiento hacia atrás disponible en iOS 7.

UPD: llenó el radar # 17481106 con respecto a este problema.

UPD2: radar # 17481106 arreglado en iOS 8.


Además de la respuesta de l0gg3r, puede crear una subclase de UINavigationBar donde pueda implementar la lógica de l0gg3r y personalizar su botón Atrás.
Después de lo cual solo tienes que establecer el nombre de la clase en tu barra de navegación del guión gráfico.

Algo como esto:

#import "MyNavigationBar.h" #import <objc/runtime.h> #import <objc/message.h> #pragma mark - UINavigationController category @interface UINavigationController (InteractiveGesture) <UINavigationControllerDelegate, UIGestureRecognizerDelegate> - (void)fixInteractivePopGesture; @end @implementation UINavigationController (InteractiveGesture) - (void)fixInteractivePopGesture { __weak UINavigationController *weakSelf = self; if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.interactivePopGestureRecognizer.delegate = weakSelf; self.delegate = weakSelf; } [self swizzleOriginalSelectorWithName:@"pushViewController:animated:" toSelectorWithName:@"myPushViewController:animated:"]; } #pragma mark - Swizzle method - (void)swizzleOriginalSelectorWithName:(NSString *)origName toSelectorWithName:(NSString *)swizzleName { Method origMethod = class_getInstanceMethod([self class], NSSelectorFromString(origName)); Method newMethod = class_getInstanceMethod([self class], NSSelectorFromString(swizzleName)); method_exchangeImplementations(origMethod, newMethod); } - (void)myPushViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.interactivePopGestureRecognizer.enabled = NO; } [self myPushViewController:viewController animated:animated]; } #pragma mark UINavigationControllerDelegate - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate { // Enable the gesture again once the new controller is shown if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.interactivePopGestureRecognizer.enabled = YES; } } @end #pragma mark - MyNavigationBar @interface MyNavigationBar() @property (strong, nonatomic) UIButton *backButtonCustomView; @end @implementation MyNavigationBar - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setup]; } return self; } - (void)awakeFromNib { [super awakeFromNib]; [self setup]; } - (void)setup { self.backButtonCustomView = [UIButton buttonWithType:UIButtonTypeCustom]; // here customize your button // e.g. set images for Normal state, or highlighted state, etc... // ... [self.backButtonCustomView addTarget:self action:@selector(handleBackButton:) forControlEvents:UIControlEventTouchUpInside]; self.backButton = [[UIBarButtonItem alloc] initWithCustomView: self.backButtonCustomView]; } - (void)layoutSubviews { [super layoutSubviews]; if ([[self navigationController] viewControllers].count > 1) { [self.topItem setLeftBarButtonItem:self.backButton animated:YES]; } // Enabling back "Swipe from edge to pop" feature. [self.navigationController fixInteractivePopGesture]; } - (void)handleBackButton:(id)sender { UINavigationController *nvc = [self navigationController]; [nvc popViewControllerAnimated:YES]; } - (UINavigationController *)navigationController { UINavigationController *resultNC = nil; UIViewController *vc = nil; for (UIView* next = [self superview]; next; next = next.superview) { UIResponder* nextResponder = [next nextResponder]; if ([nextResponder isKindOfClass:[UIViewController class]]) { vc = (UIViewController*)nextResponder; break; } } if (vc) { if ([vc isKindOfClass:[UINavigationController class]]) { resultNC = (UINavigationController *)vc; } else { resultNC = vc.navigationController; } } return resultNVC; } @end

Entonces:

Aqui tienes. ¡Eso es! Ahora puede copiar / pegar esa clase en cualquier proyecto que desee y simplemente establecer el nombre de clase del guión gráfico :)


No quería una vista personalizada porque rompería el deslizamiento, pero debería agregar esta línea.

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

Tu código sería algo como a continuación.

UIImage *normalImage = [[[UIImage imageNamed:@"btn_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] resizableImageWithCapInsets:UIEdgeInsetsMake(0.f, 17.f, 0.f, 0.f)]; UIImage *pressedImage = [[[UIImage imageNamed:@"btn_on_press"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] resizableImageWithCapInsets:UIEdgeInsetsMake(0.f, 17.f, 0.f, 0.f)]; UIButton *customBackButton = [UIButton buttonWithType:UIButtonTypeCustom]; [customBackButton setBackgroundImage:normalImage forState:UIControlStateNormal]; [customBackButton setBackgroundImage:pressedImage forState:UIControlStateHighlighted]; [customBackButton addTarget:self action:@selector(customBackMethod:) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *customBackBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:customBackButton]; self.navigationItem.leftBarButtonItem = customBackBarButtonItem; self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self; - (IBAction)customBackMethod:(id)sender { [self.navigationController popViewControllerAnimated:YES]; }