Cambiar el button Atrás en iOS 7 desactiva deslizar para navegar hacia atrás

Tengo una aplicación iOS 7 donde estoy configurando un button de retroceso personalizado como este:

UIImage *backButtonImage = [UIImage imageNamed:@"back-button"]; UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; [backButton setImage:backButtonImage forState:UIControlStateNormal]; backButton.frame = CGRectMake(0, 0, 20, 20); [backButton addTarget:self action:@selector(popViewController) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton]; viewController.navigationItem.leftBarButtonItem = backBarButtonItem; 

Pero esto desactiva el gesto de "deslizar de izquierda a derecha" de iOS 7 para navegar hasta el controller anterior. ¿Alguien sabe cómo puedo configurar un button personalizado y mantener este gesto habilitado?

EDIT: Intenté configurar el viewController.navigationItem.backBarButtonItem en su lugar, pero esto no parece mostrar mi image personalizada.

IMPORTANTE: este es un truco. Recomendaría echar un vistazo a esta respuesta .

Llamar a la siguiente línea después de asignar el leftBarButtonItem funcionó para mí:

 self.navigationController.interactivePopGestureRecognizer.delegate = self; 

Editar: Esto no funciona si se llama en los methods init . Se debe llamar en viewDidLoad o methods similares.

Utilice las properties backIndicatorImage y backIndicatorTransitionMaskImage de UINavigationBar si es posible. Configurarlos en un UIAppearanceProxy puede modificar fácilmente el comportamiento en su aplicación. La arruga es que solo puedes configurarlos en ios 7, pero eso funciona porque solo puedes usar el gesto pop en ios 7 de todos modos. Su estilo normal de ios 6 puede permanecer intacto.

 UINavigationBar* appearanceNavigationBar = [UINavigationBar appearance]; //the appearanceProxy returns NO, so ask the class directly if ([[UINavigationBar class] instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { appearanceNavigationBar.backIndicatorImage = [UIImage imageNamed:@"back"]; appearanceNavigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"]; //sets back button color appearanceNavigationBar.tintColor = [UIColor whiteColor]; }else{ //do ios 6 customization } 

Tratar de manipular el delegado de interactivePopGestureRecognizer provocará muchos problemas.

Vi esta solución http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/ qué subclasss UINavigationController. Es una solución mejor ya que maneja el caso en el que deslizas antes de que el controller esté en su lugar, lo que provoca un locking.

Además de esto, noté que si desliza el control de la vista raíz (después de presionar uno y viceversa), la interfaz de usuario deja de responder (también el mismo problema en la respuesta anterior).

Por lo tanto, el código en el UINavigationController subclasificado debería tener el siguiente aspecto:

 @implementation NavigationController - (void)viewDidLoad { [super viewDidLoad]; __weak NavigationController *weakSelf = self; if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.interactivePopGestureRecognizer.delegate = weakSelf; self.delegate = weakSelf; } } - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { // Hijack the push method to disable the gesture 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 self.interactivePopGestureRecognizer.enabled = ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && [self.viewControllers count] > 1); } @end 

yo suelo

 [[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"nav_back.png"]]; [[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"nav_back.png"]]; [UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault]; 

También oculto el button Atrás, reemplazándolo con un leftBarItem personalizado.
Quitar el delegado interactivePopGestureRecognizer después de la acción push me funcionó:

 [self.navigationController pushViewController:vcToPush animated:YES]; // Enabling iOS 7 screen-edge-pan-gesture for pop action if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.delegate = nil; } 
 navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self; 

Esto es de http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks , pero provoca varios errores:

  1. Empuje otro viewController en el control de navigation al deslizar desde el borde izquierdo de la pantalla;
  2. O bien, deslícese desde el borde izquierdo de la pantalla cuando el topViewController esté apareciendo desde el controlController;

Por ejemplo, cuando el rootViewController de navigationController se muestra, desliza el dedo desde el borde izquierdo de la pantalla y toca algo (RÁPIDAMENTE) para empujar a otro ViewController al browserController, luego

  • El rootViewController no responde ningún evento táctil;
  • El otro ViewController no se mostrará;
  • Pase de nuevo desde el borde de la pantalla, se mostrará el otro ViewController;
  • Toque el button de retroceso personalizado para abrir el otro ViewController, ¡crash!

Por lo tanto, debe implementar el método self.navigationController.interactivePopGestureRecognizer.delegate en self.navigationController.interactivePopGestureRecognizer.delegate así:

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) { return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1; } return YES; } 

Prueba el self.navigationController. interactivePopGestureRecognizer .enabled = YES;

