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.

exsulto
fonte
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];
Jacob K
fonte
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];
}];
Boneco de neve
fonte
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.

Christos Chadjikyriacou
fonte
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()
}
Paulw11
fonte
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 .

shede333
fonte
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 .

Norbert
fonte
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

dubenko
fonte
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

arunjos007
fonte
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)
Carter Randall
fonte
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)

Xhacker Liu
fonte
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.

Amit Tandel
fonte
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;)

cdf1982
fonte
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!

Mendy
fonte
3

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

    [[[theButton titleLabel] layer] removeAllAnimations];
Jacksonh
fonte
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)
    }
} 
faraz khonsari
fonte
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()
}
sgl0v
fonte
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)

sCha
fonte
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];
}
AppsolutEinfach
fonte
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 ..

Michael
fonte
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()
}
Sensível
fonte
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)
    }
}
Paweł
fonte
-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;}];
coda
fonte
-1

Consegui trabalhar com uma combinação de respostas:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];
Jaspe
fonte
-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)
    }
  }
}
Richard Topchii
fonte
@Fogmeister 1. Minha resposta é diferente 2. Sintaxe Swift atualizada 3. Consistente com a API da Apple para UIButton.
Richard Topchii