Как остановить нежелательную анимацию UIButton при смене названия?

211

В iOS 7 мои названия UIButton оживляются и выходят не в то время - поздно. Эта проблема не появляется на iOS 6. Я просто использую:

[self setTitle:text forState:UIControlStateNormal];

Я бы предпочел, чтобы это произошло мгновенно и без пустой рамки. Это мерцание особенно отвлекает и отвлекает внимание от других анимаций.

exsulto
источник
Мы тоже переживаем это. Не уверен, что это ошибка iOS7 или что-то, что мы должны исправить.
Sway
Попробуйте, [self.button setHighlighted: NO];
Картика
Спасибо за эти идеи. Я попробовал setHighlighted: НЕТ, но не повезло там. Я могу уменьшить мерцание, поместив setTitle внутрь: [UIView animateWithDuration: 0.0f animations: ^ {...}];
exsulto
1
Вы можете использовать этот метод обхода в некоторых случаях: self.button.titleLabel.text = text. Но это не меняет размер рамки метки и не работает с UIControlStates правильно
zxcat
Это умный обходной путь. Я поиграю с этим и посмотрю, что произойдет, к сожалению, я использую UIControlStates.
exsulto

Ответы:

165

Это работает для пользовательских кнопок:

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

Для системных кнопок вам нужно добавить это перед повторным включением анимации (спасибо @Klaas):

[_button layoutIfNeeded];
Джейкоб К
источник
12
К сожалению, это не похоже на работу. Ни один из них не выполняет без анимации
Sway
9
Итак, исправление, которое сработало в конце, состояло в том, чтобы оставить исходный текст UIButton пустым, чтобы при установке его с кодом он не вызывал анимацию.
Sway
27
Это работает, только если вы установили тип кнопки на пользовательский, согласно этому ответу stackoverflow.com/a/20718467/62 .
Лирон Яхдав
15
Начиная с iOS 7.1 я должен был добавить[_button layoutIfNeeded];
Klaas
6
@LironYahdav, если для типа кнопки установлено значение UIButtonTypeCustom, этот ответ не требуется.
DonnaLea
262

Используйте performWithoutAnimation:метод, а затем принудительно создайте макет, а не позже.

[UIView performWithoutAnimation:^{
  [self.myButton setTitle:text forState:UIControlStateNormal];
  [self.myButton layoutIfNeeded];
}];
Снеговик
источник
11
Это работает так же хорошо, как и принятый ответ, но кажется более приятным, потому что он более инкапсулирован - невозможно забыть добавить [UIView setAnimationsEnabled: YES] или удалить его в будущем.
Сибурб
19
Это работает для системных кнопок, если вы звоните [button layoutIfNeeded];внутри блока.
Александр Блин
1
Кстати, для системных кнопок, layoutIfNeed должен вызываться после изменения текста
Yon
Это лучшее решение! Приветствия
sachadso
Это правильно для меня. Это наиболее проголосовали и на 6-м месте. Хорошо ...
Сольгар
80

Измените тип кнопки на пользовательский конструктор интерфейса формы.

введите описание изображения здесь

Это сработало для меня.

Христос Чаджикириаку
источник
7
Лучшее решение! Спасибо.
Томас Калмон
3
Но это также отключает анимацию при нажатии кнопки. Я хочу только отключить анимацию при показе кнопки.
Петр Васильевич
Это работает, если вас не волнует анимация при нажатии кнопки.
Хоакин Перейра
У меня была пара кнопок, установленных вот так, и, очевидно, это самый элегантный ответ для моего случая. Хорошо, спасибо!
Золтан
79

В Swift вы можете использовать:

UIView.performWithoutAnimation {
    self.someButtonButton.setTitle(newTitle, forState: .normal)
    self.someButtonButton.layoutIfNeeded()
}
Paulw11
источник
1
Это был безусловно самый простой метод. И спасибо, что
включили
1
Лучший ответ для Swift!
Нубаслон
Возникла досадная ошибка, из-за которой изменение заголовка UIButton в то время как за кадром приводило к странным временам анимации с interactivePopGestureRecognizer, и это решало ее. Я все еще думаю, что это ошибка в ОС, хотя
SparkyRobinson
Странно, что .layoutIfNeeded () должен быть вызван, но я протестировал его в Swift 5 в обоих направлениях, и он определенно все еще анимируется без него.
wildcat12
2
На самом деле, нет. Если вы не звоните, layoutIfNeeded()кнопка помечается как нуждающаяся в перерисовке, но этого не произойдет до следующего прохода макета, который будет за пределамиperformWithoutAnimation
Paulw11
60

