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.