Pregunta

Estoy tratando de crear una interfaz similar a un trampolín dentro de mi aplicación. Estoy tratando de usar UIButtons agregados a un UIScrollView. El problema con el que me estoy encontrando es que los botones no pasan ningún toque al UIScrollView: si trato de mover / deslizar y presiono el botón, no se registra para el UIScrollView, pero si cambio el espacio entre botones funcionará. Los botones hacen clic / funcionan si los toco.

¿Hay alguna propiedad o configuración que obligue al botón a enviar los eventos táctiles a su padre (supervista)? ¿Es necesario agregar los botones a otra cosa antes de agregar UIScrollView?

Aquí está mi código:

//init scrolling area
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 480, 480)];
scrollView.contentSize = CGSizeMake(480, 1000);
scrollView.bounces = NO;
scrollView.delaysContentTouches = NO;

//create background image
UIImageView *rowsBackground = [[UIImageView alloc] initWithImage:[self scaleAndRotateImage:[UIImage imageNamed:@"mylongbackground.png"]]];
rowsBackground.userInteractionEnabled = YES;

//create button
UIButton *btn = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
btn.frame = CGRectMake(100, 850, 150, 150);
btn.bounds = CGRectMake(0, 0, 150.0, 150.0);
[btn setImage:[self scaleAndRotateImage:[UIImage imageNamed:@"basicbutton.png"]] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];

//add "stuff" to scrolling area
[scrollView addSubview:rowsBackground];
[scrollView addSubview:btn];

//add scrolling area to cocos2d
//this is just a UIWindow
[[[Director sharedDirector] openGLView] addSubview:scrollView];

//mem-mgmt
[rowsBackground release];
[btn release];
[scrollView release];
¿Fue útil?

Solución

Para que UIScrollView determine la diferencia entre un clic que pasa a su (s) vista (s) de contenido y un toque que se convierte en un deslizamiento o pellizco, necesita retrasar el toque y ver si su dedo se movió durante ese retraso. Al configurar delaysContentTouches en NO en su ejemplo anterior, está evitando que esto suceda. Por lo tanto, la vista de desplazamiento siempre pasa el toque al botón, en lugar de cancelarlo cuando resulta que el usuario está realizando un gesto de deslizamiento. Intente configurar delaysContentTouches en YES .

También podría ser una buena idea, estructuralmente, agregar todas las vistas que se alojarán en su vista de desplazamiento a una vista de contenido común y solo usar esa vista como subvista de la vista de desplazamiento.

Otros consejos

La solución que funcionó para mí incluyó:

  1. Configurando canCancelContentTouches en UIScrollView en YES .
  2. Extender UIScrollView para anular touchShouldCancelInContentView: (UIView *) view para devolver YES cuando view es un < code> UIButton .

De acuerdo con la documentación, touchShouldCancelInContentView devuelve " YES para cancelar más mensajes táctiles para ver, NO para que la vista continúe recibiendo esos mensajes. El valor devuelto predeterminado es YES si view no es un objeto UIControl ; de lo contrario, devuelve NO . "

Dado que UIButton es un UIControl , la extensión es necesaria para que canCancelContentTouches surta efecto y permita el desplazamiento.

Tengo un caso similar al de varios botones en un UIScrollView, y quiero desplazar estos botones. Al principio, subclasifiqué UIScrollView y UIButton. Sin embargo, noté que mi subclase UIScrollView no recibió el evento touchesEnded, así que cambié a solo la subclase UIButton.


@interface MyPhotoButton : UIButton {
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
@end

@implementation MyPhotoButton

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    [self setEnabled:NO];
    [self setSelected:NO];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    [self setEnabled:YES];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    [self setEnabled:YES];
}

@end

UIScrollView maneja muchos eventos por sí mismo. Debe manejar touchchesDidEnd y presionar la prueba de botones dentro del UIScrollView manualmente.

OK, aquí está tu respuesta:

