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.


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

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

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.


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

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 .


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.


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


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ć


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)

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)


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.


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


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!


3

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

    [[[theButton titleLabel] layer] removeAllAnimations];

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

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

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)


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

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


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

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

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

-1

Mam do pracy z kombinacją odpowiedzi:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];

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

@Fogmeister 1. Moja odpowiedź jest inna 2. Aktualna składnia Swift 3. Zgodny z API Apple dla UIButton.
Richard Topchii
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.