Como parar a animação UIButton indesejada na alteração de título?


211

No iOS 7, meus títulos UIButton estão entrando e saindo na hora errada - atrasados. Esse problema não aparece no iOS 6. Estou apenas usando:

[self setTitle:text forState:UIControlStateNormal];

Eu preferiria que isso aconteça instantaneamente e sem uma moldura em branco. Esse piscar é especialmente perturbador e desvia a atenção de outras animações.


Estamos passando por isso também. Não tenho certeza se é um bug do iOS7 ou algo que devemos corrigir.
Balanço

Tente, [self.button setHighlighted: NO];
Kartika # 23/13

Obrigado por essas idéias. Tentei setHighlighted: NÃO, mas sem sorte lá. Consigo reduzir o piscar colocando setTitle dentro: [UIView animateWithDuration: 0.0f animations: ^ {...}];
exsulto 23/09

1
Você pode usar essa solução em alguns casos: self.button.titleLabel.text = text. Mas isso não redimensionar quadro rótulo e não funcionam com UIControlStates corretamente
zxcat

Essa é uma solução inteligente. Vou brincar com isso e ver o que acontece, infelizmente estou usando o UIControlStates.
exsulto 9/10

Respostas:


165

Isso funciona para botões personalizados:

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

Para os botões do sistema, você precisa adicioná-lo antes de reativar as animações (obrigado @Klaas):

[_button layoutIfNeeded];

12
Infelizmente, isso não parece funcionar. Não executeWithoutAnimation
Sway

9
Ok, então a correção que funcionou no final foi deixar o texto UIButton original em branco para que, quando eu o definisse com código, não disparasse a animação.
Sway

27
Isso funciona apenas se você definir o tipo do botão como personalizado, conforme esta resposta stackoverflow.com/a/20718467/62 .
Liron Yahdav

15
A partir do iOS 7.1 Eu tive que adicionar[_button layoutIfNeeded];
Klaas

6
@LironYahdav, se você tiver o tipo de botão definido como UIButtonTypeCustom, essa resposta não será necessária.
DonnaLea

262

Use o performWithoutAnimation:método e, em seguida, force o layout a acontecer imediatamente e não mais tarde.

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

11
Isso funciona tão bem quanto a resposta aceita, mas parece melhor porque é mais encapsulado - é impossível esquecer de adicionar o [UIView setAnimationsEnabled: YES] ou para que ele seja removido no caminho.
siburb

19
Funciona para os botões do sistema se você ligar [button layoutIfNeeded];dentro do bloco.
Alexandre Blin

1
BTW, para botões do sistema, layoutIfNeed deve ser chamado depois que o texto for alterado
Yon

Essa é a melhor solução! Cheers
sachadso

Este é o correto para mim. É o mais votado e está no 6º lugar. Bom ...
solgar

79

Altere o tipo de botão para o construtor de interface de formulário customizado.

insira a descrição da imagem aqui

Isso funcionou para mim.


6
Melhor solução! Obrigado.
Thomás Calmon

3
Mas isso também desativa a animação no botão de clique. Quero apenas desativar a animação ao mostrar o botão.
Piotr Wasilewicz

Isso funciona se você não se importa com a animação quando o botão é tocado.
Joaquin Pereira

Eu já coloquei alguns botões assim e, obviamente, essa é a resposta mais elegante para o meu caso. Nice, obrigado!
Zoltán

79

No Swift você pode usar:

UIView.performWithoutAnimation {
    self.someButtonButton.setTitle(newTitle, forState: .normal)
    self.someButtonButton.layoutIfNeeded()
}

1
Este foi de longe o método mais fácil. E obrigado por incluir uma resposta rápida btw
simplexity

1
Melhor resposta para Swift!
Nubaslon 2/06

Tinha um bug irritante em que a alteração de um título UIButton enquanto fora da tela causava tempos estranhos de animação com o InteractivePopGestureRecognizer e isso o solucionava. Eu ainda acho que é um bug com o sistema operacional embora
SparkyRobinson

