web-dev-qa-db-fra.com

UIButton ne montre pas de surbrillance au robinet dans iOS7

J'ai examiné une tonne d'articles sur des sujets similaires, mais aucun ne correspond vraiment à ce problème ni ne le résout. Depuis iOS 7, chaque fois que j'ajoute une UIButton à une UITableViewCell ou même à l'aperçu des pieds, cela fonctionne "bien", c'est-à-dire qu'il reçoit l'action cible, mais il ne montre pas le petit point fort qui se produit normalement lorsque vous appuyez sur une UIButton. L'interface utilisateur a l'air géniale si le bouton ne réagit pas au toucher.

Je suis sûr que cela compte comme un bug dans iOS7, mais quelqu'un a-t-il trouvé une solution ou pourrait-il m'aider à en trouver une :)

Edit: J’ai oublié de mentionner qu’il sera surligné si je tiens longtemps sur le bouton, mais pas comme si de taper rapidement comme cela vient d’être ajouté à une vue standard.

Code:

Création du bouton:

UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.titleLabel.font = [UIFont systemFontOfSize:14];
    button.titleLabel.textColor = [UIColor blueColor];
    [button setTitle:@"Testing" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonPressed:) forControlEvents: UIControlEventTouchDown];
    button.frame = CGRectMake(0, 0, self.view.frame.size.width/2, 40);

Choses que j'ai testées:

// Suppression des identifiants de mouvements sur UITableView au cas où ils gêneraient.

for (UIGestureRecognizer *recognizer in self.tableView.gestureRecognizers) {
   recognizer.enabled = NO;
}

// Suppression des gestes de la cellule

for (UIGestureRecognizer *recognizer in self.contentView.gestureRecognizers) {
       recognizer.enabled = NO;
    }

// Cela montre un léger toucher, mais ce n'est pas le look souhaité

button.showsTouchWhenHighlighted = YES;
68
Eric

Dans cette table, vous ajoutez simplement cette propriété.

tableview.delaysContentTouches = NO;

Et ajoutez cellForRowAtIndexPath après avoir initialisé la cellule que vous venez d'ajouter au code ci-dessous. La structure de la cellule est apparemment différente sous iOS 6 et iOS 7.
iOS 7 nous avons un contrôle UITableViewCellScrollView In entre UITableViewCell et la vue de contenu.

for (id obj in cell.subviews)
{
    if ([NSStringFromClass([obj class]) isEqualToString:@"UITableViewCellScrollView"])
    {
        UIScrollView *scroll = (UIScrollView *) obj;
        scroll.delaysContentTouches = NO;
        break;
    }
}
87
subramani

Depuis iOS 8, nous devons appliquer la même technique aux sous-vues UITableView (la table contient une vue de défilement UITableViewWrapperView masquée). Il n'est plus nécessaire d'itérer les sous-vues UITableViewCell.

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

Cette réponse devrait être liée à cette question.

40
Roman B.

J'ai essayé d'ajouter ceci à la réponse acceptée mais cela n'a jamais abouti. Il s'agit d'un moyen beaucoup plus sûr de désactiver la propriété de delayContentTouches de cellules car elle ne recherche pas une classe spécifique, mais plutôt tout ce qui répond au sélecteur.

En cellule:

for (id obj in self.subviews) {
     if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]) {
          [obj setDelaysContentTouches:NO];
     }
}

Dans TableView:

self.tableView.delaysContentTouches = NO;
27
Eric

Pour une solution fonctionnant à la fois dans iOS7 et iOS8, créez une sous-classe UITableView personnalisée et une sous-classe UITableViewCell personnalisée.

Utilisez cet exemple UITableView 's initWithFrame:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self)
    {
        // iterate over all the UITableView's subviews
        for (id view in self.subviews)
        {
            // looking for a UITableViewWrapperView
            if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"])
            {
                // this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
                if([view isKindOfClass:[UIScrollView class]])
                {
                    // turn OFF delaysContentTouches in the hidden subview
                    UIScrollView *scroll = (UIScrollView *) view;
                    scroll.delaysContentTouches = NO;
                }
                break;
            }
        }
    }
    return self;
}

Utilisez cet exemple UITableViewCell 's initWithStyle:reuseIdentifier:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self)
    {
        // iterate over all the UITableViewCell's subviews
        for (id view in self.subviews)
        {
            // looking for a UITableViewCellScrollView
            if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewCellScrollView"])
            {
                // this test is here for safety only, also there is no UITableViewCellScrollView in iOS8
                if([view isKindOfClass:[UIScrollView class]])
                {
                    // turn OFF delaysContentTouches in the hidden subview
                    UIScrollView *scroll = (UIScrollView *) view;
                    scroll.delaysContentTouches = NO;
                }
                break;
            }
        }
    }

    return self;
}
18
Quentin

