UIScrollView com UIButtons - como recriar trampolim?
-
19-08-2019 - |
Pergunta
Eu estou tentando criar uma interface de trampolim-como dentro do meu aplicativo. Eu estou tentando usar UIButtons adicionados a um UIScrollView. O problema que eu estou correndo para é com os botões não passar qualquer toques para o UIScrollView - se eu tentar filme / slide e acontecer de imprensa sobre o botão não se inscrever para o UIScrollView, mas se eu apertar o espaço entre botões que vai funcionar. Os botões do mouse / trabalho se eu tocá-los.
Existe uma propriedade ou configuração que força o botão para enviar os eventos de toque para seu pai (superview)? Fazer os botões precisam ser adicionados para outra coisa antes de ser adicionado o UIScrollView?
Aqui está o meu 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];
Solução
Para UIScrollView
para determinar a diferença entre um clique que passa através de seu conteúdo view (s) e um toque que se transforma em um furto ou beliscar, ele precisa atrasar o toque e veja se o seu dedo se moveu durante esse demora. Ao definir delaysContentTouches
para NO
no seu exemplo acima, você está impedindo que isso aconteça. Portanto, a visão de rolagem está sempre passando o toque do botão, em vez de cancelá-lo quando verifica-se que o usuário está executando um gesto de furto. Tente definir delaysContentTouches
para YES
.
Ele também pode ser uma boa ideia, estruturalmente, para adicionar todos os pontos de vista a ser hospedado em seu ponto de vista de rolagem para a exibição de conteúdo comum e só usar que um ponto de vista como subexibição da exibição da rolagem.
Outras dicas
solução que funcionou para mim incluído:
- Configuração
canCancelContentTouches
emUIScrollView
paraYES
. - Estendendo
UIScrollView
paratouchesShouldCancelInContentView:(UIView *)view
substituição paraYES
retorno quandoview
é umUIButton
.
De acordo com a documentação touchesShouldCancelInContentView
retornos "YES
para cancelar mais mensagens Toque para ver, NO
ter vista continuar a receber essas mensagens O valor padrão retornado é YES
se a visão não é um objeto UIControl
;. Caso contrário, retorna NO
."
Desde UIButton
é um UIControl
a extensão é necessária para obter canCancelContentTouches
entrem em vigor que permite a rolagem.
Eu tenho um caso semelhante que um número de botões em um UIScrollView, e eu quero rolar esses botões. No início, eu subclasse tanto UIScrollView e UIButton. No entanto, notei que meu subclasse UIScrollView não recebeu touchesEnded evento, então eu mudei a única subclasse 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 lida com um monte de si eventos. Você precisa lidar com touchesDidEnd e teste de visitas para os botões dentro do UIScrollView manualmente.
OK aqui está sua resposta:
subclasse UIButton. (NOTA:. Chamada [Super ....] no início de cada override
- Adicione uma propriedade. Uma do tipo BOOL (Chamado enableToRestore)
- Adicione uma propriedade. Um dos tipo CGPoint (Chamado startTouchPosition)
- no awakeFromNib e initWithFrame, defina o enableToRestore ao isEnabled propriedade)
- Override "touchesBegan: withEvent:" para armazenar o início do toque posição.
- Override "touchesMoved: withEvent:" para verificar para ver se há horizontal movimento.
- Se SIM, conjunto permitiu NO e selecionado para NO.
Exemplo de código:
- (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];
}
}
Isso irá ativar o UIScrollView.
subclasse UIScrollView. (NOTA:. Chamada [Super ....] no início de cada override
- Substituir ambos "touchesEnded: withEvent:" e "touchesCancelled: withEvent:"
- Na substituição, redefinir todos os subviews (e seus subviews) habilitado bandeira.
- NOTA: Use uma categoria e adicionar o método para 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];
}
- Na categoria:
.
-(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];
}
}
}
EDIT Nota: Eu nunca tive Resposta 3 a trabalho. Da mesma forma: os setDelaysContentTouches: NÃO (definidos no controlador de vista ou em algum lugar) deve ser ajustado para melhores resultados em Resposta 4. Isto fornece resposta muito rápida para os botões. Definir setDelaysContentTouches:. Puts SIM um sério impacto (150ms) no tempo de resposta para os botões e torna leve, rápido não tocando possível
Outra maneira é:
1. Substituto de botão por um simples costume UIView
2. Coloque a bandeira "userInterationEnable = yes;" no método init
3. Na vista substituir o método UIResponder "touchesEnded" aqui você pode desencadear a ação que você precisa, como um botão.
Na minha experiência, a primeira resposta, ou seja, simplesmente definindo delaysContentTouches
para YES
, não muda nada em relação ao problema. Os botões ainda não vai entregar resultados de rastreamento para a exibição de rolagem. A terceira resposta é simples e muito útil. Graças sieroaoj!
No entanto, para a terceira resposta ao trabalho que você set delaysContentTouches
também precisa YES
. Caso contrário, o touchesEnded
método também será chamado para rastrear dentro da visão. Portanto, eu poderia resolver o problema:
- Substitua de botão por um simples costume UIView
- Coloque a bandeira "userInterationEnable = yes;" no método init
- Na vista substituir o método UIResponder "touchesEnded" aqui você pode acionar a ação que você
Em quarto lugar. conjunto delaysContentTouches
para YES