web-dev-qa-db-fra.com

UIButton ne parvient pas à enregistrer correctement le toucher dans la partie inférieure de l'écran de l'iPhone

J'ai une application avec de nombreux boutons différents disposés dans une calculatrice comme un format carré/rectangulaire. Il est en fait extrêmement similaire à la calculatrice iOS par défaut. Il y a environ 6 lignes avec 4 colonnes chacune des boutons.

Problème

Le problème que je rencontre concerne les boutons de la rangée du bas (environ le 10e de l'écran en bas sur un iPhone 4). Ils ne se comportent pas normalement lorsqu'ils sont pressés, en ce sens qu'ils doivent être maintenus enfoncés (pendant environ une seconde à peu près) pour enregistrer un "appui de bouton". Ceci est opposé au robinet court standard. 

Aucun autre bouton en dehors de cette rangée inférieure ne se comporte de cette façon. 

De plus, si ces boutons sont exploités sur leur bord supérieur, ils se comportent normalement et réagissent dès qu'ils sont touchés. Cela me porte à croire que les boutons eux-mêmes ne sont pas les problèmes, mais il y a un problème avec la disposition de mes vues.

Il convient également de noter que ce problème n'est présent que sur les périphériques physiques. Sur le simulateur, les boutons se comportent normalement.

Le contexte

La vue contenant ces boutons n'est pas le contrôleur de vue racine de l'application. Au lieu de cela, il est transféré comme tel (rien d’extraordinaire ici):

[self presentViewController:navController animated:YES completion:nil];

Où self est le contrôleur de vue racine

Le contrôleur de vue avec lequel je rencontre des problèmes est contenu dans un contrôleur de navigation et est présenté de manière modale par le contrôleur de vue racine que vous pouvez voir ci-dessus.

Ce que j'ai essayé jusqu'à présent

  • Activer et désactiver la mise en page automatique: même problème 

  • Réorganisation de la hiérarchie des vues: j'ai déplacé les boutons problématiques au-dessus et derrière toutes les autres vues Avec le même résultat: même problème 

  • Plusieurs périphériques (iPhone 4, 4s, 5): le même problème (bien que les boutons répondent normalement sur les simulateurs 3,5 pouces et 4 pouces)

  • Test d'autres applications (lorsque les boutons de cette région sont enfoncés sur d'autres applications, ils se comportent normalement)

Information additionnelle

  • Tout est présenté dans Interface Builder pour le contrôleur de vue problématique
  • Tous les boutons sont des boutons système avec des réglages standard et sont tous exactement identiques, à part leur texte.
  • Tous les éléments de l'écran (boutons, libellés, etc.) sont des sous-vues de la "vue"
  • Les boutons sont alignés les uns contre les autres et ne doivent pas chevaucher plus d'un ou deux pixels.
  • Les boutons problématiques ont des dimensions: largeur 80 x hauteur 44.
  • Les boutons problématiques sont alignés au bas de l'écran
  • En plus des boutons, il existe un UIImage et plusieurs étiquettes, mais celles-ci se trouvent en haut de l'écran et ne se chevauchent en aucune façon avec les boutons.
27
Iowa15