Ce que j'ai fait pour résoudre le problème était une catégorie d'UIButton utilisant le code suivant:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];


    [NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = YES; }];
}


- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];

    [self performSelector:@selector(setDefault) withObject:nil afterDelay:0.1];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];

    [self performSelector:@selector(setDefault) withObject:nil afterDelay:0.1];
}


- (void)setDefault
{
    [NSOperationQueue.mainQueue addOperationWithBlock:^{ self.highlighted = NO; }];
}

le bouton réagit correctement lorsque j'appuie dessus dans un UITableViewCell, et mon UITableView se comporte normalement car la delaysContentTouches n'est pas forcée.

16
Raphaël Pinto

Voici la réponse de Roman B dans Swift 2:

for view in tableView.subviews {
    if view is UIScrollView {
        (view as? UIScrollView)!.delaysContentTouches = false
        break
    }
}
10
brandonscript
    - (void)viewDidLoad
{

    [super viewDidLoad];


    for (id view in self.tableView.subviews)
    {
        // looking for a UITableViewWrapperView
        if ([NSStringFromClass([view class]) isEqualToString:@"UITableViewWrapperView"])
        {
            // this test is necessary for safety and because a "UITableViewWrapperView" is NOT a UIScrollView in iOS7
            if([view isKindOfClass:[UIScrollView class]])
            {
                // turn OFF delaysContentTouches in the hidden subview
                UIScrollView *scroll = (UIScrollView *) view;
                scroll.delaysContentTouches = NO;
            }
            break;
        }
    }
}

enter image description here

5
Gank

Je rencontrais des problèmes similaires avec un UIButton textuel dans un UITableViewCell non surligné au toucher. Ce qui a résolu le problème pour moi, c’était de changer le type de bouton de Personnalisé à Système.

La définition de delayContentTouches sur NO a permis de résoudre le problème pour le UIButton image uniquement dans le même UITableViewCell.

self.tableView.delaysContentTouches = NO;

 enter image description here

4
skg

Solution dans Swift, iOS8 uniquement (nécessite un travail supplémentaire sur chacune des cellules pour iOS7):

//
//  NoDelayTableView.Swift
//  DivineBiblePhone
//
//  Created by Chris Hulbert on 30/03/2015.
//  Copyright (c) 2015 Chris Hulbert. All rights reserved.
//
//  This solves the delayed-tap issue on buttons on cells.

import UIKit

class NoDelayTableView: UITableView {

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        delaysContentTouches = false

        // This solves the iOS8 delayed-tap issue.
        // http://stackoverflow.com/questions/19256996/uibutton-not-showing-highlight-on-tap-in-ios7
        for view in subviews {
            if let scroll = view as? UIScrollView {
                scroll.delaysContentTouches = false
            }
        }
    }

    override func touchesShouldCancelInContentView(view: UIView!) -> Bool {
        // So that if you tap and drag, it cancels the tap.
        return true
    }

}

Pour utiliser, tout ce que vous avez à faire est de changer la classe en NoDelayTableView dans votre scénarimage.

Je peux confirmer que dans iOS8, les boutons placés à l'intérieur d'un contentView dans une cellule sont maintenant surlignés instantanément.

3
Chris

Ceci est une version rapide de la réponse de Raphaël Pinto ci-dessus. N'oubliez pas de le voter aussi :)

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    super.touchesBegan(touches, withEvent: event)
    NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = true }
}

override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
    super.touchesCancelled(touches, withEvent: event)
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue()) {
        self.setDefault()
    }
}

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
    super.touchesEnded(touches, withEvent: event)
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue()) {
        self.setDefault()
    }
}

func setDefault() {
    NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in self.highlighted = false }
}
3
Chris Harrison

Version légèrement modifiée de Réponse de Chris Harrison . Swift 2.3:

class HighlightButton: UIButton {
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesBegan(touches, withEvent: event)
        NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = true }
    }

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

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

    private func setDefault() {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
            NSOperationQueue.mainQueue().addOperationWithBlock { _ in self.highlighted = false }
        }
    }
}
2
SoftDesigner

J'ai écrit une extension de catégorie sur UITableViewCell pour rendre ce problème simple à résoudre. Il fait fondamentalement la même chose que la réponse acceptée, sauf que je monte dans la hiérarchie des vues (par opposition à en bas) à partir du UITableViewCell contentView.

J'ai envisagé une solution entièrement "automagique" qui ferait que toutes les cellules ajoutées à une variable UITableView définiraient leur état delaysContentTouches pour correspondre à l'état UITableView de s delaysContentTouches. Pour que cela fonctionne, je devrais soit utiliser la variable UITableView, soit demander au développeur d'utiliser une sous-classe UITableView. Ne voulant pas exiger non plus, j'ai choisi cette solution qui, à mon avis, est plus simple et plus flexible. 

