Pull to refresh

Comments 12

Имею похожее решение, но не такое изящное. Спасибо!

В Кикстартере для стилей используют линзы, выглядит примерно так:


public func cardStyle <V: UIViewProtocol> (cornerRadius radius: CGFloat = Styles.cornerRadius) -> ((V) -> V) {

  return roundedStyle(cornerRadius: radius)
    <> V.lens.layer.borderColor .~ UIColor.ksr_grey_500.cgColor
    <> V.lens.layer.borderWidth .~ 1.0
    <> V.lens.backgroundColor .~ .white
}

пример использования:


    _ = self.cardView
      |> cardStyle()
      |> dropShadowStyle()
      |> UIView.lens.layer.borderColor .~ UIColor.ksr_navy_500.cgColor

(взято отсюда и отсюда)


Выглядит достаточно элегантно и возможности композиции на очень хорошем уровне.
Библиотека, позволяющая такие чудеса, называется Kickstarter-Prelude.


Концепция линз взята из функционального программирования, широко используется в Хаскеле (см. lens package).


На Свифте, из-за синтаксических особенностей, выглядит не так красиво, но достаточно неплохо.
Также, Хаскель позволяет генерировать код для линз автоматически (используя Template Haskell), а тут приходится писать их вручную. Но, поскольку Kickstarter для нас уже постарался, можно использовать готовые.

Около полугода назад опубликовал на GitHub решение для стилизации UIView/NSView, (микро-) фреймворк StyleSheet — идея перекликается со статьёй.


Стили ассоциируются с (пустыми) протоколами-маркерами и привязываются к подклассам UIView/NSView через protocol conformance:


// Style-marker protocols
protocol BodyFontStyle {}
protocol MultilineLabelStyle {}

func appStyle(palette p: PaletteProtocol) -> StyleProtocol {
    return StyleSheet(styles: [
        // Styles implementation for different base-classes.

        Style<BodyFontStyle, UILabel> { $0.font = p.font.body },
        Style<BodyFontStyle, UITextField> { $0.font = p.font.body },
        Style<BodyFontStyle, UITextView> { $0.font = p.font.body },

        Style<MultilineLabelStyle, UILabel> {
            $0.numberOfLines = 0
            $0.lineBreakMode = .byWordWrapping
        },
    ])
}

final class BodyLabel: UILabel, BodyFontStyle, MultilineLabelStyle {}

// Perform on app initialization
try! RootStyle.autoapply(style: appStyle(palette: DefaultPalette())) // Specify `mode: .appearance` to use `UIAppearance`-hitchhiking

Таблицы стилей могут каскадироваться. Что такое "стиль" и "таблица стилей" описано в Style.swift.


Автоматическое применение стилей реализуется одним из двух механизмов, на выбор: через swizzling или через UIAppearance-hitchhiking (см. RootStyle.swift). В обоих случаях применение стилей происходит так же, как при использовании UIAppearance (доступно для iOS и tvOS).


Автоматическое применение использовать не обязательно, но удобно. Установка через Carthage и CocoaPods.


Используется в production и приносит пользу :)

Вопрос вот какого плана: допустим есть какой-то подкласс с объявленными протоколами-стилями, то изменить стили для этого класса уже не получится?

Не вполне ясно, о чем вопрос. В примере выше есть BodyLabel, подкласс UIView с двумя стилями. Очевидно, вы можете изменить декларацию этого подкласса, если имеете доступ к коду. Вы также можете изменить реализацию стилей.


Можно добавить дополнительные стили (protocol conformance) через extension.


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

Действительно, я плохо сформулировал вопрос. Вопрос был именно про изменение внешнего вида компонента во время выполнения. Какими механизмами?

Независимо от того, стилизуете ли вы компонент через Interface Builder, из кода непосредственно, через UIAppearance или с помощью какого-то фреймворка, компонент, который изменяет свой внешний вид, должен иметь параметры отображения для каждого своего состояния.


К примеру, если у вас есть on/off button, то у неё должны быть свойства onColor и offColor. Вы можете задать им нужные значения любым доступным способом, а кнопка сама будет выбирать, цветом из какого свойства покраситься :)

Последую вашему хорошему замечанию про набор состояний. Возможно даже на вторую публикацию материала наберется!

При назначении свойст лейблу опечатка, повторно используется labelNormal, вместо labelTitle.


Спасибо за статью!

Sign up to leave a comment.

Articles