UIButton no puede registrar correctamente el toque en la región inferior de la pantalla del iPhone

Tengo una aplicación con muchos botones diferentes dispuestos en una calculadora, formato cuadrado / rectangular. En realidad es muy similar a la calculadora de iOS predeterminada. Hay aproximadamente 6 filas con 4 columnas cada uno de los botones.

Problema

El problema que estoy teniendo involucra los botones en la fila inferior (aproximadamente la décima parte inferior de la pantalla en un iPhone 4). No se comportan normalmente cuando se presionan, en el sentido de que cuando se presionan, deben presionarse y mantenerse (aproximadamente por menos de un segundo) para registrar un “botón de presión”. Esto se opone al toque corto estándar.

Ningún otro botón además de esta fila inferior se comporta de esta manera.

Además, si estos botones se tocan en su borde superior, se comportan normalmente, respondiendo tan pronto como se tocan. Esto me lleva a creer que los botones en sí mismos no son los problemas, pero hay un problema con el diseño de mis vistas.

También se debe tener en cuenta que este problema solo está presente en los dispositivos físicos. En el simulador, los botones se comportan normalmente.

Contexto

La vista que contiene estos botones no es el controlador de vista raíz de la aplicación. En cambio, se transiciona a eso (nada de lujoso aquí):

[self presentViewController:navController animated:YES completion:nil]; 

Donde self es el controlador de vista raíz

El controlador de vista con el que tengo problemas figura dentro de un controlador de navegación y se presenta de forma modal por el controlador de vista raíz que puede ver arriba.

Lo que he intentado hasta ahora

  • Activación y desactivación del diseño automático: mismo problema

  • Reordenando la jerarquía de vistas: moví los botones problemáticos encima y detrás de todas las otras vistas con el mismo resultado: el mismo problema

  • Varios dispositivos (iPhone 4, 4s, 5): el mismo problema (aunque los botones responden normalmente en simuladores de 3,5 pulgadas y 4 pulgadas)

  • Prueba de otras aplicaciones (cuando los botones en esta región se presionan en otras aplicaciones, se comportan normalmente)

Información Adicional

  • Todo se presenta en Interface Builder para el controlador de vista problemático
  • Todos los botones son botones del sistema con configuraciones estándar y son todos exactamente iguales además de su texto.
  • Todos los elementos de la pantalla (botones, tags, etc.) son subvistas de la “vista”
  • Los botones están alineados entre sí y no deben superponerse más de uno o dos píxeles.
  • Los botones problemáticos tienen dimensiones: 80 ancho X 44 alto.
  • Los botones problemáticos están al ras contra la parte inferior de la pantalla
  • Además de los botones, hay un UIImage y varias tags, sin embargo, están en la parte superior de la pantalla y no se superponen con ninguno de los botones de ninguna manera.

Esto suena como una interacción entre los botones y el UIScreenEdgePanGestureRecognizer (o lo que sea) que es responsable de detectar que el usuario desea abrir el Centro de control del sistema.

En realidad, hay dos posibles problemas aquí:

  • Puede haber una interacción (es decir, conflicto) entre la posibilidad de un gesto dirigido a su aplicación y un gesto dirigido al sistema. Si tiene reconocedores de gestos, puede que tenga que usar métodos de delegado para mediar entre ellos y los reconocedores de gestos del sistema.

  • Existe un error bien establecido en el que un grifo cerca del borde de la pantalla (es decir, en la “zona” del reconocedor de gestos del borde de la pantalla) funciona, pero hace que el botón se comporte mal, es decir, no parece que se haya tocado aunque haya iniciado sesión. muestra que tiene (ver mi respuesta aquí: https://stackoverflow.com/a/22000692/341994 ).

La causa de este problema es que Apple parece colocar un GestureRecognizer en la parte inferior de la pantalla que retrasa los toques en cualquier otra vista. Después de juguetear con los reconocedores de gestos en las ventanas de la aplicación, se me ocurrió una solución que incorpora una subclase de UIButton:

 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { BOOL inside = [super pointInside: point withEvent: event]; if (inside && !self.isHighlighted && event.type == UIEventTypeTouches) { self.highlighted = YES; } return inside; } 

Se llama al método dado aunque touchesBegan: se llama demorado. Una comprobación de si la vista se encuentra en la parte inferior de la pantalla puede ser adecuada para evitar los efectos secundarios que puedan ocurrir con esta corrección.

respuesta de Lukas en rápido y deseleccionar resaltado

 extension UIButton { public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { var inside = super.pointInside(point, withEvent: event) if inside != highlighted && event?.type == .Touches { highlighted = inside } return inside } } 

Escribí una solución completa en función de la respuesta de Luka . Simplemente haga que los botones conflictivos se ajusten a esta clase y el problema desaparecerá:

 class BorderBugFixButton : UIButton { override func awakeFromNib() { super.awakeFromNib() NSNotificationCenter.defaultCenter().addObserver(self, selector: "unHighlight", name: UIApplicationWillResignActiveNotification, object: nil) } deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool { let inside = super.pointInside(point, withEvent: event) if inside != highlighted && event?.type == .Touches { highlighted = inside } return inside } internal func unHighlight() { highlighted = false } } 

PD: para aquellos de ustedes que no les gusta Storyboards / Xib, solo migren la implementación de awakeFromNib() a init()

Solución Swift 3

 extension UIControl { open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { let inside = super.point(inside: point, with: event) if inside != isHighlighted && event?.type == .touches { isHighlighted = inside } return inside } } 

Me he encontrado con esto muchas veces cuando trabajo con la actualización de Storyboards anteriores a iOS 7+, generalmente cuando ViewController en cuestión tiene una forma de UIScrollView. Comprueba dos veces estas 2 configuraciones en tu Storyboard en el Objeto ViewController (No en la vista, la que tiene el círculo amarillo). Cuando desmarqué los ‘Extender bordes’ debajo de las barras superior e inferior, el marco de scrollView se ajustó por 64 puntos (altura de las barras de navegación y estado).

UIButton no funciona en la parte inferior de la pantalla Después de establecer el espacio entre NavBar.bottom y scrollView.top de nuevo en 0, el botón comenzó a funcionar. Esto podría deberse al hecho de que scrollView.frame.bottom estaba a 64 píxeles por encima de la parte inferior de la ventana, por lo que los toques en esa área no se tuvieron en cuenta porque estaban técnicamente fuera del marco de scrollView, pero todavía se muestran visualmente por algún motivo.

iOS 9.3, Xcode 7.3

Sugeriría que debe hacer una categoría para la clase UIButton que implemente la respuesta de Lukas . Para obtener instrucciones sobre cómo crear una categoría, consulte esta publicación: ¿Cómo creo una categoría en Xcode 6 o superior?

Dale un nombre apropiado con el signo “+” tradicional, es decir, si lo llamas “BottomOfScreen”, el nombre del archivo resultante será “UIButton + BottomOfScreen”.

Si usa Object-C, obtendrá archivos * .h y * .m con la nueva categoría.

* .h

 #import  @interface UIButton (BottomOfScreen) @end 

*.metro

 #import "UIButton+BottomOfScreen.h" @implementation UIButton (BottomOfScreen) - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { BOOL inside = [super pointInside:point withEvent:event]; if (inside && !self.isHighlighted && (event.type == UIEventTypeTouches)) { self.highlighted = true; } else { nil; } return inside; } @end 
Intereting Posts