Jak zatrzymać niechcianą animację UIButton przy zmianie tytułu?

211

W iOS 7 moje tytuły UIButton pojawiają się i pojawiają w niewłaściwym czasie - późno. Ten problem nie pojawia się w iOS 6. Po prostu używam:

[self setTitle:text forState:UIControlStateNormal];

Wolałbym, żeby stało się to natychmiast i bez pustej ramki. Mrugnięcie jest szczególnie rozpraszające i odciąga uwagę od innych animacji.

exsulto
źródło
Tego również doświadczamy. Nie jestem pewien, czy to błąd iOS7, czy coś, co powinniśmy naprawić.
Sway
Spróbuj, [self.button setHighlighted: NO];
karthika
Dzięki za te pomysły. Próbowałem setHighlighted: NIE, ale nie ma tam szczęścia. Jestem w stanie zredukować mrugnięcie, umieszczając setTitle wewnątrz: [UIView animateWithDuration: 0.0f animacje: ^ {...}];
exsulto
1
Można użyć tego obejścia w niektórych przypadkach: self.button.titleLabel.text = text. Ale to nie zmienia rozmiaru ramki etykiety i nie działa poprawnie z UIControlStates
zxcat
To sprytne obejście. Będę się tym bawić i zobaczę, co się stanie, niestety używam UIControlStates.
exsulto

Odpowiedzi:

165

Działa to w przypadku niestandardowych przycisków:

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

W przypadku przycisków systemowych musisz to dodać przed ponownym włączeniem animacji (dziękuję @Klaas):

[_button layoutIfNeeded];
Jacob K.
źródło
12
Niestety to nie działa. Żadne z nich nie wykonujeWithoutAnimation
Sway
9
Ok, więc poprawka, która ostatecznie działała, polegała na pozostawieniu oryginalnego tekstu UIButton pustego, aby po ustawieniu go kodem nie uruchamiał animacji.
Sway
27
Działa to tylko wtedy, gdy ustawisz typ przycisku na niestandardowy, zgodnie z tą odpowiedzią stackoverflow.com/a/20718467/62 .
Liron Yahdav
15
W iOS 7.1 musiałem dodać[_button layoutIfNeeded];
Klaas
6
@LironYahdav, jeśli typ przycisku jest ustawiony na UIButtonTypeCustom, ta odpowiedź nie jest wymagana.
DonnaLea
262

Użyj tej performWithoutAnimation:metody, a następnie wymuś natychmiastowe rozmieszczenie układu zamiast później.

[UIView performWithoutAnimation:^{
  [self.myButton setTitle:text forState:UIControlStateNormal];
  [self.myButton layoutIfNeeded];
}];
Bałwan
źródło
11
Działa to tak samo, jak zaakceptowana odpowiedź, ale wydaje się ładniejsze, ponieważ jest bardziej enkapsulowane - nie można zapomnieć o dodaniu [UIView setAnimationsEnabled: TAK] lub o usunięciu go z toru.
siburb
19
Działa z przyciskami systemowymi, jeśli wywołujesz [button layoutIfNeeded];wewnątrz bloku.
Alexandre Blin,
1
BTW, dla przycisków systemowych layoutIfNeed powinny być nazywane po tekst zmieniony
Yon
To najlepsze rozwiązanie! Pozdrawiam
sachadso
To jest dla mnie poprawne. Jest najczęściej głosowany i zajmuje 6 miejsce. Fajnie ...
solgar
79

Zmień typ przycisku na niestandardowy konstruktor interfejsu formularza.

wprowadź opis zdjęcia tutaj

To zadziałało dla mnie.

Christos Chadjikyriacou
źródło
6
Najlepsze rozwiązanie! Dziękuję Ci.
Thomás Calmon
3
Ale to również wyłącza animację po kliknięciu przycisku. Chcę tylko wyłączyć animację po wyświetleniu przycisku.
Piotr Wasilewicz
Działa to, jeśli nie obchodzi Cię animacja po naciśnięciu przycisku.
Joaquin Pereira,
Mam kilka takich przycisków i oczywiście jest to najbardziej elegancka odpowiedź w moim przypadku. Fajnie dzięki!
Zoltán
79

W Swift możesz użyć:

UIView.performWithoutAnimation {
    self.someButtonButton.setTitle(newTitle, forState: .normal)
    self.someButtonButton.layoutIfNeeded()
}
Paulw11
źródło
1
To była zdecydowanie najłatwiejsza metoda. I dziękuję za szybką odpowiedź btw
simplexity
1
Najlepsza odpowiedź dla Swift!
Nubaslon
Miał irytujący błąd polegający na tym, że zmiana tytułu UIButton podczas wyświetlania poza ekranem powodowałaby dziwne czasy animacji z interaktywnym programem PopGestureRecognizer, co rozwiązało problem. Nadal uważam, że to błąd w systemie operacyjnym
SparkyRobinson
Dziwne, że trzeba wywołać funkcję .layoutIfNeeded (), ale przetestowałem ją w obie strony w Swift 5 i na pewno nadal bez niej animuje.
wildcat12
2
Nie całkiem. Jeśli nie zadzwonisz, layoutIfNeeded()przycisk zostanie oznaczony jako wymagający ponownego przerysowania, ale nie stanie się to, dopóki nie przejdzie następny układ, który będzie pozaperformWithoutAnimation
Paulw11,
60

