web-dev-qa-db-fra.com

iOS - Événement "Touch Down" retardé pour UIButton dans UITableViewCell

J'ai un UITableViewCell personnalisé, qui est initialisé par ce qui suit:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
        self = [nibArray objectAtIndex:0];

        [self setSelectionStyle:UITableViewCellSelectionStyleNone];

        [self.downButton setBackgroundImage:[UIImage imageNamed:@"button"] forState:UIControlStateNormal];
        [self.downButton setBackgroundImage:[UIImage imageNamed:@"buttonSelected"] forState:UIControlStateHighlighted];
    }
    return self;
}

Le bouton apparaît correctement, avec l'image d'arrière-plan appropriée, mais l'image en surbrillance n'apparaît pas instantanément lorsque le bouton est enfoncé/cliqué. Au lieu de cela, vous devez le maintenir enfoncé pendant une seconde ou deux avant que le changement ne se produise. Le relâchement du bouton, d'autre part, a un instantané retour à l'image d'arrière-plan d'origine.

En essayant d'atténuer le changement tardif lors du passage à l'image en surbrillance, j'ai mis le changement dans la méthode suivante:

- (IBAction)downDown:(id)sender {
    [self.downButton setBackgroundColor:[UIColor redColor]];
}

La méthode ci-dessus est définie pour "Touch Down" (contrairement au "Touch Up Inside" plus commun), et j'ai supprimé le setBackgroundImage:forState: pour l'état en surbrillance. Même problème que mentionné ci-dessus. La couleur ne éventuellement devient rouge, mais seulement après avoir cliqué et maintenu le bouton pendant une seconde ou deux.

J'ai une méthode pour le bouton qui est appelé lorsque "Touch Up Inside" se produit, et cette méthode s'exécute sans problème - que j'appuie rapidement sur le bouton ou que je clique dessus et le maintienne pendant un certain temps avant de le relâcher.

Alors pourquoi le retard pour le "Touch Down" ou UIControlStateHighlighted? J'essaie de fournir une rétroaction instantanée à l'utilisateur pour montrer que le bouton a été enfoncé.

Je peux fournir plus de code si nécessaire, mais ce sont les seuls bits qui ont quelque chose à voir avec l'apparence de l'arrière-plan.

39
Birrel

Cela est dû à la propriété UIScrollViewdelaysContentTouches.

Auparavant, il suffisait de simplement définir cette propriété sur NO pour le UITableView lui-même, mais cela ne fonctionnera que pour les sous-vues de la table qui ne sont pas enfermées dans un autre UIScrollView.

UITableViewCells contient une vue de défilement interne dans iOS 7, vous devrez donc modifier la valeur de cette propriété au niveau des cellules pour toutes les cellules contenant des boutons.

Voici ce que tu dois faire:

1. dans viewDidLoad ou quelque part similaire une fois que votre UITableView a été initialisé, mettez ceci dans:

self.tableView.delaysContentTouches = NO;

2.pour la prise en charge iOS 7, dans la méthode d'initialisation de votre UITableViewCell (initWithStyle:reuseIdentifier: ou initWithCoder: pour les NIB), mettez ceci à la fin:

for (UIView *currentView in self.subviews)
{
    if([currentView isKindOfClass:[UIScrollView class]])
    {
        ((UIScrollView *)currentView).delaysContentTouches = NO;
        break;
    }
}

Ce n'est malheureusement pas une solution permanente à 100% car Apple peut changer la hiérarchie de vue à l'intérieur des cellules à l'avenir (peut-être déplacer la vue de défilement d'une autre couche vers le bas ou quelque chose qui vous obligerait à imbriquer une autre boucle là-dedans), mais jusqu'à ce qu'ils révèlent la classe ou au moins la propriété aux développeurs, c'est le meilleur que nous ayons.

83
Dima

Solution Swift 3 (pour iOS8 +):

Tout d'abord, désactivez les retards dans UITableView après son chargement réussi, par exemple, à l'intérieur de la méthode viewDidLoad():

someTableView.delaysContentTouches = false

Désactivez ensuite les retards dans les vues de défilement contenues dans le UITableView:

for case let scrollView as UIScrollView in someTableView.subviews {
    scrollView.delaysContentTouches = false
}

Remarque pour iOS7: vous devrez peut-être désactiver les retards dans UITableViewCell. (Vérifiez la réponse de Dima).

Vous pouvez également trouver d'autres conseils ici .

26
Nitin Nain

je résous ce problème dans mon code en sous-classant UIButton,

Objectif c

@interface TBButton : UIButton

@end

@implementation TBButton

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.highlighted = true;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.highlighted = false;
    [super touchesEnded:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.highlighted = false;
    [super touchesCancelled:touches withEvent:event];
}

@end

Swift

class TBButton: UIButton {
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        highlighted = true
        super.touchesBegan(touches, withEvent: event)
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        highlighted = false
        super.touchesEnded(touches, withEvent: event)
    }
    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        highlighted = false
        super.touchesCancelled(touches, withEvent: event)
    }
}

Swift 3

class TBButton: UIButton {
    override func touchesBegan(_ touches: Set<UITouch>, with event:     UIEvent?) {
        isHighlighted = true
        super.touchesBegan(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event:     UIEvent?) {
        isHighlighted = false
        super.touchesEnded(touches, with: event)
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        isHighlighted = false
        super.touchesCancelled(touches, with: event)
    }
}
20
Alex

Version Swift 3 de Alex 's réponse :

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    isHighlighted = true
    super.touchesBegan(touches, with: event)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    isHighlighted = false
    super.touchesEnded(touches, with: event)
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    isHighlighted = false
    super.touchesCancelled(touches, with: event)
}
4
Marc

Voici une solution récursive que vous pouvez ajouter à votre contrôleur de vue:

+ (void)disableDelayedTouches:(UIView*)view
{
    for(UIView *currentView in view.subviews) {
        if([currentView isKindOfClass:[UIScrollView class]]) {
            ((UIScrollView *)currentView).delaysContentTouches = NO;
        }
        [self disableDelayedTouches:currentView];
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.class disableDelayedTouches:self.view];
}
2
Sandy Chapman

Alors pourquoi le retard pour le "Touch Down" ou UIControlStateHighlighted? J'essaie de fournir une rétroaction instantanée à l'utilisateur pour montrer que le bouton a été enfoncé.

N'est-ce pas Apple l'explique encore?

Sans ce délai:

  1. Si vous souhaitez faire défiler la vue et que votre doigt touche à quelque chose de surlignable (par exemple, n'importe quelle cellule de tableau avec le style de sélection par défaut), vous obtenez ces artefacts clignotants:

    enter image description here

    Vraiment, ils ne savent pas sur touchDown, si vous allez appuyer ou faire défiler.

  2. Si vous souhaitez faire défiler la vue et que votre doigt touche un UIButton/UITextField, la vue n'est pas du tout défilée ! Le comportement par défaut de Button/TextField est "plus fort" que celui de scrollview, il attend toujours un événement touchUpInside.

1
Dmitry Isaev