Пожалуйста, обратите внимание :

когда " buttonType " _button равен "UIButtonTypeSystem" , приведенный ниже код недействителен :

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

когда « buttonType » _button равен «UIButtonTypeCustom» , приведенный выше код действителен .

shede333
источник
Не могу поверить, сколько времени я потратил, прежде чем выяснить, тебе просто нужно сменить тип кнопки ... тьфу ...
Сэнди Чепмен
Работает без кода. Измените только тип кнопок, и это будет работать.
Алекс Мотор
52

Начиная с iOS 7.1, единственным решением, которое работало для меня, была инициализация кнопки с типом UIButtonTypeCustom.

Norbert
источник
Это самый разумный подход для тех, кто не требует UIButtonTypeSystem.
DonnaLea
Это оказалось лучше всего для меня, я просто создал кнопку CUSTOM и сделал ее похожей на системную кнопку. Вряд ли вижу разницу, но у вас нет этой задержки.
Трэвис М.
18

так что я нашел работающее решение:

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

сначала мы меняем заголовок для кнопки, затем изменяем размер кнопки для этого заголовка

Дубенко
источник
1
Я использую тот же обходной путь. Принятый ответ не работает для меня.
Диджей
Это заставляет заголовок мигать дважды, по крайней мере, с iOS 8.
Джордан Х
1
Это работает для меня в 7.1 и 8.1 без перепрошивки. Просто и эффективно.
Тодд
Прекрасно работает в iOS 11, хотя мне пришлось снова использовать ту же строку для второй строки (использование названия метки кнопки вызывало ее мигание).
Серебряный Волк - Восстановить Монику
13

Установите тип кнопки на UIButtonTypeCustom, и он перестанет мигать

arunjos007
источник
Как все эти обходные «решения» могут иметь столько голосов, когда этот простой ответ должен решить эту проблему в 99% случаев ...
Роб
12

Swift 5

myButton.titleLabel?.text = "title"
myButton.setTitle("title", for: .normal)
Картер Рэндалл
источник
Не знаю, почему это работает, но это работает, и это самое чистое решение. UIKit это странно.
Натан Хосселтон
11

Я сделал расширение Swift для этого:

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

        setTitle(title, forState: .Normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}

У меня работает на iOS 8 и 9, с UIButtonTypeSystem.

(Код для Swift 2, Swift 3 и Objective-C должен быть похожим)

Ксхакер Лю
источник
Не собираюсь использовать его сейчас, но очень удобно иметь рядом!
Фрэнсис Рейнольдс
9

Установите тип UIButton как Custom. Это должно удалить анимацию постепенного появления и исчезновения.

Амит Тандель
источник
1
Это должно иметь больше голосов! Работает отлично и отключает анимацию по первопричине вместо этих других обходных путей.
Джеспер Шлегер
7

Обычно просто установка типа кнопки на Custom работает для меня, но по другим причинам мне нужно было создать подкласс UIButton и установить тип кнопки обратно на значение по умолчанию (System), чтобы мигающее изображение появилось снова.

настройка UIView.setAnimationsEnabled(false) перед изменением названия и затем снова на истину после этого не избежала мигания для меня, независимо от того, звонил я self.layoutIfNeeded()или нет.

Это, и только это в следующем точном порядке, работало для меня с iOS 9 и 10 бета:

1) Создайте подкласс для UIButton (не забудьте также установить собственный класс для кнопки в раскадровке).

2) Переопределите setTitle:forState:следующим образом:

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

    UIView.performWithoutAnimation({

        super.setTitle(title, forState: state)

        self.layoutIfNeeded()
    })
}

В Интерфейсном Разработчике вы можете оставить тип кнопки «Система», не нужно менять его на «Пользовательский тип», чтобы этот подход работал.

Надеюсь, это поможет кому-то еще, я так долго боролся с раздражающими мигающими кнопками, что надеюсь избежать этого для других;)

cdf1982
источник
Не забывайте layoutIfNeeded():]
Тай Ле
6

Вы можете просто создать кнопку Custom, и она прекратит анимацию при изменении заголовка.

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

