¿Cómo detener la animación UIButton no deseada en el cambio de título?

211

En iOS 7, mis títulos de UIButton están entrando y saliendo en el momento equivocado, tarde. Este problema no aparece en iOS 6. Solo estoy usando:

[self setTitle:text forState:UIControlStateNormal];

Preferiría que esto ocurra al instante y sin un marco en blanco. Este parpadeo es especialmente molesto y desvía la atención de otras animaciones.

exsulto
fuente
Estamos experimentando esto también. No estoy seguro si es un error de iOS7 o algo que deberíamos solucionar.
Sway
Intente, [self.button setHighlighted: NO];
karthika
Gracias por estas ideas. Intenté setHighlighted: NO, pero no tuve suerte allí. Puedo reducir el parpadeo colocando setTitle dentro: [UIView animateWithDuration: 0.0f animaciones: ^ {...}];
exsulto
1
Puede utilizar esta solución en algunos casos: self.button.titleLabel.text = text. Pero esto no cambia el tamaño del marco de la etiqueta y no funciona con UIControlStates correctamente
zxcat
Esa es una solución inteligente. Jugaré con esto y veré qué sucede, desafortunadamente estoy usando UIControlStates.
exsulto

Respuestas:

165

Esto funciona para botones personalizados:

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

Para los botones del sistema, debe agregar esto antes de volver a habilitar las animaciones (gracias @Klaas):

[_button layoutIfNeeded];
Jacob K
fuente
12
Lamentablemente, esto no parece funcionar. Ninguno de los dos se presenta sin animación
Sway
9
Ok, entonces la solución que funcionó al final fue dejar el texto original de UIButton en blanco para que cuando lo configure con código no active la animación.
Sway
27
Esto funciona solo si configura el tipo de botón como personalizado, según esta respuesta stackoverflow.com/a/20718467/62 .
Liron Yahdav
15
A partir de iOS 7.1 tuve que agregar[_button layoutIfNeeded];
Klaas
66
@LironYahdav si tiene el tipo de botón configurado en UIButtonTypeCustom, entonces esta respuesta no es necesaria.
DonnaLea
262

Use el performWithoutAnimation:método y luego fuerce el diseño para que ocurra inmediatamente en lugar de más adelante.

[UIView performWithoutAnimation:^{
  [self.myButton setTitle:text forState:UIControlStateNormal];
  [self.myButton layoutIfNeeded];
}];
Monigote de nieve
fuente
11
Esto funciona tan bien como la respuesta aceptada, pero parece más agradable porque está más encapsulado: es imposible olvidar agregar el [UIView setAnimationsEnabled: YES], o que se elimine por el camino.
siburb
19
Funciona para los botones del sistema si llamas [button layoutIfNeeded];dentro del bloque.
Alexandre Blin
1
Por cierto, para los botones del sistema, debería llamarse layoutIfNeed después de cambiar el texto
Yon
¡Esta es la mejor solución! Saludos
sachadso
Este es el correcto para mí. Es el más votado y en el sexto lugar. Bonito ...
Solgar
79

Cambie el tipo de botón a un generador de interfaz de formulario personalizado.

ingrese la descripción de la imagen aquí

Esto funcionó para mí.

Christos Chadjikyriacou
fuente
66
¡Mejor solución! Gracias.
Thomás Calmon
3
Pero esto también deshabilita la animación al hacer clic en el botón. Solo quiero desactivar la animación al mostrar el botón.
Piotr Wasilewicz
Esto funciona si no te importa la animación cuando se toca el botón.
Joaquín Pereira
He configurado un par de botones como este y obviamente esta es la respuesta más elegante para mi caso. ¡Genial gracias!
Zoltán
79

En Swift puedes usar:

UIView.performWithoutAnimation {
    self.someButtonButton.setTitle(newTitle, forState: .normal)
    self.someButtonButton.layoutIfNeeded()
}
Paulw11
fuente
1
Este fue, con mucho, el método más fácil. Y gracias por incluir una respuesta rápida por cierto
simplexity
1
La mejor respuesta para Swift!
Nubaslon
Tenía un error molesto en el que cambiar un título de UIButton mientras estaba fuera de la pantalla causaría tiempos de animación extraños con interactivePopGestureRecognizer y esto lo resolvió. Todavía creo que es un error con el sistema operativo
SparkyRobinson
Es extraño que deba llamarse .layoutIfNeeded (), pero lo probé en ambos sentidos en Swift 5 y definitivamente todavía se anima sin él.
wildcat12
2
Realmente no. Si no llama layoutIfNeeded(), el botón se marca como necesario volver a dibujar, pero esto no sucederá hasta el próximo pase de diseño, que estará fuera delperformWithoutAnimation
Paulw11
60