Estranho que .layoutIfNeeded () precise ser chamado, mas eu testei nos dois sentidos no Swift 5 e ele definitivamente ainda anima sem ele.
wildcat12

2
Na verdade não. Se você não chamar layoutIfNeeded(), em seguida, o botão é sinalizado como a necessidade de ser redesenhado, mas isso não vai acontecer até o passe seguinte layout, que será fora doperformWithoutAnimation
Paulw11

60

Observe :

quando " buttonType " de _button é "UIButtonTypeSystem" , o código abaixo é inválido :

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

quando " buttonType " de _button é "UIButtonTypeCustom" , o código acima é válido .


Não posso acreditar quanto tempo eu passei antes de descobrir que você só precisa mudar o tipo de botão ... ugh ...
Sandy Chapman

Funciona sem nenhum código. Mude apenas o tipo de botão e funcionará.
Alex Motor

52

A partir do iOS 7.1, a única solução que funcionou para mim foi inicializar o botão com o tipo UIButtonTypeCustom .


Essa é a abordagem mais sensata para quem não requer UIButtonTypeSystem.
DonnaLea

Isso acabou funcionando melhor para mim, eu apenas criei um botão PERSONALIZADO e fiz com que parecesse e realce como um botão do sistema. Mal consigo ver a diferença, mas você não tem esse atraso.
Travis M.

18

então eu acho solução trabalhada:

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

primeiro, mudamos o título do botão e redimensionamos o botão para este título


1
Eu uso a mesma solução alternativa. A resposta aceita não funciona para mim.
deej

Isto faz com que o título a piscar duas vezes, com, pelo menos, iOS 8.
Jordan H

1
Isso funciona para mim tanto no 7.1 quanto no 8.1 sem piscar. Simples e eficaz.
Todd

Funciona perfeitamente no iOS 11, embora eu tenha que usar a mesma sequência novamente para a segunda linha (o uso do título do rótulo do botão fez com que piscasse).
SilverWolf - Reinstate Monica

13

Defina o tipo de botão como UIButtonTypeCustom e ele parará de piscar


Como podem essas solução alternativa "soluções" têm tantos upvotes quando esta resposta simples deve resolver esta questão 99% do tempo ...
Rob

12

Swift 5

myButton.titleLabel?.text = "title"
myButton.setTitle("title", for: .normal)

Não faço ideia por que isso funciona, mas funciona e é a solução mais limpa. UIKit é estranho.
Nathan Hosselton

11

Eu fiz uma extensão Swift para fazer isso:

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

        setTitle(title, forState: .Normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}

Funciona para mim no iOS 8 e 9, com UIButtonTypeSystem.

(O código é para Swift 2, Swift 3 e Objective-C devem ser semelhantes)


Não vou usá-lo agora, mas muito útil para ter por perto!
22416 Francis Reynolds

9

Defina o tipo de UIButton como personalizado. Isso deve remover as animações de fade in e out.


1
Isso deveria ter mais votos positivos! Funciona perfeitamente e desativa a animação na causa raiz, em vez dessas outras soluções alternativas.
Jesper Schläger

7

Normalmente, basta definir o tipo de botão como Personalizado funciona para mim, mas por outros motivos, precisei subclassificar UIButton e definir o tipo de botão novamente para o padrão (Sistema), para que o pisque reapareça.

Definir UIView.setAnimationsEnabled(false)antes de alterar o título e depois retornar a verdade depois disso não evitou piscar para mim, não importa se eu chamasseself.layoutIfNeeded() ou não.

Isso, e somente isso na seguinte ordem exata, funcionou para mim no iOS 9 e 10 beta:

1) Crie uma subclasse para o UIButton (não esqueça de definir também a classe personalizada para o botão no Storyboard).

2) Substitua da setTitle:forState:seguinte forma:

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

    UIView.performWithoutAnimation({

        super.setTitle(title, forState: state)

        self.layoutIfNeeded()
    })
}

No Interface Builder, você pode deixar o tipo de botão como Sistema, sem precisar alterá-lo para Tipo Personalizado para que essa abordagem funcione.

