Comment arrêter l'animation UIButton indésirable lors d'un changement de titre?

211

Dans iOS 7, mes titres UIButton s'animent au mauvais moment - tard. Ce problème n'apparaît pas sur iOS 6. J'utilise simplement:

[self setTitle:text forState:UIControlStateNormal];

Je préférerais que cela se produise instantanément et sans cadre vide. Ce clignotement est particulièrement distrayant et détourne l'attention des autres animations.

exsulto
la source
Nous vivons cela aussi. Je ne sais pas si c'est un bug iOS7 ou quelque chose que nous devrions corriger.
Sway
Essayez, [self.button setHighlighted: NO];
karthika
Merci pour ces idées. J'ai essayé setHighlighted: NON, mais pas de chance là-bas. Je peux réduire le clignotement en plaçant setTitle à l'intérieur: [UIView animateWithDuration: 0.0f animations: ^ {...}];
exsulto
1
Vous pouvez utiliser cette solution de contournement dans certains cas: self.button.titleLabel.text = text. Mais cela ne redimensionne pas le cadre d'étiquette et ne fonctionne pas correctement avec UIControlStates
zxcat
C'est une solution de contournement intelligente. Je vais jouer avec cela et voir ce qui se passe, malheureusement j'utilise UIControlStates.
exsulto

Réponses:

165

Cela fonctionne pour les boutons personnalisés:

[UIView setAnimationsEnabled:NO];
[_button setTitle:@"title" forState:UIControlStateNormal];
[UIView setAnimationsEnabled:YES];

Pour les boutons système, vous devez ajouter ceci avant de réactiver les animations (merci @Klaas):

[_button layoutIfNeeded];
Jacob K
la source
12
Malheureusement, cela ne semble pas fonctionner. PerformWithoutAnimation non plus
Sway
9
Ok, donc le correctif qui a fonctionné à la fin était de laisser le texte UIButton d'origine vide afin que lorsque je le définissais avec du code, il ne déclenche pas l'animation.
Sway
27
Cela ne fonctionne que si vous définissez le type du bouton sur personnalisé, conformément à cette réponse stackoverflow.com/a/20718467/62 .
Liron Yahdav
15
Depuis iOS 7.1, je devais ajouter[_button layoutIfNeeded];
Klaas
6
@LironYahdav si le type de bouton est défini sur UIButtonTypeCustom, cette réponse n'est pas requise.
DonnaLea
262

Utilisez la performWithoutAnimation:méthode, puis forcez la mise en page à se produire immédiatement au lieu de plus tard.

[UIView performWithoutAnimation:^{
  [self.myButton setTitle:text forState:UIControlStateNormal];
  [self.myButton layoutIfNeeded];
}];
Bonhomme de neige
la source
11
Cela fonctionne aussi bien que la réponse acceptée, mais semble plus agréable car il est plus encapsulé - il est impossible d'oublier d'ajouter [UIView setAnimationsEnabled: YES], ou pour qu'il soit supprimé sur la piste.
siburb
19
Cela fonctionne pour les boutons système si vous appelez [button layoutIfNeeded];à l'intérieur du bloc.
Alexandre Blin
1
BTW, pour les boutons système, layoutIfNeed doit être appelé après la modification du texte
Yon
C'est la meilleure solution! Vive
sachadso
C'est correct pour moi. Il est le plus voté et à la 6ème place. Nice ...
Solgar
80

Changez le type de bouton en générateur d'interface de formulaire personnalisé.

enter image description here

Cela a fonctionné pour moi.

Christos Chadjikyriacou
la source
7
Meilleure solution! Je vous remercie.
Thomás Calmon
3
Mais cela désactive également l'animation sur le bouton de clic. Je veux seulement désactiver l'animation lors de l'affichage du bouton.
Piotr Wasilewicz
Cela fonctionne si vous ne vous souciez pas de l'animation lorsque vous appuyez sur le bouton.
Joaquin Pereira
J'ai eu quelques boutons définis comme ceci et, évidemment, c'est la réponse la plus élégante pour mon cas. Bien, merci!
Zoltán
79

Dans Swift, vous pouvez utiliser:

UIView.performWithoutAnimation {
    self.someButtonButton.setTitle(newTitle, forState: .normal)
    self.someButtonButton.layoutIfNeeded()
}
Paulw11
la source
1
C'était de loin la méthode la plus simple. Et merci d'avoir inclus une réponse rapide btw
simplexity
1
Meilleure réponse pour Swift!
Nubaslon
Il y avait un bug ennuyeux où changer un titre UIButton en dehors de l'écran provoquerait des timings d'animation étranges avec interactivePopGestureRecognizer et cela l'a résolu. Je pense toujours que c'est un bug avec le système d'exploitation
SparkyRobinson
Étrange que .layoutIfNeeded () doive être appelé, mais je l'ai testé dans les deux sens dans Swift 5 et il s'anime définitivement sans lui.
wildcat12
2
Pas vraiment. Si vous n'appelez pas, layoutIfNeeded()le bouton est signalé comme devant être redessiné, mais cela ne se produira pas avant la prochaine passe de mise en page, qui sera en dehors deperformWithoutAnimation
Paulw11
60

Notez s'il vous plaît :

lorsque " buttonType " de _button est "UIButtonTypeSystem" , le code ci-dessous n'est pas valide :

[UIView setAnimationsEnabled:NO];
[_button setTitle:@"title" forState:UIControlStateNormal];
[UIView setAnimationsEnabled:YES];

lorsque " buttonType " de _button est "UIButtonTypeCustom" , le code ci-dessus est valide .

shede333
la source
Je ne peux pas croire combien de temps j'ai passé avant de comprendre que vous avez juste besoin de changer le type de bouton ... ugh ...
Sandy Chapman
Fonctionne sans aucun code. Modifiez uniquement le type de boutons et cela fonctionnera.
Alex Motor
52

À partir d'iOS 7.1, la seule solution qui fonctionnait pour moi était d'initialiser le bouton avec le type UIButtonTypeCustom.

Norbert
la source
Il s'agit de l'approche la plus judicieuse pour quiconque ne nécessite pas UIButtonTypeSystem.
DonnaLea
Cela a fini par fonctionner le mieux pour moi, je viens de créer un bouton PERSONNALISÉ et de le faire ressembler et surligner comme un bouton système. Je vois à peine la différence mais vous n'avez pas ce délai.
Travis M.
18

donc je trouve une solution efficace:

_logoutButton.titleLabel.text = NSLocalizedString(@"Logout",);
[_logoutButton setTitle:_logoutButton.titleLabel.text forState:UIControlStateNormal];

au début, nous changeons le titre du bouton, puis redimensionnons le bouton pour ce titre

dubenko
la source
1
J'utilise la même solution de contournement. La réponse acceptée ne fonctionne pas pour moi.
deej
Cela provoque le titre à clignoter deux fois, au moins avec iOS 8.
Jordan H
1
Cela fonctionne pour moi en 7.1 et 8.1 sans clignoter. Simple et efficace.
Todd
Fonctionne parfaitement dans iOS 11, même si j'ai dû réutiliser la même chaîne pour la deuxième ligne (l'utilisation du titre de l'étiquette du bouton l'a fait clignoter).
SilverWolf
13

Définissez le type de bouton sur UIButtonTypeCustom et il cessera de clignoter

arunjos007
la source
Comment toutes ces "solutions" de contournement peuvent-elles avoir autant de votes positifs alors que cette réponse simple doit résoudre ce problème 99% du temps ...
Rob
12

Swift 5

myButton.titleLabel?.text = "title"
myButton.setTitle("title", for: .normal)
Carter Randall
la source
Je ne sais pas pourquoi cela fonctionne, mais c'est le cas et c'est la solution la plus propre. UIKit est bizarre.
Nathan Hosselton
11

J'ai fait une extension Swift pour ce faire:

