ios - UIView. ¿Por qué una subvistas fuera de la extensión de sus padres no recibe toques?
hittest (5)
- @dugla, gracias por la pregunta!
- @Bastian y @laoyur, gracias a ti por las respuestas!
Swift 4
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if yourChildView.point(inside: convert(point, to: yourChildView), with: event) {
return true
}
return super.point(inside: point, with: event)
}
Tengo una jerarquía simple / trivial de UIView padre / hijo. Uno de los padres (UIView). Un niño (UIButton). Los límites de los padres son más pequeños que sus límites, por lo que una parte del niño se extiende más allá del cuadro delimitador de su padre.
Aquí está el problema: aquellas partes del niño fuera de la bbox del padre no reciben toques. Solo tocar dentro de la bbox del padre permite que el botón secundario reciba toques.
¿Alguien puede sugerir una solución / solución alternativa?
ACTUALIZAR
Para aquellos que siguen esta pregunta, aquí está la solución que implementé como resultado de la respuesta más excelente de @Bastians:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL isInside = [super pointInside:point withEvent:event];
// identify the button view subclass
UIButton *b = (UIButton *)[self viewWithTag:3232];
CGPoint inButtonSpace = [self convertPoint:point toView:b];
BOOL isInsideButton = [b pointInside:inButtonSpace withEvent:nil];
if (isInsideButton) {
return isInsideButton;
} // if (YES == isInsideButton)
return isInside;
}
De acuerdo con la propia documentación de Apple , la manera más simple y confiable que he encontrado para hacer esto es anulando hitTest:withEvent:
en la superclase de su vista recortada para que se vea como la siguiente:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// Convert the point to the target view''s coordinate system.
// The target view isn''t necessarily the immediate subview
CGPoint pointForTargetView = [self.targetView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.targetView.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return [self.targetView hitTest:pointForTargetView withEvent:event];
}
return [super hitTest:point withEvent:event];
}
El problema es la cadena de respuesta. Cuando tocas la pantalla, pasará de los padres al childen.
Entonces ... cuando toque la pantalla, el padre verá que el toque está fuera de sus propios límites y por eso los niños ni siquiera lo preguntarán.
La función que hace eso es el hitTest. Si tiene su propia clase UIView, puede sobrescribirla y devolver el botón usted mismo.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
Tenía el mismo problema exacto a mano. Solo necesita anular:
- (BOOL) pointInside: (CGPoint) point withEvent: (UIEvent *) evento
Aquí está el código de trabajo para una subclase UIView personalizada creada únicamente para que pueda tocarse para sus hijos fuera de límites.
@implementation ARQViewToRightmost
// We should not need to override this one !!!
/*-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self isInside:point])
return self;
return nil;
}*/
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self isInside:point])
return YES; // That answer will make hitTest continue recursively probing the view''s subviews for hit
return NO;
}
// Check to see if the point is within the bounds of the view. We''ve extended the bounds to the max right
// Make sure you do not have any other sibling UIViews to the right of this one as they will never receive events
- (BOOL) isInside:(CGPoint)point
{
CGFloat maxWidth = CGFLOAT_MAX;
CGRect rect = CGRectMake(0, 0, maxWidth, self.frame.size.height);
if (CGRectContainsPoint(rect, point))
return YES;
return NO;
}
@end
Condición previa :
Usted tiene un UIButton
(llamado como button1) dentro de un UIView
(nombrado como contenedor), y button1 está parcialmente fuera de los límites del contenedor.
Problema :
la parte que está fuera del contenedor de button1 no dará clic de respuesta.
Solución :
subclass su contenedor de UIView:
class Container: UIView {
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let closeButton = viewWithTag(10086) as! UIButton //<-- note the tag value
if closeButton.pointInside(convertPoint(point, toView: closeButton), withEvent: event) {
return true
}
return super.pointInside(point, withEvent: event)
}
}
No olvides darle a tu button1 una etiqueta de 10086