Espero que isso ajude outra pessoa, lutei por tanto tempo com os botões irritantes que espero evitar isso para os outros;)


Não se esqueça layoutIfNeeded():]
Tai Le

6

Você pode simplesmente criar o botão Personalizado e ele irá parar de animar enquanto altera o título.

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

você também pode fazê-lo na caixa de seleção Storyboard: selecione o botão no storyboard -> selecione o inspetor de atributos (quarto do lado esquerdo) -> no menu suspenso 'Tipo', selecione 'Personalizado' em vez de 'Sistema' que provavelmente foi selecionado .

Boa sorte!


3

Você pode remover as animações da camada do rótulo do título:

    [[[theButton titleLabel] layer] removeAllAnimations];

Eu passei por todas as respostas. Esse é o melhor.
Rudolf Adamkovič

2
Ainda pisca, mas é melhor.
Lucien

ESTA DEVE SER A RESPOSTA.
Mxcl 30/03/19

3

Swift 4 versão da resposta Xhacker Liu

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

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
} 

1

UIButton com systemtype tem animação implícita ativada setTitle(_:for:). Você pode corrigi-lo de duas maneiras diferentes:

  1. Defina o tipo de botão como custom, no código ou no Interface Builder:
let button = UIButton(type: .custom)

insira a descrição da imagem aqui

  1. Desative a animação do código:
UIView.performWithoutAnimation {
    button.setTitle(title, for: .normal)
    button.layoutIfNeeded()
}

0

Descobri que essa solução alternativa também funciona com UIButtonTypeSystem , mas só funcionará se o botão estiver ativado por algum motivo.

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

Portanto, você precisará adicioná-los se precisar que o botão seja desativado ao definir seu título.

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

(iOS 7, Xcode 5)


Acabei de confirmar que esta solução alternativa não funciona mais no iOS 7.1.
Scha

não suponha que você encontrou uma solução para 7.1?
George Green

A @GeorgeGreen não conseguiu encontrar nenhuma solução funcional para o UIButtonTypeSystem . Eu tive que usar UIButtonTypeCustom .
SCha

Desde o 7.1, você precisará aplicar as alterações de título a todos os estados, configurando-o apenas para o estado normal não se aplica mais. [_button setTitle:@"title" forState:UIControlStateDisabled]
Sam

0

A combinação das excelentes respostas acima resulta na seguinte solução alternativa para o 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

Eu tive o problema feio de animação ao alterar os títulos dos botões nos controladores de exibição em um UITabBarController. Os títulos que foram originalmente definidos no storyboard apareceram por um curto período de tempo antes de desaparecer em seus novos valores.

Eu queria percorrer todas as subvisões e usar os títulos dos botões como chaves para obter seus valores localizados com o 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];
    }

}

Eu descobri que o que está desencadeando a animação é realmente a chamada para btn.titleLabel.text. Portanto, para ainda usar os storyboards e ter os componentes localizados dinamicamente dessa maneira, defina a ID de restauração de cada botão (no Identity Inspector) como igual ao título e use-a como chave em vez do 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];
    }

}

Não é o ideal, mas funciona ..


0

Na verdade, você pode definir o título fora de um bloco de animação; layoutIfNeeded()lembre- se de chamar dentro de um 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()
}

Se você tiver vários botões, considere apenas chamar layoutIfNeeded()a super visão:

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

0

A extensão Xhacker Liu convertida para Swift 3:

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

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}

-1

Talvez gerar 2 animações e 2 botões seja uma solução melhor, para evitar o problema que aparece com a animação e a alteração do texto de um botão?

Criei um segundo uibutton e gerei 2 animações, esta solução funciona sem 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;}];

-1

Consegui trabalhar com uma combinação de respostas:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];

-1

Uma extensão conveniente para a alteração do título do botão animado no Swift que funciona bem com a implementação padrão:

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. Minha resposta é diferente 2. Sintaxe Swift atualizada 3. Consistente com a API da Apple para UIButton.
Richard Topchii
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.