extension UIButton {
    func setTitleWithoutAnimation(title: String?) {
        UIView.setAnimationsEnabled(false)

        setTitle(title, forState: .Normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}

Fonctionne pour moi sur iOS 8 et 9, avec UIButtonTypeSystem.

(Le code est pour Swift 2, Swift 3 et Objective-C devrait être similaire)

Xhacker Liu
la source
Je ne vais pas l'utiliser maintenant mais très pratique à avoir autour!
Francis Reynolds
9

Définissez le type UIButton sur Personnalisé. Cela devrait supprimer les animations de fondu d'entrée et de sortie.

Amit Tandel
la source
1
Cela devrait avoir plus de votes positifs! Fonctionne parfaitement et désactive l'animation à la racine, au lieu de ces autres solutions de contournement.
Jesper Schläger
7

Habituellement, le simple fait de définir le type de bouton sur Personnalisé fonctionne pour moi, mais pour d'autres raisons, je devais sous-classer UIButton et rétablir le type de bouton par défaut (Système), de sorte que le clignotement réapparaissait.

Le réglage UIView.setAnimationsEnabled(false)avant de changer le titre, puis la valeur true après cela n'a pas évité de clignoter pour moi, que j'appelle self.layoutIfNeeded()ou non.

Cela, et seulement cela dans l'ordre exact suivant, a fonctionné pour moi avec iOS 9 et 10 bêta:

1) Créez une sous-classe pour UIButton (n'oubliez pas de définir également la classe personnalisée pour le bouton dans le Storyboard).

2) Remplacez setTitle:forState:comme suit:

override func setTitle(title: String?, forState state: UIControlState) {

    UIView.performWithoutAnimation({

        super.setTitle(title, forState: state)

        self.layoutIfNeeded()
    })
}

Dans Interface Builder, vous pouvez laisser le type de bouton à Système, pas besoin de le changer en Type personnalisé pour que cette approche fonctionne.

J'espère que cela aide quelqu'un d'autre, j'ai lutté si longtemps avec les boutons clignotants ennuyeux que j'espère éviter aux autres;)

cdf1982
la source
N'oubliez pas layoutIfNeeded():]
Tai Le
6

Vous pouvez simplement créer un bouton personnalisé et il cessera d'animer tout en changeant le titre.

        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        [btn setTitle:@"the title" forState:UIControlStateNormal];

vous pouvez également le faire dans la case Storyboard: sélectionnez le bouton dans le storyboard -> sélectionnez l'inspecteur d'attributs (quatrième à gauche) -> dans le menu déroulant 'Type', sélectionnez 'Personnalisé' au lieu de 'Système' qui a probablement été sélectionné .

Bonne chance!

Mendy
la source
3

Vous pouvez supprimer les animations du calque de l'étiquette de titre:

    [[[theButton titleLabel] layer] removeAllAnimations];
Jacksonh
la source
J'ai parcouru toutes les réponses. Celui-ci est le meilleur.
Rudolf Adamkovič
2
Il clignote toujours mais c'est mieux.
Lucien
CECI DEVRAIT ÊTRE LA RÉPONSE.
mxcl
3

Version Swift 4 de la réponse de Xhacker Liu

import Foundation
import UIKit
extension UIButton {
    func setTitleWithOutAnimation(title: String?) {
        UIView.setAnimationsEnabled(false)

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
} 
faraz khonsari
la source
1

UIButton avec systemtype a une animation implicite activée setTitle(_:for:). Vous pouvez le corriger de deux manières différentes:

  1. Définissez le type de bouton sur custom, à partir du code ou d'Interface Builder:
let button = UIButton(type: .custom)

entrez la description de l'image ici

