¿Existe una forma sencilla de permitir la interacción con un botón en una UIView que se encuentra debajo de otra UIView, donde no hay objetos reales de la UIView superior en la parte superior del botón?
Por ejemplo, en este momento tengo una UIView (A) con un objeto en la parte superior y un objeto en la parte inferior de la pantalla y nada en el medio. Esto se encuentra encima de otra UIView que tiene botones en el medio (B). Sin embargo, parece que no puedo interactuar con los botones en el medio de B.
Puedo ver los botones en B - he configurado el fondo de A en clearColor - pero los botones en B no parecen recibir toques a pesar del hecho de que no hay objetos de A realmente encima de esos botones.
EDITAR : todavía quiero poder interactuar con los objetos en la UIView superior
¿Seguro que hay una forma sencilla de hacer esto?
fuente
UIButton
que está debajo de un semitransparente,UIView
mientras que la parte no transparente delUIView
aún responderá a los eventos táctiles.Respuestas:
Debe crear una subclase UIView para su vista superior y anular el siguiente método:
También puede mirar el método hitTest: event:.
fuente
return
hace esa declaración, peroreturn CGRectContainsPoint(eachSubview.frame, point)
funciona para mí. Respuesta extremadamente útil de lo contrarioMIDDLE_Y1<=y<=MIDDLE_Y2
área.Si bien muchas de las respuestas aquí funcionarán, estoy un poco sorprendido de ver que aquí no se ha dado la respuesta más conveniente, genérica e infalible. @Ash estuvo más cerca, excepto que hay algo extraño al devolver la supervista ... no hagas eso.
Esta respuesta está tomada de una respuesta que di a una pregunta similar aquí .
[super hitTest:point withEvent:event]
devolverá la vista más profunda en la jerarquía de esa vista que se tocó. SihitView == self
(es decir, si no hay una subvista debajo del punto de contacto), regresenil
, especificando que esta vista no debe recibir el toque. La forma en que funciona la cadena de respuesta significa que la jerarquía de vistas por encima de este punto se seguirá recorriendo hasta que se encuentre una vista que responda al toque. No devuelva la supervista, ya que no depende de esta vista si su supervista debe aceptar toques o no.Esta solución es:
pointInside:withEvent:
para devolver un área táctil en particular).Utilizo esto con la suficiente frecuencia que lo he abstraído en una subclase para guardar subclases de vista sin sentido para una anulación. Como beneficio adicional, agregue una propiedad para que sea configurable:
Entonces vuélvase loco y use esta vista donde quiera que pueda usar un plano
UIView
. Configuración es tan simple como la creaciónonlyRespondToTouchesInSubviews
deYES
.fuente
Hay varias formas de manejar esto. Mi favorito es anular hitTest: withEvent: en una vista que es una supervista común (tal vez indirectamente) a las vistas en conflicto (parece que las llama A y B). Por ejemplo, algo como esto (aquí A y B son punteros UIView, donde B es el "oculto", que normalmente se ignora):
También puede modificar el
pointInside:withEvent:
método como sugirió gyim. Esto le permite lograr esencialmente el mismo resultado "haciendo un agujero" en A, al menos para los toques.Otro enfoque es el reenvío de eventos, lo que significa anular
touchesBegan:withEvent:
y métodos similares (como,touchesMoved:withEvent:
etc.) para enviar algunos toques a un objeto diferente al que tenían por primera vez. Por ejemplo, en A, podría escribir algo como esto:Sin embargo, ¡ esto no siempre funcionará de la manera esperada! Lo principal es que los controles integrados como UIButton siempre ignorarán los toques reenviados. Debido a esto, el primer enfoque es más confiable.
Hay una buena publicación de blog que explica todo esto con más detalle, junto con un pequeño proyecto de xcode funcional para demostrar las ideas, disponible aquí:
http://bynomial.com/blog/?p=74
fuente
Tienes que configurar
upperView.userInteractionEnabled = NO;
, de lo contrario, la vista superior interceptará los toques.La versión de Interface Builder de esto es una casilla de verificación en la parte inferior del panel Ver atributos llamada "Interacción del usuario habilitada". Desmárquelo y estará listo para comenzar.
fuente
La implementación personalizada de pointInside: withEvent: de hecho parecía el camino a seguir, pero tratar con coordenadas codificadas de forma rígida me parecía extraño. Así que terminé verificando si el CGPoint estaba dentro del botón CGRect usando la función CGRectContainsPoint ():
fuente
Últimamente escribí una clase que me ayudará con eso. Usarlo como una clase personalizada para un
UIButton
oUIView
pasará eventos táctiles que se ejecutaron en un píxel transparente.Esta solución es algo mejor que la respuesta aceptada porque aún puede hacer clic en un
UIButton
que está debajo de un semitransparenteUIView
mientras que la parte no transparente delUIView
aún responderá a los eventos táctiles.Como puede ver en el GIF, el botón Jirafa es un rectángulo simple, pero los eventos táctiles en áreas transparentes se pasan al amarillo
UIButton
debajo.Enlace a la clase
fuente
Supongo que llego un poco tarde a esta fiesta, pero agregaré esta posible solución:
Si usa este código para anular la función estándar hitTest de una UIView personalizada, ignorará SOLAMENTE la vista en sí. Cualquier subvista de esa vista devolverá sus visitas normalmente, y cualquier visita que hubiera ido a la vista misma se pasa a su supervista.
-Ceniza
fuente
[self superview]
. la documentación sobre este método indica "Devuelve el descendiente más lejano del receptor en la jerarquía de vista (incluido él mismo) que contiene un punto especificado" y "Devuelve nulo si el punto está completamente fuera de la jerarquía de vista del receptor". Creo que deberías volvernil
. cuando devuelve nil, el control pasará a la supervista para que verifique si tiene algún acierto o no. así que básicamente hará lo mismo, excepto que devolver la supervista podría romper algo en el futuro.Simplemente riffing en la respuesta aceptada y poner esto aquí para mi referencia. La respuesta aceptada funciona perfectamente. Puede extenderlo de esta manera para permitir que las subvistas de su vista reciban el toque, O pasarlo a cualquier vista detrás de nosotros:
Nota: Ni siquiera tiene que hacer recursividad en el árbol de subvista, porque cada
pointInside:withEvent:
método lo manejará por usted.fuente
Establecer la propiedad userInteraction deshabilitada puede ayudar. P.ej:
(Nota: en el código anterior, 'self' se refiere a una vista)
De esta manera, solo puede mostrar en la vista superior, pero no obtendrá entradas de usuario. Todos esos toques de usuario pasarán por esta vista y la vista inferior responderá por ellos. Usaría esta vista superior para mostrar imágenes transparentes o animarlas.
fuente
Este enfoque es bastante limpio y permite que las subvistas transparentes no reaccionen también a los toques. Simplemente subclase
UIView
y agregue el siguiente método a su implementación:fuente
Mi solución aquí:
Espero que esto ayude
fuente
Hay algo que puede hacer para interceptar el toque en ambas vistas.
Vista superior:
Pero esa es la idea.
fuente
Aquí hay una versión Swift:
fuente
Swift 3
fuente
Nunca he creado una interfaz de usuario completa con el kit de herramientas de la interfaz de usuario, por lo que no tengo mucha experiencia con él. Sin embargo, esto es lo que creo que debería funcionar.
Cada UIView, y esta UIWindow, tiene una propiedad
subviews
, que es un NSArray que contiene todas las subvistas.La primera subvista que agregue a una vista recibirá el índice 0, y el siguiente índice 1 y así sucesivamente. También puede reemplazar
addSubview:
coninsertSubview: atIndex:
oinsertSubview:aboveSubview:
y métodos que pueden determinar la posición de su subvista en la jerarquía.Así que verifique su código para ver qué vista agrega primero a su UIWindow. Ese será 0, el otro será 1.
Ahora, desde una de sus subvistas, para llegar a otra, haría lo siguiente:
¡Avísame si eso funciona para tu caso!
(debajo de este marcador está mi respuesta anterior):
Si las vistas necesitan comunicarse entre sí, deben hacerlo a través de un controlador (es decir, utilizando el popular modelo MVC ).
Cuando crea una nueva vista, puede asegurarse de que se registre con un controlador.
Entonces, la técnica es asegurarse de que sus vistas se registren con un controlador (que puede almacenarlas por nombre o lo que prefiera en un diccionario o matriz). Puede hacer que el controlador le envíe un mensaje, o puede obtener una referencia a la vista y comunicarse con ella directamente.
Si su vista no tiene un enlace al controlador (que puede ser el caso), entonces puede hacer uso de métodos únicos y / o de clase para obtener una referencia a su controlador.
fuente
Creo que la forma correcta es utilizar la cadena de vistas integrada en la jerarquía de vistas. Para sus subvistas que se insertan en la vista principal, no use la UIView genérica, sino la subclase UIView (o una de sus variantes como UIImageView) para hacer MYView: UIView (o cualquier supertipo que desee, como UIImageView). En la implementación de YourView, implemente el método touchesBegan. Este método se invocará cuando se toque esa vista. Todo lo que necesita tener en esa implementación es un método de instancia:
this touchesBegan es una API de respuesta, por lo que no necesita declararla en su interfaz pública o privada; es una de esas API mágicas que debes conocer. Este self.superview enviará la solicitud al viewController. En viewController, implemente este toque Comenzó a manejar el toque.
Tenga en cuenta que la ubicación de los toques (CGPoint) se ajusta automáticamente en relación con la vista abarcadora a medida que rebota en la cadena de jerarquía de vistas.
fuente
Solo quiero publicar esto, porque tuve un problema similar, pasé una cantidad considerable de tiempo tratando de implementar respuestas aquí sin suerte. Lo que terminé haciendo:
e implementando
UIGestureRecognizerDelegate
:La vista inferior era un controlador de navegación con un número de segues y tenía una especie de puerta encima que podía cerrarse con un gesto de panorámica. Todo estaba incrustado en otro VC. Trabajado como un encanto. Espero que esto ayude.
fuente
Implementación de Swift 4 para solución basada en HitTest
fuente
Derivado de la respuesta excelente y en su mayoría infalible de Stuart, y la implementación útil de Segev, aquí hay un paquete de Swift 4 que puede incluir en cualquier proyecto:
Y luego con hitTest:
fuente