J'essaie de créer une interface semblable à un tremplin au sein de mon application. J'essaie d'utiliser les boutons UIB ajoutés à un UIScrollView. Le problème que je rencontre est lié au fait que les boutons ne transmettent aucun contact à UIScrollView. Si j'essaie de glisser/glisser et que j'appuie sur le bouton, cela ne sert à rien, mais si je déplace l'espace boutons cela fonctionnera. Les boutons cliquent/fonctionnent si je les touche.
Existe-t-il une propriété ou un paramètre obligeant le bouton à envoyer les événements tactiles à son parent (superview)? Les boutons doivent-ils être ajoutés à quelque chose avant d’être ajoutés à UIScrollView?
Voici mon code:
//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];
Pour que UIScrollView
puisse déterminer la différence entre un clic qui passe dans la ou les vues de contenu et un contact qui se transforme en un glissement ou un pincement, il doit retarder le toucher et voir si votre doigt s'est déplacé pendant ce délai. En définissant delaysContentTouches
sur NO
dans votre exemple ci-dessus, vous empêchez cela de se produire. Par conséquent, la vue de défilement transmet toujours le toucher au bouton au lieu de l’annuler s’il s’avère que l’utilisateur effectue un geste de balayage. Essayez de régler delaysContentTouches
à YES
.
Il peut également être judicieux d'ajouter structurellement toutes les vues à héberger dans votre vue de défilement à une vue de contenu commune et de n'utiliser que cette vue comme sous-vue de la vue de défilement.
La solution qui a fonctionné pour moi inclut:
canCancelContentTouches
dans UIScrollView
à YES
. UIScrollView
pour remplacer touchesShouldCancelInContentView:(UIView *)view
pour renvoyer YES
lorsque view
est une UIButton
. Selon la documentation, touchesShouldCancelInContentView
retourne "YES
pour annuler d'autres messages tactiles à afficher, NO
pour continuer à recevoir ces messages. La valeur renvoyée par défaut est YES
si view n'est pas un objet UIControl
; sinon, il renvoie NO
."
Étant donné que UIButton
est une UIControl
, l'extension est nécessaire pour que canCancelContentTouches
prenne effet, ce qui active le défilement.
J'ai le même cas qu'un certain nombre de boutons sur un UIScrollView et je souhaite les faire défiler. Au début, j'ai sous-classé UIScrollView et UIButton. Cependant, j'ai remarqué que mon sous-groupe UIScrollView ne recevait pas l'événement touchesEnded. J'ai donc changé pour un sous-type 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 gère un grand nombre d'événements lui-même. Vous devez gérer touchesDidEnd et cliquer sur test pour les boutons à l'intérieur de UIScrollView manuellement.
OK voici ta réponse:
Sous-classe UIButton. (REMARQUE: appelez [super ....] au début de chaque substitution.
Exemple de code:
- (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];
}
}
Cela activera UIScrollView.
Sous-classe UIScrollView. (REMARQUE: appelez [super ....] au début de chaque substitution.
.
- (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];
}
.
-(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 Remarque: Je n’ai jamais obtenu la réponse 3 au travail . boutons. La définition de setDelaysContentTouches: YES a un impact important (150 ms) sur le temps de réponse des boutons et rend impossible tout contact léger et rapide.
Selon mon expérience, la première réponse, c’est-à-dire le simple fait de placer delaysContentTouches
à YES
, ne change rien au problème. Les boutons ne transmettent toujours pas les résultats du suivi à la vue de défilement. La troisième réponse est à la fois simple et très utilisable. Merci sieroaoj!
Cependant, pour que la troisième réponse fonctionne, vous devez également définir delaysContentTouches
sur YES
. Sinon, la méthode touchesEnded
sera également appelée pour le suivi dans la vue. Par conséquent, je pourrais résoudre le problème en:
- Remplacez le bouton par un simple personnalisé UIView
- Placez le drapeau "userInterationEnable = yes;" sur la méthode init
- Dans la vue, remplacez la méthode UIResponder "touchesEnded" ici vous pouvez déclencher l'action que vous
Quatrième. définir delaysContentTouches
à YES
Une autre façon est:
1. Remplacer le bouton par un simple UIView personnalisé
2. Placez le drapeau "userInterationEnable = yes;" sur la méthode init
3. Dans la vue, remplacez la méthode UIResponder "touchesEnded". Vous pouvez alors déclencher l'action souhaitée sous forme de bouton.