Subclase UIButton. (NOTA: llame a [super ....] al comienzo de cada anulación.

  • Añadir una propiedad. Uno de tipo BOOL (llamado enableToRestore)
  • Añadir una propiedad. Uno de tipo CGPoint (llamado startTouchPosition)
  • en el wakekefromNib y initWithFrame, establezca el enableToRestore en el isEnabled propiedad)
  • Anular " touchBegan: withEvent: " para almacenar el inicio del toque posición.
  • Anular " touchMoved: withEvent: " a verifique si hay horizontal movimiento.
  • En caso afirmativo, configure habilitado en NO y seleccionado a NO.

Código de muestra:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    UITouch *touch = [touches anyObject];

    [super touchesBegan:touches withEvent:event];
    [self setStartTouchPosition:[touch locationInView:self]];
}


//
// Helper Function
//
- (BOOL)isTouchMovingHorizontally:(UITouch *)touch 
{
    CGPoint currentTouchPosition = [touch locationInView:self];
    BOOL      rValue = NO;

    if (fabsf([self startTouchPosition].x - currentTouchPosition.x) >= 2.0) 
    {
        rValue = YES;
    }

    return (rValue);
}

//
// This is called when the finger is moved.  If the result is a left or right
// movement, the button will disable resulting in the UIScrollView being the
// next responder.  The parrent ScrollView will then re-enable the button
// when the finger event is ended of cancelled.
//
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{
    [super touchesMoved:touches withEvent:event];
    if ([self isTouchMovingHorizontally:[touches anyObject]]) 
    {
        [self setEnabled:NO];
        [self setSelected:NO];
    } 
}

Esto activará el UIScrollView.

Subclase UIScrollView. (NOTA: llame a [super ....] al comienzo de cada anulación.

  • Reemplazar ambos " touchesEnded: withEvent: " y " touchCancelled: withEvent: "
  • En la anulación, restablezca todas las subvistas (y sus subvistas) marca habilitada.
  • NOTA: Use una Categoría y agregue el método a UIView:

.

- (void) restoreAllEnables
{
    NSArray   *views = [self subviews];

    for (UIView *aView in views)
    {
        if ([aView respondsToSelector:@selector(restoreEnable)])
        {
            [aView restoreEnable];
        }
    }
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self restoreAllEnables];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    [self restoreAllEnables];
}
  • En la Categoría:

.

-(void) restoreEnable
{
    NSArray   *views = [self subviews];

    if ([self respondsToSelector:@selector(enableToRestore)])
    {
        [self setEnabled:[self enableToRestore]];
    }

    for (UIView *aView in views)
    {
        if ([aView respondsToSelector:@selector(restoreEnable)])
        {
            [aView restoreEnable];
        }
    }
}

EDITAR Nota: nunca obtuve la respuesta 3 para trabajar. Del mismo modo: setDelaysContentTouches: NO (establecido en el controlador de vista o en algún lugar) se debe configurar para obtener mejores resultados en la Respuesta 4. Esto proporciona una respuesta muy rápida a los botones. Establecer setDelaysContentTouches: YES ejerce un grave impacto (150 ms) en el tiempo de respuesta a los botones y hace que no sea posible tocar la luz rápidamente.

Otra forma es:
1. Sustituya el botón por un simple UIView personalizado
2. Ponga la bandera " userInterationEnable = yes; " en el método init
3. En la vista, anule el método UIResponder " touchesEnded " aquí puede activar la acción que necesita como un botón.

En mi experiencia, la primera respuesta, es decir, simplemente configurar delaysContentTouches en YES , no cambia nada con respecto al problema. Los botones aún no entregarán resultados de seguimiento a la vista de desplazamiento. La tercera respuesta es simple y muy útil. Gracias sieroaoj!

Sin embargo, para que la tercera respuesta funcione, también necesita delaysContentTouches establecido en YES . De lo contrario, también se llamará al método touchesEnded para realizar un seguimiento dentro de la vista. Por lo tanto, podría resolver el problema:

  
      
  1. Sustituya el botón por una simple UIView personalizada
  2.   
  3. Ponga la bandera " userInterationEnable = yes; " en el método init
  4.   
  5. En la vista, anule el método UIResponder " touchesEnded " aquí   puedes activar la acción que
  6.   

Cuarto. establezca delaysContentTouches en YES

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top