Proszę zanotować :

gdy „ buttonType ” przycisku _ ma wartość „UIButtonTypeSystem” , poniższy kod jest nieprawidłowy

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

gdy „ buttonType ” przycisku _ ma wartość „UIButtonTypeCustom” , powyższy kod jest prawidłowy .

shede333
źródło
Nie mogę uwierzyć, ile czasu spędziłem, zanim zorientowałem się, że musisz zmienić typ przycisku ... ugh ...
Sandy Chapman,
Działa bez żadnego kodu. Zmień tylko typ przycisków i będzie działać.
Alex Motor
52

Począwszy od iOS 7.1 jedynym rozwiązaniem, które działało dla mnie, było zainicjowanie przycisku z typem UIButtonTypeCustom.

Norbert
źródło
Jest to najbardziej rozsądne podejście dla każdego, kto nie wymaga UIButtonTypeSystem.
DonnaLea
Skończyło się to dla mnie najlepszym działaniem, właśnie utworzyłem przycisk NIESTANDARDOWY i nadałem mu wygląd i wyróżnienie jak przycisk systemowy. Trudno dostrzec różnicę, ale nie masz tego opóźnienia.
Travis M.
18

więc znajduję działające rozwiązanie:

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

najpierw zmieniamy tytuł przycisku, a następnie zmieniamy rozmiar przycisku dla tego tytułu

dubenko
źródło
1
Korzystam z tego samego obejścia. Przyjęta odpowiedź nie działa dla mnie.
deej
Powoduje to dwukrotne flashowanie tytułu, przynajmniej w systemie iOS 8.
Jordan H
1
Działa to dla mnie zarówno w wersji 7.1, jak i 8.1 bez flashowania. Prosty i skuteczny.
Todd
Działa doskonale w iOS 11, chociaż musiałem ponownie użyć tego samego ciągu w drugiej linii (użycie tytułu etykiety przycisku spowodowało, że zaczął mrugać).
SilverWolf - Przywróć Monikę
13

Ustaw typ przycisku na UIButtonTypeCustom, a przestanie migać

arunjos007
źródło
Jak wszystkie te obejścia „rozwiązań” mają tak wiele pozytywnych opinii, skoro ta prosta odpowiedź musi rozwiązać ten problem w 99% przypadków ...
Rob
12

Szybki 5

myButton.titleLabel?.text = "title"
myButton.setTitle("title", for: .normal)
Carter Randall
źródło
Nie mam pojęcia, dlaczego to działa, ale działa i jest to najczystsze rozwiązanie. UIKit jest dziwny.
Nathan Hosselton
11

Zrobiłem rozszerzenie Swift, aby to zrobić:

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

        setTitle(title, forState: .Normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}

Działa dla mnie na iOS 8 i 9, z UIButtonTypeSystem .

(Kod dotyczy Swift 2, Swift 3 i Cel-C powinien być podobny)

Xhacker Liu
źródło
Nie będziesz go teraz używać, ale bardzo przydatny, aby mieć go przy sobie!
Francis Reynolds
9

Ustaw typ UIButton jako Niestandardowy. To powinno usunąć animacje pojawiania się i zanikania.

Amit Tandel
źródło
1
To powinno mieć więcej głosów pozytywnych! Działa idealnie i wyłącza animację u źródła, zamiast tych innych obejść.
Jesper Schläger
7

Zwykle po prostu ustawienie typu przycisku na Niestandardowy działa dla mnie, ale z innych powodów musiałem podklasować UIButton i ustawić typ przycisku z powrotem na domyślny (System), więc miganie pojawiło się ponownie.

Ustawienie UIView.setAnimationsEnabled(false)przed zmianą tytułu, a potem ponownie na prawdę, nie uniknęło mrugania dla mnie, bez względu na to, czy zadzwoniłem, self.layoutIfNeeded()czy nie.

To i tylko to w następującej dokładnej kolejności działało dla mnie z iOS 9 i 10 beta:

1) Utwórz podklasę dla UIButton (nie zapomnij też ustawić niestandardowej klasy dla przycisku w Storyboard).

2) Zastąp setTitle:forState:w następujący sposób:

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

    UIView.performWithoutAnimation({

        super.setTitle(title, forState: state)

        self.layoutIfNeeded()
    })
}

W Konstruktorze interfejsów możesz pozostawić typ przycisku na System, nie musisz zmieniać go na Typ niestandardowy, aby to podejście działało.

Mam nadzieję, że to pomaga komuś innemu, tak długo walczyłem z irytującymi migającymi przyciskami, że mam nadzieję, że uniknę tego innym;)

