Cambiar el botón atrás en iOS 7 desactiva deslizar para navegar hacia atrás


Tengo una aplicación iOS 7 donde estoy configurando un botón 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 deshabilita el gesto de iOS 7 "deslizar de izquierda a derecha" para navegar al controlador anterior. ¿Alguien sabe cómo puedo configurar un botón personalizado y mantener este gesto habilitado?

EDITAR: Intenté configurar el ViewController.navigationItem.backBarButtonItem en su lugar, pero esto no parece mostrar mi imagen personalizada.

Author: RyanR, 2013-09-27

13 answers

IMPORTANTE: Esto es un truco. Yo 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 métodos init. Debe ser llamado en viewDidLoad o métodos similares.

 76
Author: Paul Hunter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:46

Utilice las propiedades backIndicatorImage y backIndicatorTransitionMaskImage de la barra de navegación Uin si es posible. Configurarlos en un UIAppearanceProxy puede modificar fácilmente el comportamiento en toda la 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. Tu 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 interactivePopGestureRecognizer conducirá a mucho de temas.

 56
Author: Saltymule,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-02 14:35:17

Vi esta solución http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/ que subclases UINavigationController. Es una mejor solución, ya que maneja el caso en el que desliza antes de que el controlador esté en su lugar, lo que causa un accidente.

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

Así que el código en el la subclase UINavigationController debería verse así:

@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
 28
Author: Nick H247,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-27 13:20:19

Utilizo

[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"nav_back.png"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"nav_back.png"]];

[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault];
 19
Author: Albert Chu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 09:37:27

También oculto el botón atrás, reemplazándolo con un botón izquierdo personalizado.
Eliminar interactivePopGestureRecognizer delegate después de la acción push funcionó para mí:

[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;
}
 6
Author: avishic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-02 09:18:47
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

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

  1. Empuje otro ViewController en el NavigationController al deslizar desde el borde izquierdo de la pantalla;
  2. O, deslice el dedo desde el borde izquierdo de la pantalla cuando el controlador topViewController está apareciendo desde el controlador NavigationController;

Por ejemplo, cuando se muestra el rootViewController de NavigationController, deslice el dedo desde la izquierda borde de la pantalla, y toque algo (RÁPIDAMENTE) para empujar otro controlador ViewController en el controlador NavigationController, entonces

  • El rootViewController no responde a ningún evento táctil;
  • El controlador anotherViewController no se mostrará;
  • Pase desde el borde de la pantalla de nuevo, se mostrará el anotherViewController;
  • Toque el botón custom back para hacer estallar el Anothererviewcontroller, crash!

Por lo que debe implementar UIGestureRecognizerDelegate método en self.navigationController.interactivePopGestureRecognizer.delegate como esto:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) {
        return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1;
    }
    return YES;
}
 6
Author: iwill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 03:22:40

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 {}
 6
Author: duan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:01

Pruebe self.navigationController.interactivePopGestureRecognizer.enabled = YES;

 3
Author: ilya n.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-27 16:42:50

No escribí esto, pero el siguiente blog ayudó mucho y resolvió mis problemas con el botón de navegación personalizado:

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

En resumen, implementa un controlador UINavigationController personalizado que usa 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. Agregada corrección de problemas cuando un usuario intenta deslizar hacia la izquierda en un controlador 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;
}
 1
Author: kgaidis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-29 14:22:02

RootView

override func viewDidAppear(_ animated: Bool) {
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}

ChildView

override func viewDidLoad() {
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
} 

extension ChildViewController: UIGestureRecognizerDelegate {}
 1
Author: PW486,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-08 04:50:52

Tuve un problema similar cuando asignaba el controlador de vista actual como delegado para el gesto pop interactivo, pero rompía el gesto en cualquier vista empujada, o vistas debajo de la vista en la pila de navegación. La forma en que resolví esto fue establecer el delegado en -viewDidAppear, luego establecerlo en nil en -viewWillDisappear. Eso permitió que mis otros puntos de vista funcionaran correctamente.

 0
Author: Bill Burgess,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-06 22:33:12

Imagine que estamos utilizando la plantilla de proyecto maestro/detalle predeterminada de Apple, donde maestro es un controlador de vista de tabla y al tocarlo se mostrará el controlador de vista de detalle.

Queremos personalizar el botón atrás que aparece en el controlador de vista de detalle. Así es como personalizar la imagen , color de la imagen, text , text color, and font of the back button.


Para cambiar la imagen, el color de la imagen, el color del texto o la fuente globalmente, coloque seguir en una ubicación que se llama antes de que se cree cualquiera de sus controladores 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 cuenta que puede usar appearanceWhenContainedIn: para tener más control sobre qué controladores de vista se ven afectados por estos cambios, pero tenga en cuenta que no puede pasar [DetailViewController class], porque está contenido dentro de un UINavigationController, no su DetailViewController. Esto significa que necesitará subclase UINavigationController si desea más control sobre lo que es afectar.

Para personalizar el texto o la fuente/color de un elemento específico del botón atrás, debe hacerlo en el MasterViewController (¡no en el DetailViewController!). Esto parece poco intuitivo porque el botón aparece en DetailViewController. Sin embargo, una vez que comprende que la forma de personalizarlo es estableciendo una propiedad en un navigationItem, 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 los titleTextAttributes después de establecer auto.navigationItem.backBarButtonItem no parece funcionar, por lo que deben establecerse antes de asignar el valor a esta propiedad.

 0
Author: Senseful,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:46

Cree una clase 'TTNavigationViewController' que sea una subclase de 'UINavigationController' y haga que su controlador de navegación existente de esta clase sea en storyboard / class, código de ejemplo 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
}}
 0
Author: Prakash Raj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-19 10:30:20