Cómo evitar que UIButton parpadee al actualizar el título

85

Cuando llamo a setTitle en un UIButton, el botón parpadea en iOS 7. Intenté configurar myButton.highlights = NO, pero eso no impidió que el botón parpadeara.

[myButton setTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"elapsedLabelKey"] forState:UIControlStateNormal];

myButton.highlighted = NO;

Así es como configuro el temporizador que actualizó los títulos:

- (void)actionTimer {
    if (myTimer == nil) {

        myTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0
                        target: self
                        selector: @selector(showActivity)
                        userInfo: nil
                        repeats: YES];
    }
}

Este es el método que realmente actualiza los títulos:

- (void)showActivity {

    NSString *sym = [[NSLocale currentLocale] objectForKey:NSLocaleCurrencySymbol];

    if (pauseInterval == nil) {

        // Update clock
        seconds = [[NSDate date] timeIntervalSinceDate:startInterval] - breakTime;

        // Update total earned
        secRate = rate.value / 60 / 60;
        total = secRate * seconds;
        [totalLabel setTitle:[NSString stringWithFormat:@"%@%.4f",sym,total] forState:UIControlStateNormal];

        days = seconds / (60 * 60 * 24);
        seconds -= days * (60 * 60 * 24);
        int hours = seconds / (60 * 60);
        fhours = (float)seconds / (60.0 * 60.0);
        seconds -= hours * (60 * 60);
        int minutes = seconds / 60;
        seconds -= minutes * 60;

        // Update the timer clock
        [elapsed setTitle:[NSString stringWithFormat:@"%.2i:%.2i:%.2i:%.2i",days,hours,minutes,seconds] forState:UIControlStateNormal];
    }
}
Feroz mono
fuente
No debería estar parpadeando. ¿Estás intentando hacer esto en un hilo de fondo?
BergQuester
Sí, se está configurando en un temporizador para actualizar el título del botón cada segundo (que creo que crea su propio hilo)
FierceMonkey
Los temporizadores solo se ejecutan en un hilo separado si los configura para hacerlo. ¿Podríamos ver el método en el que se actualiza el título y cómo configura el temporizador?
BergQuester
Agregué

Respuestas:

205

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

Quanlong
fuente
16
Esta debería ser realmente la respuesta preferida. La solucion mas sencilla. Y está funcionando.
Johan Karlsson
4
Convenido. Probé muchas otras soluciones y esto es lo que lo hizo, por sí solo.
BBQ Steve
2
Esta debería ser la respuesta aceptada. La solución más sencilla hasta ahora.
Machado
1
¡Funciona de maravilla! Gracias
2
Creo que esta es una solución realmente buena sin escribir ningún código para eso.
Chauyan
55

Un enfoque mejor que el [UIView setAnimationsEnabled:NO]que puede afectar a otras animaciones es deshabilitar solo la animación del título específico.

C objetivo:

[UIView performWithoutAnimation:^{
  [myButton setTitle:text forState:UIControlStateNormal];
  [myButton layoutIfNeeded];
}];

Rápido:

UIView.performWithoutAnimation { 
    myButton.setTitle(text, for: .normal)
    myButton.layoutIfNeeded()
}
Cœur
fuente
Acabo de probar la solución Swift en el último iOS: funciona muy bien.
Ronen Rabinovici
1
Salvador del día. ¡Salud!
pkc456
Intenté esto en Swift 3, y funciona sin el uso de layoutIfNeeded ()
user1898712
3
¡Esta debería ser la respuesta aceptada sin lugar a dudas!
Julius
1
Esta es la mejor forma de hacer esto. ¡Gran respuesta!
Ascender
29

* Tenga en cuenta *

cuando " buttonType " de _button es "UIButtonTypeSystem" , el código siguiente 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
19

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

[UIView setAnimationsEnabled:NO];
[elapsed setTitle:[NSString stringWithFormat:@"%.2i:%.2i:%.2i:%.2i",days,hours,minutes,seconds] forState:UIControlStateNormal];
[UIView setAnimationsEnabled:YES];
Marius
fuente
7
@FierceMonkey trate de añadir una llamada a layoutIfNeeded: [elapsed layoutIfNeeded];. Esto eliminó el parpadeo en mi caso.
Klaas
1
layoutIfNeeded lo hizo por mí.
Kaolin Fire
Parece ser una solución bastante sucia, sin embargo layoutIfNeeded, parece estar funcionando
Przemysław Wrzesiński
7