Extension de catégorie et échantillon de harnais ici: 

https://github.com/TomSwift/UITableViewCell-TS_delaysContentTouches

C'est très simple à utiliser:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // using static cells from storyboard...
    UITableViewCell* cell = [super tableView: tableView cellForRowAtIndexPath: indexPath];

    cell.ts_delaysContentTouches = NO;

    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

Voici le code pour la catégorie:

@interface UITableViewCell (TS_delaysContentTouches)

@property (nonatomic, assign) BOOL ts_delaysContentTouches;

@end

@implementation UITableViewCell (TS_delaysContentTouches)

- (UIScrollView*) ts_scrollView
{
    id sv = self.contentView.superview;
    while ( ![sv isKindOfClass: [UIScrollView class]] && sv != self )
    {
        sv = [sv superview];
    }

    return sv == self ? nil : sv;
}

- (void) setTs_delaysContentTouches:(BOOL)delaysContentTouches
{
    [self willChangeValueForKey: @"ts_delaysContentTouches"];

    [[self ts_scrollView] setDelaysContentTouches: delaysContentTouches];

    [self didChangeValueForKey: @"ts_delaysContentTouches"];
}

- (BOOL) ts_delaysContentTouches
{
    return [[self ts_scrollView] delaysContentTouches];
}

@end
0
TomSwift

Puisque objc est dynamique et que scrollView est la seule classe qui réponde à delayContentTouches, cela devrait fonctionner pour ios 7 et 8 (mettez-le quelque part au début de votre tableViewController, comme dans awakeFromNib):

for (id view in self.tableView.subviews)
    {
        if ([view respondsToSelector:@selector(delaysContentTouches)]) {
            UIScrollView *scrollView = (UIScrollView *)view;
            scrollView.delaysContentTouches = NO;
            break;
        }
}

Vous devrez peut-être également désactiver "delayContentTouches" dans votre storyboard ou votre plume en sélectionnant la table dans votre viewController. En passant, cela pourrait ne pas fonctionner sur ios 7 si vous utilisez une table dans un viewController, du moins je ne pouvais pas le faire fonctionner.

0
smileBot

La réponse acceptée ne fonctionnait pas à certains "robinets" pour moi.

Enfin, j’ajoute le code ci-dessous dans la catégorie uibutton (/ subclass), et cela fonctionne à cent pour cent.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

self.backgroundColor = [UIColor greenColor];
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    self.backgroundColor = [UIColor clearColor];

} completion:^(BOOL finished)
 {
 }];
[super touchesBegan:touches withEvent:event];

}
0
rotoava

Dans Swift 3, cette extension UIView peut être utilisée sur UITableViewCell. De préférence dans la méthode cellForRowAt.

func removeTouchDelayForSubviews() {
    for subview in subviews {
        if let scrollView = subview as? UIScrollView {
            scrollView.delaysContentTouches = false
        } else {
            subview.removeTouchDelayForSubviews()
        }
    }
}
0
Sunkas

Cette solution pour moi ne fonctionne pas, j'ai corrigé sous-classer TableView et implémenté ces deux méthodes

- (instancetype)initWithCoder:(NSCoder *)coder{
   self = [super initWithCoder:coder];
   if (self) {
     for (id obj in self.subviews) {
      if ([obj respondsToSelector:@selector(setDelaysContentTouches:)]){
            [obj performSelector:@selector(setDelaysContentTouches:) withObject:NO];
      }
     }
   }
   return self;
}

- (BOOL)delaysContentTouches{
   return NO;
}
0
Serluca

Solution dans Swift pour iOS 7 et 8:

J'ai d'abord écrit une fonction utilitaire:

class func classNameAsString(obj: AnyObject) -> String {
    return _stdlib_getDemangledTypeName(obj).componentsSeparatedByString(".").last!
}

puis je sous-classe UITableView et implémenter ceci:

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    for view in self.subviews {
        if (Utility.classNameAsString(view) == "UITableViewWrapperView") {
            if view.isKindOfClass(UIScrollView) {
                var scroll = (view as UIScrollView)
                scroll.delaysContentTouches = false
            }
            break
        }
    }
}

Je sous-classe également UITableViewCell et implémente ceci:

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    for view in self.subviews {
        if (Utility.classNameAsString(view) == "UITableViewCellScrollView") {
            if view.isKindOfClass(UIScrollView) {
                var scroll = (view as UIScrollView)
                scroll.delaysContentTouches = false
            }

        }
    }
}

Dans mon cas, l'init (codeur :) sera exécuté. S'il vous plaît mettez le point de débogage dans vos fonctions init pour savoir quelle fonction init sera exécutée, puis utilisez le code ci-dessus pour le faire fonctionner.

0
grandagile