objective c - Uiscrollview con UIButtons: ¿cómo recrear el trampolín?
objective-c programmatically-created (7)
Intento 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.
¿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];
En mi experiencia, la primera respuesta, es decir, simplemente configurar los 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 que la tercera respuesta funcione, también necesita delaysContentTouches
establecido en YES
. De lo contrario, el método touchesEnded
también se llamará para el seguimiento dentro de la vista. Por lo tanto, podría resolver el problema de la siguiente manera:
- Sustituir botón por una simple UIView personalizada
- Ponga la bandera "userInterationEnable = yes;" en el método init
- En la vista, anula el método UIResponder "touchesEnded" aquí, puede desencadenar la acción que
Cuarto. establecer delaysContentTouches
a YES
La solución que funcionó para mí incluía:
- Estableciendo
canCancelContentTouches
enUIScrollView
enYES
. - Extendiendo
UIScrollView
para anulartouchesShouldCancelInContentView:(UIView *)view
para devolverYES
cuandoview
es unUIButton
.
De acuerdo con la documentación, touchesShouldCancelInContentView
devuelve " YES
" para cancelar otros mensajes táctiles para ver, NO
para que la vista continúe recibiendo esos mensajes. El valor predeterminado devuelto es YES
si la vista no es un objeto UIControl
; de lo contrario, devuelve NO
".
Como UIButton
es un UIControl
la extensión es necesaria para que canCancelContentTouches
surta efecto y permite el desplazamiento.
OK aquí está tu respuesta:
Subclase UIButton. (NOTA: llame [super ....] al inicio de cada anulación.
- Agregar una propiedad. Uno de tipo BOOL (llamado enableToRestore)
- Agregar una propiedad. Uno de tipo CGPoint (llamado startTouchPosition)
- en awakeFromNib e initWithFrame, establezca enableToRestore en la propiedad isEnabled)
- Anule "touchesBegan: withEvent:" para almacenar el inicio de la posición táctil.
- Anule "touchesMoved: withEvent:" para verificar si hay movimiento horizontal.
- Si es SÍ, configure habilitado a 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á UIScrollView.
Subclase UIScrollView. (NOTA: llame [super ....] al inicio de cada anulación.
- Anula los "toquesEntrada: conEvento:" y "tocaCancelado: conEvento:"
- En la anulación, restablezca todas las subvistas (y sus subvistas) habilitado bandera.
- 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 que funcione. Del mismo modo: setDelaysContentTouches: NO (establecido en el controlador de visualización o en algún otro lugar) se debe configurar 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.
Otra forma es:
1. Sustituir 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 "touchesEnded" aquí, puede activar la acción que necesita como un botón.
Para que UIScrollView
determine la diferencia entre un clic que pasa a su vista (s) de contenido y un toque que se convierte en deslizamiento o pellizco, necesita retrasar el toque y ver si su dedo se movió 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. Intenta 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.
Tengo un caso similar que una serie 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
UIScrollView maneja muchos eventos en sí mismo. Debes manejar touchesDidEnd y presionar la tecla de prueba dentro de UIScrollView manualmente.