cdf1982
źródło
Nie zapomnij layoutIfNeeded():]
Tai Le
6

Możesz po prostu utworzyć przycisk Niestandardowy, który zatrzyma animację podczas zmiany tytułu.

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

możesz to również zrobić w polu wyboru Scenorys: wybierz przycisk w serii ujęć -> wybierz inspektor atrybutów (czwarty z lewej strony) -> z menu rozwijanego „Typ” wybierz „Niestandardowy” zamiast „System”, który prawdopodobnie został wybrany .

Powodzenia!

Mendy
źródło
3

Możesz usunąć animacje z warstwy etykiety tytułu:

    [[[theButton titleLabel] layer] removeAllAnimations];
Jacksonh
źródło
Przejrzałem wszystkie odpowiedzi. Ten jest najlepszy.
Rudolf Adamkovič
2
Nadal miga, ale jest lepiej.
Lucien
TO POWINNA BYĆ ODPOWIEDŹ.
mxcl
3

Swift 4 wersja odpowiedzi 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
źródło
1

UIButton z systemtypem ma domyślną animację setTitle(_:for:). Możesz to naprawić na dwa różne sposoby:

  1. Ustaw typ przycisku na custom, z kodu lub Konstruktora interfejsów:
let button = UIButton(type: .custom)

wprowadź opis zdjęcia tutaj

  1. Wyłącz animację z kodu:
UIView.performWithoutAnimation {
    button.setTitle(title, for: .normal)
    button.layoutIfNeeded()
}
sgl0v
źródło
0

Przekonałem się, że to obejście działa również z UIButtonTypeSystem, ale zadziała tylko wtedy, gdy przycisk jest z jakiegoś powodu włączony .

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

Musisz je dodać, jeśli chcesz wyłączyć przycisk podczas ustawiania jego tytułu.

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

(iOS 7, Xcode 5)

sCha
źródło
Właśnie potwierdzono, że to obejście nie działa już w systemie iOS 7.1.
sCha,
nie przypuszczasz, że znalazłeś rozwiązanie dla 7.1?
George Green
@GeorgeGreen nie mógł znaleźć żadnych działających rozwiązań dla UIButtonTypeSystem . Musiałem użyć UIButtonTypeCustom .
sCha
Począwszy od 7.1, musisz zastosować zmiany tytułu do wszystkich stanów, ustawienie tylko dla stanu normalnego już nie ma zastosowania. [_button setTitle:@"title" forState:UIControlStateDisabled]
Sam
0

Połączenie powyższych świetnych odpowiedzi daje w rezultacie następujące obejście dla 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
źródło
0

Mam brzydki problem z animacją podczas zmiany tytułów przycisków w kontrolerach widoku w UITabBarController. Tytuły, które zostały pierwotnie ustawione w serii ujęć, pojawiły się na krótko, zanim zaczęły pojawiać się nowe wartości.

Chciałem iterować przez wszystkie podviewy i używać tytułów przycisków jako kluczy, aby uzyskać ich zlokalizowane wartości za pomocą NSLocalizedString, takich jak;

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

}

Dowiedziałem się, że to, co wyzwala animację, to tak naprawdę wywołanie btn.titleLabel.text. Aby nadal korzystać ze scenariuszy i dynamicznie lokalizować komponenty w ten sposób, ustawiam identyfikator przywracania każdego przycisku (w Inspektorze tożsamości) na taki sam jak tytuł i używam go jako klucza zamiast tytułu;

for(UIView *v in view.subviews) {

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

}

Nie idealne, ale działa ..

Michael
źródło
0

Możesz ustawić tytuł poza blokiem animacji, pamiętaj tylko o wywołaniu layoutIfNeeded()wewnątrz 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()
}

Jeśli masz kilka przycisków, rozważ wywołanie layoutIfNeeded()super widoku:

button1.setTitle("abc", forState: .Normal)
button2.setTitle("abc", forState: .Normal)
button3.setTitle("abc", forState: .Normal)
UIView.performWithoutAnimation {
    self.view.layoutIfNeeded()
}
Rozsądny
źródło
0

Rozszerzenie Xhacker Liu przekonwertowane na Swift 3:

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

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}
Paweł
źródło
-1

Może lepszym rozwiązaniem jest wygenerowanie 2 animacji i 2 przycisków, aby uniknąć problemu z animacją i zmianą tekstu przycisku?

Utworzyłem drugi przycisk uibutton i wygenerowałem 2 animacje, to rozwiązanie działa bez żadnych problemów.

    _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
źródło
-1

Mam do pracy z kombinacją odpowiedzi:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];
Jaspis
źródło
-1

Wygodne rozszerzenie do zmiany tytułu animowanego przycisku w Swift, które ładnie współpracuje z domyślną implementacją:

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
źródło
@Fogmeister 1. Moja odpowiedź jest inna 2. Aktualna składnia Swift 3. Zgodny z API Apple dla UIButton.
Richard Topchii