- PVSM.RU - https://www.pvsm.ru -
Interface Builder в Xcode с некоторого времени экономит мне много времени в работе по стандартному лайауту элементов интерфейса и иногда помогает в задаче прототипирования. С версии 6 в Xcode добавили возможность рендера кастомных вьюшек, помеченных атрибутом IBDesignable, а также отображение в билдере полей класса, помеченных атрибутом IBInspectable.
С версии Xcode 7 этой фичей стало более-менее возможно пользоваться, поэтому мне захотелось проверить её возможности.
Почитать про IBDesignable/IBInspectable можно тут [1] и тут [2].
Давайте создадим кастомную кнопку с возможностью настраивать цвет, толщину и радиус скругления border, причем чтобы все эти параметры можно было контролировать через Interface Builder.
@IBDesignable class BorderedButton : UIButton {
/// Толщина границы
@IBInspectable var borderWidth: CGFloat {
set { layer.borderWidth = newValue }
get { return layer.borderWidth }
}
/// Цвет границы
@IBInspectable var borderColor: UIColor? {
set { layer.borderColor = newValue?.CGColor }
get { return layer.borderColor?.UIColor }
}
/// Радиус границы
@IBInspectable var cornerRadius: CGFloat {
set { layer.cornerRadius = newValue }
get { return layer.cornerRadius }
}
}
extension CGColor {
private var UIColor: UIKit.UIColor {
return UIKit.UIColor(CGColor: self)
}
}
Все работает, билдер обновляет рендер при изменении параметров.
Но ведь такие параметры наверное могут быть не только у нашего класса кнопки, а у любых других кнопок. Почему бы не сделать расширение базового класса UIButton.
extension UIButton {
/// Радиус гараницы
@IBInspectable var cornerRadius: CGFloat {
set { layer.cornerRadius = newValue }
get { return layer.cornerRadius }
}
/// Толщина границы
@IBInspectable var borderWidth: CGFloat {
set { layer.borderWidth = newValue }
get { return layer.borderWidth }
}
/// Цвет границы
@IBInspectable var borderColor: UIColor? {
set { layer.borderColor = newValue?.CGColor }
get { return layer.borderColor?.UIColor }
}
}
Сотрём IBInspectable поля класса кастомной кнопки, так как они уже прописаны в расширении. В результате класс останется пустым.
@IBDesignable class BorderedButton : UIButton {}
Добавим еще одну кнопку рядом с нашей кастомной кнопкой, но не будем назначать ей класса (будет стандартный UIButton).
Как видно из результата, Interface Builder сохранил возможность ввода IBInspectable полей даже у базового класса UIButton, однако не рендерит его, так как он не помечен атрибутом IBDesignable.
Похожим образом можно расширить базовый класс UIView.
extension UIView {
/// Радиус гараницы
@IBInspectable var cornerRadius: CGFloat {
set { layer.cornerRadius = newValue }
get { return layer.cornerRadius }
}
/// Толщина границы
@IBInspectable var borderWidth: CGFloat {
set { layer.borderWidth = newValue }
get { return layer.borderWidth }
}
/// Цвет границы
@IBInspectable var borderColor: UIColor? {
set { layer.borderColor = newValue?.CGColor }
get { return layer.borderColor?.UIColor }
}
/// Смещение тени
@IBInspectable var shadowOffset: CGSize {
set { layer.shadowOffset = newValue }
get { return layer.shadowOffset }
}
/// Прозрачность тени
@IBInspectable var shadowOpacity: Float {
set { layer.shadowOpacity = newValue }
get { return layer.shadowOpacity }
}
/// Радиус блура тени
@IBInspectable var shadowRadius: CGFloat {
set { layer.shadowRadius = newValue }
get { return layer.shadowRadius }
}
/// Цвет тени
@IBInspectable var shadowColor: UIColor? {
set { layer.shadowColor = newValue?.CGColor }
get { return layer.shadowColor?.UIColor }
}
/// Отсекание по границе
@IBInspectable var _clipsToBounds: Bool {
set { clipsToBounds = newValue }
get { return clipsToBounds }
}
}
Теперь параметрами слоя любой вьюшки можно управлять через билдер. Для возможности live-рендера только одно условие — у вьюшки в билдере должен быть указан кастомные класс с атрибутом IBDesignable.
Допустим, у нас в приложении есть светлая и темная темы. Попробуем стилизовать кнопки с помощью перечисления.
/// Стиль кнопки
enum ButtonStyle: String {
/// Светлый стиль
case Light = "light"
/// Темный стиль
case Dark = "dark"
/// Оттенок
var tintColor: UIColor {
switch self {
case .Light: return UIColor.blackColor()
case .Dark: return UIColor.lightGrayColor()
}
}
/// Цвет границы
var borderColor: UIColor { return tintColor }
/// Цвет фона
var backgroundColor: UIColor { return UIColor.clearColor() }
/// Толщина границы
var borderWidth: CGFloat { return 1 }
/// Радиус границы
var cornerRadius: CGFloat { return 4 }
}
Напишем соответствующее расширение для класса UIButton, которое позволяет выбирать и применять стили к кнопкам:
extension UIButton {
/// Стиль кнопки
@IBInspectable var style: String? {
set { setupWithStyleNamed(newValue) }
get { return nil }
}
/// Применение стиля по его строковому названию
private func setupWithStyleNamed(named: String?){
if let styleName = named, style = ButtonStyle(rawValue: styleName) {
setupWithStyle(style)
}
}
/// Применение стиля по его идентификатору
func setupWithStyle(style: ButtonStyle){
backgroundColor = style.backgroundColor
tintColor = style.tintColor
borderColor = style.borderColor
borderWidth = style.borderWidth
cornerRadius = style.cornerRadius
}
}
Теперь добавляем а билдере еще две кнопки, и в новом поле Style прописываем стили «dark» и «light» соответственно.
Теперь мы можем применять стили к кнопкам одним полем в билдере и наблюдать их реальное отображение. Если ограничится только первым, то нам даже не придется создавать свой IBDesignable класс (который по сути пустой). Ничто не мешает добавить еще несколько стилей, а также расширить тип стиля и сделать динамический выбор применяемых значений в зависимости от класса вьюшки.
В статье я не пытался преподнести новый способ стилизации элементов интерфейса. Однако, возможно, данный подход подтолкнет кого-нибудь на другое оригинальное применение данной фичи.
Исходники можно найти на гите [3].
Автор: dante_photo
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/interfejsy/108341
Ссылки в тексте:
[1] тут: https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/Chapters/CreatingaLiveViewofaCustomObject.html
[2] тут: http://nshipster.com/ibinspectable-ibdesignable/
[3] гите: https://github.com/danteteam/InterfaceBuilderExtension
[4] Источник: http://habrahabr.ru/post/274687/
Нажмите здесь для печати.