34

Estoy tratando de crear una interfaz tipo trampolín dentro de mi aplicación. Estoy intentando usar UIButtons agregados a UIScrollView. El problema al que me estoy enfrentando es con los botones que no pasan ningún toque al UIScrollView; si intento deslizar/deslizar y presiono el botón, no se registra en UIScrollView, pero si deslizo el espacio entre botones funcionará. Los botones hacen clic/trabajar si los toco.Uiscrollview con UIButtons: ¿cómo recrear el trampolín?

¿Hay alguna propiedad o configuración que obligue al botón a enviar los eventos táctiles hasta su elemento principal (supervista)? ¿Los botones deben agregarse a otra cosa antes de agregarse 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]; 
+0

Hay que señalar que, a pesar de que Apple ha permitido que antes, '' 10.4' Aplicaciones alternativas que crean entornos de imágenes de escritorio/casa o simulan experiencias de widgets multi-aplicación será RECHAZADO. Solo en caso de que algunos recién llegados planeen enviar una aplicación de producción que tenga su propio trampolín. – user

Respuesta

28

Para que UIScrollView determine la diferencia entre un clic que pasa por su vista de contenido y un toque que se convierte en deslizamiento o pellizco, necesita retrasar el toque y ver si su dedo se ha movido durante ese retraso. Al configurar delaysContentTouches en NO en el ejemplo anterior, impide 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 usar solo esa vista como subvista de la vista de desplazamiento.

+3

Un posible problema con la demora en el conteo es que agregará un retraso antes de que un botón se resalte. La respuesta de Roman K a continuación le permitirá no tener demoras, pero aún podrá desplazarse después de presionar un botón. – GhostM

+0

El consejo de estructura es increíble, pero me interesaría saber por qué. Gracias. –

1

UIScrollView maneja una gran cantidad de eventos en sí. Debes manejar touchesDidEnd y presionar la tecla de prueba dentro de UIScrollView manualmente.

0

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

0

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

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

  1. Sustituto de botón por un simple encargo UIView
  2. Ponga la bandera "userInterationEnable = yes;" en el método init
  3. En la vista de reemplazar el método UIResponder "touchesEnded" aquí puede desencadenar la acción que

Cuarto. establecer delaysContentTouches-YES

+0

delatrasContentTouches = SÍ de forma predeterminada. – Andy

1

bien aquí es su respuesta:

Subclase UIButton. (NOTA: llame [super ....] al inicio 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 awakeFromNib y initWithFrame, establezca la enableToRestore a la isEnabled propiedad)
  • Override "touchesBegan: withEvent:" para almacenar el inicio de la posición de contacto.
  • Anular "toquesMovido: conEvento:" a compruebe si hay movimiento horizontal .
  • En caso afirmativo, configure habilitado a NO y seleccionado a NO.

Código de ejemplo:

- (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 al [súper ....] al inicio de cada anulación

  • de anulación tanto "touchesEnded: withEvent:". Y "touchesCancelled: withEvent:"
  • En la anulación, restablecer todos subvistas (y sus subvistas) permitieron la bandera
  • NOTA:. Usar una categoría y añadir el método de 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 llegué Respuesta 3 al trabajo. Del mismo modo: setDelaysContentTouches: NO (configurado en el controlador de vista o en algún otro lugar) se debe establecer para obtener mejores resultados en la Respuesta 4. Esto proporciona una respuesta muy rápida a los botones. Configuración de setDelaysContentTouches: YES tiene un impacto grave (150 ms) en el tiempo de respuesta a los botones y no permite un toque ligero y rápido.

5

Tengo un caso similar que un número de botones en un UIScrollView, y quiero desplazar estos botones. Al principio, subclasé UIScrollView y UIButton. Sin embargo, noté que mi UIScrollView subclasificado no recibió el evento touchesEnded, así que cambié a 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 
+0

Esta solución funcionó perfectamente para mí. – GroovyCarrot

44

solución que funcionó para mí incluye:

  1. Configuración canCancelContentTouches en UIScrollView-YES.
  2. Extendiendo UIScrollView para anular touchesShouldCancelInContentView:(UIView *)view para devolver YES cuando view es un UIButton. .

Según la documentación touchesShouldCancelInContentView devuelve "YES para cancelar más mensajes táctiles para ver, NO tener vista siguen recibiendo esos mensajes El valor predeterminado regresaron valor es YES si la vista no es un objeto UIControl, de lo contrario, devuelve NO "

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

+8

¡Muchas gracias! Para aclarar, esto funciona mejor cuando retraysContentTouches = NO. – Schrockwell

+7

¡Este es el camino a seguir! (al menos para iOS 4+) Encontré que es más versátil para sobrescribir de esta manera: '- (BOOL) touchesShouldCancelInContentView: (UIView *) view { return! [view isKindOfClass: [UISlider class]]; } ' –

+0

Todavía funciona perfectamente en iOS 7. Muchas gracias !! – RyanG

Cuestiones relacionadas