Simplemente puede crear otra función para UIButton para facilitar el uso futuro.

extension UIButton {
    func setTitleWithoutAnimation(_ title: String?, for controlState: UIControlState) {
        UIView.performWithoutAnimation {
            self.setTitle(title, for: controlState)
            self.layoutIfNeeded()
        }
    }
}

¡Eso es!

Y simplemente establezca el título como lo haría antes, pero reemplácelo setTitleconsetTitleWithoutAnimation

Daniel Tseng
fuente
6

¡El comportamiento predeterminado de "setTitle" es definitivamente odioso!

Mi solucion es:

[UIView setAnimationsEnabled:NO];
[_button layoutIfNeeded]; 
[_button setTitle:[NSString stringWithFormat:@"what" forState:UIControlStateNormal];
[UIView setAnimationsEnabled:YES];

Además, en el guión gráfico, en la propiedad del botón desmarco:

  • Invierte en resaltado
  • Imagen de ajuste resaltada

Y comprobar:

  • Desactivado Ajustar imagen

Probado y funcionando también en iOS 8 .

Actualización - Swift

Le sugiero que cree su botón personalizado y anule el método setTitle .

class UnflashingButton: UIButton {
   
    override func setTitle(title: String?, forState state: UIControlState) {
        UIView.setAnimationsEnabled(false)
        self.layoutIfNeeded()
        super.setTitle(title, forState: state)
        UIView.setAnimationsEnabled(true)
    }

}
Luca Davanzo
fuente
Probé este enfoque (subclasificación, anulando setTitle: forState: y deshabilitando-habilitando UIView.setAnimationsEnabled). No me funciona en iOS 9 y 10 beta. Funciona si solo desactivo las animaciones, pero luego se pierden para cada UIView ...
cdf1982
2

Pruebe esto, esto funciona en las versiones más recientes de IOS:

class CustomButtonWithNoEffect : UIButton {
    override func setTitle(_ title: String?, for state: UIControlState) {
        UIView.performWithoutAnimation {
            super.setTitle(title, for: state)
            super.layoutIfNeeded()
        }
    }

}
Kárpáti András
fuente
0

Soy nuevo en codificación y Stackoverflow, así que no tengo suficiente reputación para comentar directamente para expandir https://stackoverflow.com/users/4673064/daniel-tseng excelente respuesta. Entonces tengo que escribir mi propia respuesta nueva, y es esta:

extension UIButton {
    func setTitleWithoutAnimation(_ title: String?, for controlState: UIControlState) {
        UIView.performWithoutAnimation {
            self.setTitle(title, for: controlState)
            self.layoutIfNeeded()
        }
    }
}

Funciona muy bien, EXCEPTO:

Si todas mis llamadas más adelante en el código a "setTitleWithoutAnimation" no especifican un remitente, entonces recibo estos mensajes extraños relacionados con CoreMedia o CoreData, por ejemplo, "No se pudieron heredar los permisos de CoreMedia de 2526: (nulo)"

Esto es probablemente bastante básico para la codificación y iOS, pero para mí, como nuevo codificador, me envió por un camino de conejo por un tiempo, como en: Today Extension No se pudieron heredar los permisos de CoreMedia , donde la gente tenía respuestas interesantes pero eso NO llegar a la raíz de mi problema.

Así que finalmente descubrí que tenía que revisar y dar parámetros a mis funciones sin parámetros, es decir, tenía que especificar un remitente. Ese remitente podría ser UIButton, Any o AnyObject. Todos funcionaron, pero en última instancia parece haber un conflicto entre agregar una extensión de botón con "self" y no decir específicamente más adelante que un botón la está usando.

Nuevamente, probablemente básico, pero nuevo para mí, así que pensé que valdría la pena compartirlo.

Ian Hanson
fuente
0

Otro truco

@IBOutlet private weak var button: UIButton! {
    didSet {
        button.setTitle(title, for: .normal)
    }
}

Esto solo funciona si conoce el valor del título sin realizar ninguna llamada a la API. El título del botón se establecerá antes de que se cargue la vista, por lo que no verá la animación.

TunaTheDog
fuente