Как убрать нежелательную анимацию UIButton при изменении заголовка в iOS
Причина анимации и базовое решение
Анимация смены заголовка UIButton — это встроенное поведение, которое UIKit применяет автоматически для обеспечения плавности интерфейса. Оно работает на уровне UIView и вызывается при изменении текста через setTitle(_:for:). Простой вызов UIView.performWithoutAnimation не всегда срабатывает, потому что анимация запускается внутри самого метода кнопки.
Вот проверенное решение, которое гарантированно отключает анимацию:
// Способ 1: Использование CATransaction
CATransaction.begin()
CATransaction.setDisableActions(true)
myButton.setTitle("Новый текст", for: .normal)
CATransaction.commit()
Этот код отключает все неявные анимации CALayer, включая анимацию заголовка. Работает на всех версиях iOS.
Более элегантный способ с расширением
Чтобы не писать один и тот же код каждый раз, создайте расширение для UIButton:
extension UIButton {
func setTitleWithoutAnimation(_ title: String?, for state: UIControl.State) {
UIView.performWithoutAnimation {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.setTitle(title, for: state)
self.layoutIfNeeded()
CATransaction.commit()
}
}
}
Использование:
myButton.setTitleWithoutAnimation("Мгновенный текст", for: .normal)
Добавление layoutIfNeeded() гарантирует, что кнопка сразу перерисуется, а не будет ждать следующего цикла отрисовки.
Почему ваш код не работал?
UIView.performWithoutAnimation— блокирует анимации на уровне UIView, но анимация заголовка может быть на уровне CALayer, поэтому требуется дополнительныйCATransaction.titleLabel?.layer.removeAllAnimations()— удаляет текущие анимации, но не предотвращает запуск новых при следующем изменении текста.
Полный пример для вашего кода
class ViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
myButton.setTitleWithoutAnimation("Нажми меня", for: .normal)
}
@IBAction func buttonTapped(_ sender: UIButton) {
myButton.setTitleWithoutAnimation("Новый текст", for: .normal)
}
func setTitleWithoutAnimation(_ title: String?, for state: UIControl.State) {
UIView.performWithoutAnimation {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.myButton.setTitle(title, for: state)
self.myButton.layoutIfNeeded()
CATransaction.commit()
}
}
}
Этот код полностью убирает анимацию. Рекомендую вынести расширение в отдельный файл для переиспользования в других проектах.
<<Альтернативный подход: работа с titleLabel напрямую
Если вам нужно только изменить текст без анимации, можно обратиться к titleLabel напрямую, минуя метод setTitle. Однако этот способ менее надёжен, так как не обновляет внутреннее состояние кнопки для разных состояний (normal, highlighted, disabled).
myButton.titleLabel?.text = "Новый текст"
myButton.titleLabel?.sizeToFit() // подгоняем размер, если нужно
Минусы:
- Не работает для состояний, отличных от
.normal. - Может не обновить размер кнопки автоматически.
- При переиспользовании кнопки в таблице или коллекции могут возникнуть артефакты.
Когда использовать: Только для простых интерфейсов, где кнопка не меняет состояние и не требует точного соответствия UIControl.State.
Рекомендую всё же использовать первый ответ с CATransaction — он универсален и работает без побочных эффектов.
Ответов пока нет.
Войдите чтобы оставить ответ