Вы также можете сделать это в окне раскадровки: выберите кнопку в раскадровке -> выберите инспектор атрибутов (четвертый слева) -> в раскрывающемся меню «Тип» выберите «Пользовательский» вместо «Система», который, вероятно, был выбран ,

Удачи!

Менди
источник
3

Вы можете удалить анимацию из слоя метки заголовка:

    [[[theButton titleLabel] layer] removeAllAnimations];
Jacksonh
источник
Я прошел через все ответы. Этот лучший.
Рудольф Адамкович
2
Это все еще мигает, но это лучше.
Люсьен
ЭТО ДОЛЖНО БЫТЬ ОТВЕТОМ.
mxcl
3

Swift 4 версия ответа Xhacker Лю

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

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
} 
фараз хонсари
источник
1

UIButton с systemтипом имеет неявную анимацию setTitle(_:for:). Вы можете исправить это двумя разными способами:

  1. Установите тип кнопки custom, либо из кода, либо из Interface Builder:
let button = UIButton(type: .custom)

введите описание изображения здесь

  1. Отключить анимацию из кода:
UIView.performWithoutAnimation {
    button.setTitle(title, for: .normal)
    button.layoutIfNeeded()
}
sgl0v
источник
0

Я обнаружил, что этот обходной путь работает и с UIButtonTypeSystem, но будет работать, только если кнопка включена по какой-то причине.

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

Так что вам придется добавить их, если вам нужно отключить кнопку при установке ее названия.

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

(iOS 7, Xcode 5)

ща
источник
Только что подтвердил, что этот обходной путь больше не работает на iOS 7.1.
sCha
разве вы не нашли решение для 7.1?
Джордж Грин
@GeorgeGreen не может найти какие-либо рабочие решения для UIButtonTypeSystem . Мне пришлось использовать UIButtonTypeCustom .
sCha
Начиная с версии 7.1, вам нужно будет применить изменения заголовка ко всем состояниям, а установка только для нормального состояния больше не применяется. [_button setTitle:@"title" forState:UIControlStateDisabled]
Сэм
0

Объединение вышеупомянутых великолепных ответов приводит к следующему обходному пути для 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
источник
0

У меня возникла проблема с анимацией при смене заголовков кнопок в контроллерах представления в UITabBarController. Названия, которые были первоначально установлены в раскадровке, появлялись на короткое время, прежде чем исчезли в их новых значениях.

Я хотел перебрать все подпредставления и использовать заголовки кнопок в качестве ключей, чтобы получить их локализованные значения с NSLocalizedString, такие как;

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

}

Я обнаружил, что анимация вызывает вызов btn.titleLabel.text. Поэтому, чтобы по-прежнему использовать раскадровки и динамически локализовать компоненты, как это, я должен установить для идентификатора восстановления каждой кнопки (в Identity Inspector) тот же заголовок и использовать его в качестве ключа вместо заголовка;

for(UIView *v in view.subviews) {

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

}

Не идеально, но работает ..

Майкл
источник
0

Вы можете установить заголовок вне блока анимации, просто обязательно вызовите layoutIfNeeded()внутри executeWithoutAnimation:

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

Если у вас есть несколько кнопок, рассмотрите возможность вызова layoutIfNeeded()супер-представления:

button1.setTitle("abc", forState: .Normal)
button2.setTitle("abc", forState: .Normal)
button3.setTitle("abc", forState: .Normal)
UIView.performWithoutAnimation {
    self.view.layoutIfNeeded()
}
Senseful
источник
0

Расширение Xhacker Liu преобразовано в Swift 3:

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

        setTitle(title, for: .normal)

        layoutIfNeeded()
        UIView.setAnimationsEnabled(true)
    }
}
Paweł
источник
-1

Возможно, создание 2 анимаций и 2 кнопок - лучшее решение, чтобы избежать проблемы, возникающей при анимации и изменении текста кнопки?

Я создал второй uibutton и сгенерировал 2 анимации, это решение работает без сбоев.

    _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

Я получил его для работы с комбинацией ответов:

[[[button titleLabel] layer] removeAllAnimations];

    [UIView performWithoutAnimation:^{

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

    }];
Джаспер
источник
-1

Удобное расширение для изменения заголовка анимированной кнопки в Swift, которое прекрасно сочетается с реализацией по умолчанию:

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. Мой ответ другой 2. Современный синтаксис Swift 3. В соответствии с API Apple для UIButton.
Ричард Топчий