Tenga en cuenta :

cuando " buttonType " de _button es "UIButtonTypeSystem" , el siguiente código no es válido :

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

cuando " buttonType " de _button es "UIButtonTypeCustom" , el código anterior es válido .

shede333
fuente
No puedo creer cuánto tiempo pasé antes de descubrir que solo necesitas cambiar el tipo de botón ... ugh ...
Sandy Chapman
Funciona sin ningún código. Cambie solo el tipo de botones y funcionará.
Alex Motor
52

A partir de iOS 7.1, la única solución que funcionó para mí fue inicializar el botón con el tipo UIButtonTypeCustom.

Norbert
fuente
Este es el enfoque más sensato para cualquiera que no requiera UIButtonTypeSystem.
DonnaLea
Esto terminó funcionando mejor para mí, acabo de crear un botón PERSONALIZADO y lo hice ver y resaltar como un botón del sistema. Apenas puedo ver la diferencia, pero no tienes ese retraso.
Travis M.
18

entonces encuentro la solución trabajada:

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

primero cambiamos el título del botón, luego cambiamos el tamaño del botón para este título

dubenko
fuente
1
Yo uso la misma solución. La respuesta aceptada no funciona para mí.
deej
Esto hace que el título parpadee dos veces, al menos con iOS 8.
Jordan H
1
Esto funciona para mí en 7.1 y 8.1 sin parpadear. Simple y efectivo.
Todd
Funciona perfectamente en iOS 11, aunque tuve que usar la misma cadena nuevamente para la segunda línea (el uso del título de la etiqueta del botón hizo que parpadeara).
SilverWolf - Restablece a Mónica el
13

Establezca el tipo de botón en UIButtonTypeCustom y dejará de parpadear

arunjos007
fuente
¿Cómo pueden todas esas "soluciones" alternativas tener tantos votos positivos cuando esta simple respuesta debe resolver este problema el 99% del tiempo ...
Rob
12

Swift 5

myButton.titleLabel?.text = "title"
myButton.setTitle("title", for: .normal)
Carter Randall
fuente
No tengo idea de por qué funciona, pero funciona y es la solución más limpia. UIKit es raro.
Nathan Hosselton
11

Hice una extensión Swift para hacer esto:

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

        setTitle(title, forState: .Normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}

Funciona para mí en iOS 8 y 9, con UIButtonTypeSystem.

(El código es para Swift 2, Swift 3 y Objective-C debería ser similar)

Xhacker Liu
fuente
¡No lo voy a usar ahora, pero es muy útil tenerlo cerca!
Francis Reynolds el
9

Establezca el tipo UIButton como Personalizado. Eso debería eliminar las animaciones de fundido de entrada y salida.

Amit Tandel
fuente
1
¡Esto debería tener más votos a favor! Funciona perfectamente y desactiva la animación en la causa raíz, en lugar de estas otras soluciones.
Jesper Schläger
7

Por lo general, simplemente configurar el tipo de botón en Personalizado funciona para mí, pero por otras razones necesitaba subclasificar UIButton y volver a establecer el tipo de botón en el predeterminado (Sistema), por lo que el parpadeo volvió a aparecer.

Establecer UIView.setAnimationsEnabled(false)antes de cambiar el título y luego volver a verdadero después de eso no evitó el parpadeo para mí, no importa si llaméself.layoutIfNeeded() o no.

Esto, y solo esto en el siguiente orden exacto, funcionó para mí con iOS 9 y 10 beta:

1) Cree una subclase para UIButton (no olvide configurar la clase personalizada para el botón en el Guión gráfico también).

2) Anular de la setTitle:forState:siguiente manera:

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

    UIView.performWithoutAnimation({

        super.setTitle(title, forState: state)

        self.layoutIfNeeded()
    })
}

En Interface Builder, puede dejar el tipo de botón en Sistema, no es necesario cambiarlo a Tipo personalizado para que este enfoque funcione.

