Привет! В этой статье я покажу, как можно создать в Xcode свою кастомную кнопку с простой анимацией с помощью языка Swift. Совместимость: iOS8 или выше.
Это просто пример, поэтому всевозможные права доступа, типа private и т. п., здесь не используются. Также подразумевается, что вы уже знакомы с основами Xcode и Swift и сможете без труда создать начальный «Single View App»‑проект в среде Xcode, этот этап также будет опущен.
Кнопка будет выглядеть следующим образом:
Нормальное состояние
Нажатое состояние
Анимация
Подготовка
Добавляем в проект новый файл с незатейливым названием CustomButton1.swift, в котором будет находиться класс новой кнопки с таким же незатейливым названием, наследуемый от UIButton.
//
// CustomButton1.swift
//
import UIKit
class CustomButton1: UIButton {
}
Далее в Main.storyboard добавляем кнопку в контроллер, присваиваем ей класс CustomButton1. Изменяем тип кнопки на Custom, а цвет Background на оранжевый.
Настройка кнопки
Кнопка будет иметь закруглённые края. Анимация будет осуществляться путём изменения альфа‑канала заднего плана с 0.3 до 1. Альфа‑канал названия кнопки при этом должен оставаться неизменным, поэтому для заднего цвета будет использоваться layer.backgroundColor, а не backgroundColor, который мы задали ранее.
Добавляем изменяемое поле для хранения цвета (по умолчанию чёрный), неизменяемое поле для хранения значения альфа‑канала при нажатом состоянии и метод, который будет осуществлять настройку кнопки.
class CustomButton1: UIButton {
var color: UIColor = .black
let touchDownAlpha: CGFloat = 0.3
func setup() {
backgroundColor = .clear
layer.backgroundColor = color.cgColor
layer.cornerRadius = 6
clipsToBounds = true
}
}
Автовызов настройки кнопки
Для того, чтобы настройки к кнопке из Storyboard применились автоматически, нужно добавить setup в метод awakeFromNib, не забыв при этом сохранить значение backgroundColor перед тем, как его обесцветить.
override func awakeFromNib() {
super.awakeFromNib()
if let backgroundColor = backgroundColor {
color = backgroundColor
}
setup()
}
Программное создание кнопки
Не всегда удобно использовать Storyboard для создания кнопки, иногда лучше это сделать из программы. Для этого пропишем вспомогательный инициализатор.
convenience init(color: UIColor? = nil, title: String? = nil) {
self.init(type: .custom)
if let color = color {
self.color = color
}
if let title = title {
setTitle(title, for: .normal)
}
setup()
}
Теперь, чтобы создать такую же кнопку внутри программы, достаточно написать так.
let button = CustomButton1(color: .orange, title: "Button")
Настройка кнопки готова.
События нажатия и отпускания кнопки
Перед тем, как перейти к анимации, нужно как-то ловить события нажатия и отпускания кнопки. Это можно сделать через поле isHighlighted. Методы touchDown и touchUp будут описаны ниже, пока они пустые.
override var isHighlighted: Bool {
didSet {
if isHighlighted {
touchDown()
} else {
cancelTracking(with: nil)
touchUp()
}
}
}
func touchDown() {
}
func touchUp() {
}
Анимация
Самое интересное! Как же реализовать анимацию? Первым делом напрашивается UIView.animate, но здесь есть небольшая загвоздка. При нажатии кнопки альфа‑канал заднего плана устанавливается в 0.3, а при отпускании плавно переходит в 1. Если в момент анимации пользователь снова нажал на кнопку, то анимация должна прерываться и значение альфа‑канала снова должно быть 0.3. Напоминаю, что кнопка должна работать как в последней iOS, так и в iOS8. Я не нашел простого варианта, как можно прервать UIView.animate в iOS8, поэтому использовал для анимации простой таймер.
weak var timer: Timer?
func stopTimer() {
timer?.invalidate()
}
deinit {
stopTimer()
}
Заполнение метода touchDown. Нужно остановить анимацию, если она происходит, и установить альфа‑канал заднего цвета в 0.3.
func touchDown() {
stopTimer()
layer.backgroundColor = color.withAlphaComponent(touchDownAlpha).cgColor
}
Заполнение метода touchUp. При отпускании кнопки нужно запустить анимацию, то есть сделать циклический таймер, который бы постепенно увеличивал альфа‑канал заднего плана. Но какой шаг для таймера выбрать? Опытным путём я установил, что шаг в 10 миллисекунд одинаково хорошо работает на всех моделях iPhone/iPad, даже на старых — iPhone 4s/5. Если сделать шаг чаще, то на 32‑битных контроллерах смотрится уже плохо.
Также опытным путём я установил, что стандартная анимация кнопок в iOS длится примерно 400 миллисекунд. Остаётся только вычислить альфа‑шаг, который стоит прибавлять на каждом шаге таймера и написать метод, который бы осуществлял это прибавление и следил за окончанием анимации.
let timerStep: TimeInterval = 0.01
let animateTime: TimeInterval = 0.4
lazy var alphaStep: CGFloat = {
return (1 - touchDownAlpha) / CGFloat(animateTime / timerStep)
}()
func touchUp() {
timer = Timer.scheduledTimer(timeInterval: timerStep,
target: self,
selector: #selector(animation),
userInfo: nil,
repeats: true)
}
@objc func animation() {
guard let backgroundAlpha = layer.backgroundColor?.alpha else {
stopTimer()
return
}
let newAlpha = backgroundAlpha + alphaStep
if newAlpha < 1 {
layer.backgroundColor = color.withAlphaComponent(newAlpha).cgColor
} else {
layer.backgroundColor = color.cgColor
stopTimer()
}
}
Вот собственно и всё. Кнопка готова. Спасибо за внимание!
Автор: asklv