¿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.


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

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

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í.


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()
}

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 .


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.


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


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


¿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)

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)


¡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.


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;)


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!


3

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

    [[[theButton titleLabel] layer] removeAllAnimations];

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)
    }
} 

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()
}

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)


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

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.


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()
}

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)
    }
}

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

-1

Lo hice funcionar con una combinación de respuestas:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];

-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)
    }
  }
}

@Fogmeister 1. Mi respuesta es diferente 2. Sintaxis Swift actualizada 3. De acuerdo con la API de Apple para UIButton.
Richard Topchii
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.