Aquí está la versión swift3 de la respuesta de Nick H247

 class NavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() if responds(to: #selector(getter: interactivePopGestureRecognizer)) { interactivePopGestureRecognizer?.delegate = self delegate = self } } override func pushViewController(_ viewController: UIViewController, animated: Bool) { if responds(to: #selector(getter: interactivePopGestureRecognizer)) { interactivePopGestureRecognizer?.isEnabled = false } super.pushViewController(viewController, animated: animated) } } extension NavigationController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { interactivePopGestureRecognizer?.isEnabled = (responds(to: #selector(getter: interactivePopGestureRecognizer)) && viewControllers.count > 1) } } extension NavigationController: UIGestureRecognizerDelegate {} 

No escribí esto, pero el siguiente blog me ayudó mucho y resolvió mis problemas con el button de navigation personalizado:

http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/

En resumen, implementa un UINavigationController personalizado que utiliza el delegado de gestos pop. Muy limpio y portátil!

Código:

 @interface CBNavigationController : UINavigationController <UINavigationControllerDelegate, UIGestureRecognizerDelegate> @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; } 

Editar. Solución agregada para problemas cuando un usuario intenta deslizar hacia la izquierda en un controller de vista raíz:

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && self.topViewController == [self.viewControllers firstObject] && gestureRecognizer == self.interactivePopGestureRecognizer) { return NO; } return YES; } 

Tuve un problema similar en el que estaba asignando el controller de vista actual como el delegado para el gesto de pop interactivo, pero rompería el gesto en cualquier vista empujada o en las vistas debajo de la vista en la stack de navigation. La forma en que solucioné esto fue establecer el delegado en -viewDidAppear , luego establecerlo en nil en -viewWillDisappear . Eso permitió que mis otras vistas funcionaran correctamente.

Imagine que estamos usando la plantilla de proyecto maestro / detalle pnetworkingeterminada de Apple, donde el maestro es un controller de vista de tabla y al tocar en él se mostrará el controller de vista detallada.

Queremos personalizar el button de retroceso que aparece en el controller de vista de detalle. Esta es la forma de personalizar la image , el color de la image , el text , el color del text y la fuente del button Atrás.


Para cambiar la image, el color de la image, el color del text o la fuente de manera global, coloque lo siguiente en una location que se llama antes de crear cualquiera de sus controlleres de vista (por ejemplo, application:didFinishLaunchingWithOptions: es un buen lugar).

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { UINavigationBar* navigationBarAppearance = [UINavigationBar appearance]; // change the back button, using default tint color navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"back"]; navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"]; // change the back button, using the color inside the original image navigationBarAppearance.backIndicatorImage = [[UIImage imageNamed:@"back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"]; // change the tint color of everything in a navigation bar navigationBarAppearance.tintColor = [UIColor greenColor]; // change the font in all toolbar buttons NSDictionary *barButtonTitleTextAttributes = @{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0], NSForegroundColorAttributeName: [UIColor purpleColor] }; [[UIBarButtonItem appearance] setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal]; return YES; } 

Tenga en count que puede usar appearanceWhenContainedIn: para tener más control sobre qué controlleres de vista se ven afectados por estos cambios, pero tenga en count que no puede pasar [DetailViewController class] , ya que está contenido dentro de un UINavigationController, no de su DetailViewController. Esto significa que necesitará subclass UINavigationController si desea más control sobre lo que se ve afectado.

Para personalizar el text o la fuente / color de un elemento de button de retroceso específico, debe hacerlo en MasterViewController (¡no DetailViewController!). Esto parece poco intuitivo porque el button aparece en DetailViewController. Sin embargo, una vez que entienda que la forma de personalizarlo es configurando una propiedad en un elemento de navigation, comienza a tener más sentido.

 - (void)viewDidLoad { // MASTER view controller [super viewDidLoad]; UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithTitle:@"Testing" style:UIBarButtonItemStylePlain target:nil action:nil]; NSDictionary *barButtonTitleTextAttributes = @{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0], NSForegroundColorAttributeName: [UIColor purpleColor] }; [buttonItem setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal]; self.navigationItem.backBarButtonItem = buttonItem; } 

Nota: intentar establecer el títuloTextAttributes después de configurar self.navigationItem.backBarButtonItem no parece funcionar, por lo que deben configurarse antes de asignar el valor a esta propiedad.

Cree una class 'TTNavigationViewController' que es la subclass de 'UINavigationController' y configure su controller de navigation existente de esta class en storyboard / class, Ejemplo de código en class –

  class TTNavigationViewController: UINavigationController, UIGestureRecognizerDelegate { override func viewDidLoad() { super.viewDidLoad() self.setNavigationBarHidden(true, animated: false) // enable slide-back if self.responds(to: #selector(getter: UINavigationController.interactivePopGestureRecognizer)) { self.interactivePopGestureRecognizer?.isEnabled = true self.interactivePopGestureRecognizer?.delegate = self } } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return true }}