Создание красивой, отзывчивой UIButton в Swift
UIButton - один из самых любимых и ненавистных элементов управления в UIKit. Это центральный элемент практически любого приложения, но, поскольку это один из старейших классов UIKit, его иногда довольно неудобно использовать. В этой статье мы объясним, как сделать так, чтобы он не только выглядел красиво, но и как сделать его отзывчивым, чтобы мы могли радовать наших пользователей (спойлер: на самом деле это совсем несложно!).
1. “Vanilla” UIButton is very bare bones
Ну что ж, поиграем. Во-первых, что произойдет, если мы просто создадим «Vanilla» UIButton без конфигурации?
let button = UIButton()
button.tintColor = .systemTeal
button.setTitle("Tap Me", for: .normal)
У него нет фона, и, что удивительно, нет простого способа его добавить! Это нормально, но справедливо предположить, что большинство приложений хотят, чтобы хотя бы некоторые из их кнопок были более смелыми, яркими и красивыми. Что-то вроде этого:
2. Добавление цвета фона к кнопке.
Чтобы добавить цвет фона к нашему обычному UIButton, нам нужно будет использовать функцию setBackgroundImage (_: for :). Как видите, для его использования нам понадобится объект UIImage. Итак, мы будем использовать эту вспомогательную функцию (от команды Kickstarter) для создания однопиксельных изображений заданного цвета, которые затем мы будем использовать для установки в качестве фона для наших кнопок:
Отлично! Теперь применим это к нашему UIButton и посмотрим, что произойдет:
Ладно, это уже лучше! Давайте добавим эту функцию в расширение для удобства:
extension UIButton {
func setBackgroundColor(_ backgroundColor: UIColor, for state: UIControl.State) {
self.setBackgroundImage(.pixel(ofColor: backgroundColor), for: state)
}
}
button.setBackgroundColor(.systemBlue, for: .normal)
3. Добавление закругленных углов
Что ж, это очень просто. Мы будем использовать свойство .layer.cornerRadius, которое доступно для любого подкласса UIView:
Прекрасно! Конечно, вы можете поиграть с точным числом, чтобы достичь желаемого радиуса угла. Не забудьте установить для .layer.masksToBounds значение true!
Но мы еще не закончили! Почему? Что ж, попробуем немного поиграться с нашей кнопкой:
Нуу, это выглядит нормально, и на самом деле большинство приложений просто так и оставят. Но на самом деле мы можем добиться большего почти без усилий!
4. Как сделать кнопку более отзывчивой
Секрет заключается в том, что называется UIButton.ButtonType. Когда вы просто создаете кнопку с помощью инициализатора UIButton (), он создаст ее с типом кнопки .custom. Однако, используя тип кнопки .system, вы получите гораздо более плавную, игривую и отзывчивую анимацию! Просто посмотрите, это волшебно:
Вот так, намного лучше. Это становится особенно ясно, когда вы используете это на физическом устройстве. Он очень интуитивно понятен - он плавно анимируется в зависимости от того, быстро ли вы нажимаете на него или держите на нем палец.
5. Поддержка темного режима.
Некоторое время мы использовали вспомогательную функцию Kickstarter для создания фоновых изображений размером 1 пиксель, но, начиная с iOS 13, мы заметили некоторые проблемы при переключении из светлого режима в темный или наоборот. Допустим, у нас есть кнопка, на которой должен быть черный фон и белый текст в светлом режиме, а также белый фон и черный текст в темном режиме:
Что произойдет, если мы перейдем из светлого режима в темный во время работы приложения?
Ух, это нехорошо. Чтобы решить эту проблему, мы изменили вспомогательную функцию Kickstarter для создания фоновых изображений размером 1 пиксель с правильной поддержкой светлого и темного режимов (просто скопируйте и вставьте):
static func pixel(ofColor color: UIColor) -> UIImage {
let lightModeImage = UIImage.generatePixel(ofColor: color, userInterfaceStyle: .light)
let darkModeImage = UIImage.generatePixel(ofColor: color, userInterfaceStyle: .dark)
lightModeImage.imageAsset?.register(darkModeImage, with: UITraitCollection(userInterfaceStyle: .dark))
return lightModeImage
}
static private func generatePixel(ofColor color: UIColor, userInterfaceStyle: UIUserInterfaceStyle) -> UIImage {
var image: UIImage!
if #available(iOS 13.0, *) { UITraitCollection(userInterfaceStyle: userInterfaceStyle).performAsCurrent { image = .generatePixel(ofColor: color) } } else {
image = .generatePixel(ofColor: color)
}
return image
}
static private func generatePixel(ofColor color: UIColor) -> UIImage {
let pixel = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
UIGraphicsBeginImageContext(pixel.size)
defer { UIGraphicsEndImageContext() }
guard let context = UIGraphicsGetCurrentContext() else {
return UIImage()
}
context.setFillColor(color.cgColor)
context.fill(pixel)
return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}
}
Мы использовали эту модифицированную версию во всех наших последних проектах и не испытывали никаких проблем.