  1. Désactiver l'animation du code:
UIView.performWithoutAnimation {
    button.setTitle(title, for: .normal)
    button.layoutIfNeeded()
}
sgl0v
la source
0

J'ai trouvé que cette solution de contournement fonctionne également avec UIButtonTypeSystem mais ne fonctionnera que si le bouton est activé pour une raison quelconque.

[UIView setAnimationsEnabled:NO];
[_button setTitle:@"title" forState:UIControlStateNormal];
[UIView setAnimationsEnabled:YES];

Vous devrez donc les ajouter si vous avez besoin que le bouton soit désactivé lors de la définition de son titre.

[UIView setAnimationsEnabled:NO];
_button.enabled = YES;
[_button setTitle:@"title" forState:UIControlStateNormal];
_button.enabled = NO;
[UIView setAnimationsEnabled:YES];

(iOS 7, Xcode 5)

sCha
la source
Je viens de confirmer que cette solution de contournement ne fonctionne plus sur iOS 7.1.
sCha
ne supposez pas que vous avez trouvé une solution pour 7.1?
George Green
@GeorgeGreen n'a trouvé aucune solution de travail pour UIButtonTypeSystem . J'ai dû utiliser UIButtonTypeCustom .
sCha
Depuis la version 7.1, vous devrez appliquer les changements de titre à tous les états, le définir uniquement pour l'état normal ne s'applique plus. [_button setTitle:@"title" forState:UIControlStateDisabled]
Sam
0

La combinaison des bonnes réponses ci-dessus entraîne la solution de contournement suivante pour UIButtonTypeSystem :

if (_button.enabled)
{
    [UIView setAnimationsEnabled:NO];
    [_button setTitle:@"title" forState:UIControlStateNormal];
    [UIView setAnimationsEnabled:YES];
}
else // disabled
{
    [UIView setAnimationsEnabled:NO];
    _button.enabled = YES;
    [_button setTitle:@"title" forState:UIControlStateNormal];
    _button.enabled = NO;
    [UIView setAnimationsEnabled:YES];
}
AppsolutEinfach
la source
0

J'ai eu le vilain problème d'animation lors de la modification des titres des boutons dans les contrôleurs de vue dans un UITabBarController. Les titres qui étaient initialement définis dans le story-board sont apparus pendant un court moment avant de disparaître dans leurs nouvelles valeurs.

Je voulais parcourir toutes les sous-vues et utiliser les titres des boutons comme clés pour obtenir leurs valeurs localisées avec NSLocalizedString, telles que;

for(UIView *v in view.subviews) {

    if ([v isKindOfClass:[UIButton class]]) {
        UIButton *btn = (UIButton*)v;
        NSString *newTitle = NSLocalizedString(btn.titleLabel.text, nil);
        [btn setTitle:newTitle];
    }

}

J'ai découvert que ce qui déclenche l'animation est vraiment l'appel à btn.titleLabel.text. Donc, pour continuer à utiliser les storyboards et à localiser dynamiquement les composants comme celui-ci, je m'assure de définir l'ID de restauration de chaque bouton (dans Identity Inspector) sur le même que le titre et de l'utiliser comme clé au lieu du titre;

for(UIView *v in view.subviews) {

    if ([v isKindOfClass:[UIButton class]]) {
        UIButton *btn = (UIButton*)v;
        NSString *newTitle = NSLocalizedString(btn.restorationIdentifier, nil);
        [btn setTitle:newTitle];
    }

}

Pas idéal, mais fonctionne ..

Michael
la source
0

Vous pouvez réellement définir le titre en dehors d'un bloc d'animation, assurez-vous juste d'appeler à l' layoutIfNeeded()intérieur d'un performWithoutAnimation:

button1.setTitle("abc", forState: .Normal)
button2.setTitle("abc", forState: .Normal)
button3.setTitle("abc", forState: .Normal)
UIView.performWithoutAnimation {
    self.button1.layoutIfNeeded()
    self.button2.layoutIfNeeded()
    self.button3.layoutIfNeeded()
}

Si vous avez un tas de boutons, pensez simplement à appeler layoutIfNeeded()la super vue:

button1.setTitle("abc", forState: .Normal)
button2.setTitle("abc", forState: .Normal)
button3.setTitle("abc", forState: .Normal)
UIView.performWithoutAnimation {
    self.view.layoutIfNeeded()
}
Sensé
la source
0

L'extension Xhacker Liu convertie en Swift 3:

extension UIButton {
    func setTitleWithoutAnimation(title: String?) {
        UIView.setAnimationsEnabled(false)

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}
Paweł
la source
-1

Peut-être que générer 2 animations et 2 boutons est une meilleure solution, pour éviter le problème qui apparaît avec l'animation et la modification du texte d'un bouton?

J'ai créé un deuxième uibutton et généré 2 animations, cette solution fonctionne sans hickups.

    _button2.hidden = TRUE;
    _button1.hidden = FALSE;

    CGPoint startLocation = CGPointMake(_button1.center.x, button1.center.y - 70);
    CGPoint stopLocation  = CGPointMake(_button2.center.x, button2.center.y- 70);


    [UIView animateWithDuration:0.3 animations:^{ _button2.center = stopLocation;} completion:^(BOOL finished){_button2.center = stopLocation;}];
    [UIView animateWithDuration:0.3 animations:^{ _button1.center = startLocation;} completion:^(BOOL finished){_button1.center = startLocation;}];
coda
la source
-1

Je l'ai fait fonctionner avec une combinaison de réponses:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

        [button setTitle:@"Title" forState:UIControlStateNormal];

    }];
Jaspe
la source
-1

Une extension pratique pour le changement de titre de bouton animé dans Swift qui joue bien avec l'implémentation par défaut:

import UIKit

extension UIButton {
  /// By default iOS animated the title change, which is not desirable in reusable views
  func setTitle(_ title: String?, for controlState: UIControlState, animated: Bool = true) {
    if animated {
      setTitle(title, for: controlState)
    } else {
      UIView.setAnimationsEnabled(false)
      setTitle(title, for: controlState)
      layoutIfNeeded()
      UIView.setAnimationsEnabled(true)
    }
  }
}
Richard Topchii
la source
@Fogmeister 1. Ma réponse est différente 2. Syntaxe Swift à jour 3. Compatible avec l'API d'Apple pour UIButton.
Richard Topchii