Espero que esto ayude a alguien más, he luchado durante tanto tiempo con los molestos botones parpadeantes que espero evitarlo para otros;)

cdf1982
fuente
No olvides layoutIfNeeded():]
Tai Le
6

Simplemente puede crear un botón personalizado y dejará de animarse mientras cambia el título.

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

también puede hacerlo en la casilla de verificación Storyboard: seleccione el botón en storyboard -> seleccione el inspector de atributos (cuarto desde el lado izquierdo) -> en el menú desplegable 'Tipo', seleccione 'Personalizado' en lugar de 'Sistema' que probablemente fue seleccionado .

¡Buena suerte!

Mendy
fuente
3

Puede eliminar las animaciones de la capa de la etiqueta del título:

    [[[theButton titleLabel] layer] removeAllAnimations];
Jacksonh
fuente
Revisé todas las respuestas. Este es el mejor.
Rudolf Adamkovič
2
Todavía parpadea pero es mejor.
Lucien
ESTO DEBE SER LA RESPUESTA.
mxcl
3

Swift 4 versión de Xhacker Liu respuesta

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

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
} 
faraz khonsari
fuente
1

UIButton con systemtipo tiene animación implícita activada setTitle(_:for:). Puedes arreglarlo de dos maneras diferentes:

  1. Establezca el tipo de botón en custom, desde el código o el Generador de interfaces:
let button = UIButton(type: .custom)

ingrese la descripción de la imagen aquí

  1. Deshabilitar animación del código:
UIView.performWithoutAnimation {
    button.setTitle(title, for: .normal)
    button.layoutIfNeeded()
}
sgl0v
fuente
0

Descubrí que esta solución también funciona con UIButtonTypeSystem , pero solo funcionará si el botón está habilitado por algún motivo.

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

Por lo tanto, deberá agregarlos si necesita deshabilitar el botón al configurar su título.

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

(iOS 7, Xcode 5)

sCha
fuente
Acabo de confirmar que esta solución alternativa ya no funciona en iOS 7.1.
sCha
¿no crees que has encontrado una solución para 7.1?
George Green
@GeorgeGreen no pudo encontrar ninguna solución de trabajo para UIButtonTypeSystem . Tuve que usar UIButtonTypeCustom .
sCha
Desde 7.1, deberá aplicar los cambios de título a todos los estados, ya que establecerlo solo para el estado normal ya no se aplica. [_button setTitle:@"title" forState:UIControlStateDisabled]
Sam
0

La combinación de excelentes respuestas anteriores da como resultado la siguiente solución para 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
fuente
0

Tengo el problema de la animación fea al cambiar los títulos de los botones en los controladores de vista dentro de un UITabBarController. Los títulos que originalmente se establecieron en el guión gráfico aparecieron por un corto tiempo antes de desvanecerse en sus nuevos valores.

Quería iterar a través de todas las subvistas y usar los títulos de los botones como claves para obtener sus valores localizados con NSLocalizedString, como;

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];
    }

}

Descubrí que lo que desencadena la animación es realmente la llamada a btn.titleLabel.text. Por lo tanto, para seguir utilizando los guiones gráficos y tener los componentes localizados dinámicamente de esta manera, me aseguro de establecer la ID de restauración de cada botón (en el Inspector de identidad) al mismo título y usar eso como clave en lugar del título;

for(UIView *v in view.subviews) {

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

}

No es ideal, pero funciona.

Miguel
fuente
0

En realidad, puede establecer el título fuera de un bloque de animación, solo asegúrese de llamar layoutIfNeeded()dentro de 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 tiene un montón de botones, considere simplemente llamar layoutIfNeeded()a la vista súper:

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

La extensión Xhacker Liu convertida a Swift 3:

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

        setTitle(title, for: .normal)

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

¿Quizás generar 2 animaciones y 2 botones es una mejor solución para evitar el problema que aparece al animar y cambiar el texto de un botón?

Creé un segundo uibutton y generé 2 animaciones, esta solución funciona sin problemas.

    _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
fuente
-1

Lo hice funcionar con una combinación de respuestas:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];
Jaspe
fuente
-1

Una extensión conveniente para el cambio animado del título del botón en Swift que funciona bien con la implementación predeterminada:

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
fuente
@Fogmeister 1. Mi respuesta es diferente 2. Sintaxis Swift actualizada 3. De acuerdo con la API de Apple para UIButton.
Richard Topchii