Cela ressemble à une interaction entre les boutons et le UIScreenEdgePanGestureRecognizer (ou quoi que ce soit d'autre) chargé de détecter que l'utilisateur souhaite faire apparaître le centre de contrôle du système.

Il y a en fait deux problèmes potentiels ici:

  • Il peut y avoir une interaction (c'est-à-dire un conflit) entre la possibilité d'un geste dirigé vers votre application et un geste dirigé vers le système. Si vous avez des outils de reconnaissance de mouvements, vous devrez peut-être utiliser des méthodes de délégation pour établir une médiation entre elles et les outils de reconnaissance de mouvements du système.

  • Il y a un bogue bien établi où un tapotement près du bord de l'écran (c'est-à-dire dans la "zone" de reconnaissance de geste du bord à l'écran) fonctionne mais le bouton se comporte mal physiquement, c'est-à-dire qu'il ne ne regarde pas comme si cela avait été exploité alors que la journalisation le prouve (voir ma réponse ici: https://stackoverflow.com/a/22000692/341994 ).

17
matt

La cause de ce problème est qu’Apple semble placer un Reconnecteur de gestes au bas de l’écran qui retarde tout contact dans une autre vue. Après avoir bricolé avec des dispositifs de reconnaissance de mouvements sur les fenêtres de l’application, j’ai trouvé une solution une sous-classe de UIButton:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    BOOL inside = [super pointInside: point withEvent: event];

    if (inside && !self.isHighlighted && event.type == UIEventTypeTouches)
    {
        self.highlighted = YES;
    }

    return inside;
}

La méthode donnée est appelée bien que touchesBegan: soit appelée différée. Une vérification si la vue est au bas de l'écran peut être appropriée pour éviter tout effet secondaire qui pourrait survenir avec ce correctif.

31
Lukas

réponse de Lukas dans Swift et désélectionner en surbrillance

extension UIButton {

  public override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {

    var inside = super.pointInside(point, withEvent: event)

    if inside != highlighted && event?.type == .Touches {
        highlighted = inside
    }

    return inside

  }

}
7
ar_dsw

Solution Swift 3

extension UIControl {

    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let inside = super.point(inside: point, with: event)
        if inside != isHighlighted && event?.type == .touches {
            isHighlighted = inside
        }
        return inside
    }
}
4
Randy

J'ai écrit une solution complète dans Swift basée sur Luka's answer. Assurez-vous que tous les boutons en conflit sont conformes à cette classe et le problème aura disparu:

class BorderBugFixButton : UIButton {

    override func awakeFromNib() {
        super.awakeFromNib()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "unHighlight", name: UIApplicationWillResignActiveNotification, object: nil)
    }

    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        let inside = super.pointInside(point, withEvent: event)
        if inside != highlighted && event?.type == .Touches {
            highlighted = inside
        }
        return inside
    }

    internal func unHighlight() {
        highlighted = false
    }

}

P.S .: Pour ceux qui n'aiment pas les Storyboards/Xib, il suffit de migrer l'implémentation de awakeFromNib() vers init()

4
Danny Bravo

Je me suis souvent heurté à cette situation lors de la mise à jour d'anciens Storyboards vers iOS 7+, généralement lorsque le ViewController en question a une forme UIScrollView. Vérifiez deux fois ces 2 paramètres dans votre Storyboard sur l’objet ViewController (pas la vue, celle avec le cercle jaune). Lorsque j'ai décoché la case "Étendre les bords" sous les barres supérieure et inférieure, le cadre du scrollView a été réduit de 64 points (hauteur des barres de navigation et d'état).

UIButton not working on bottom of screen Après avoir remis à 0 l'espace entre NavBar.bottom et scrollView.top, le bouton a commencé à fonctionner. Cela est peut-être dû au fait que scrollView.frame.bottom était à 64 pixels au-dessus du bas de la fenêtre, les touches dans cette zone ont donc été ignorées car elles étaient techniquement hors du cadre de scrollView mais étaient toujours affichées visuellement pour une raison quelconque.

0
self.name

iOS 9.3, Xcode 7.3

Je suggérerais que vous deviez créer une catégorie pour la classe UIButton qui implémente la réponse de Lukas . Pour des instructions sur la création d'une catégorie, consultez cet article: Comment créer une catégorie dans Xcode 6 ou supérieur?

Donnez-lui un nom approprié avec le signe "+" traditionnel, c'est-à-dire si vous l'appelez "BottomOfScreen", le nom du fichier résultant sera "UIButton + BottomOfScreen".

Si vous utilisez objective-c, vous obtiendrez un fichier * .h et * .m avec la nouvelle catégorie.

* .h

#import <UIKit/UIKit.h>

@interface UIButton (BottomOfScreen)

@end

* .m

#import "UIButton+BottomOfScreen.h"

@implementation UIButton (BottomOfScreen)

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    BOOL inside = [super pointInside:point withEvent:event];

    if (inside && !self.isHighlighted && (event.type == UIEventTypeTouches))
    {
        self.highlighted = true;
    }
    else
    {
        nil;
    }

    return inside;
}

@end
0
serge-k