Last active
June 7, 2021 10:01
-
-
Save ABashkirova/007e8a692e30b95c9af4f3fda20ca73d to your computer and use it in GitHub Desktop.
UI Components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
// Василий Пономарев, доклад «Техническая сторона UI-компонентов» | |
protocol SimpleViewViewModel { | |
var title: String { get } | |
var subtitle: String { get } | |
var image: UIImage? { get } | |
} | |
struct DefaultSimpleViewViewModel { | |
var title: String | |
var subtitle: String | |
var image: UIImage | |
} | |
struct NoIconSimpleViewViewModel { | |
var title: String | |
var subtitle: String | |
} | |
protocol LayoutMakerType { | |
associatedtype Container | |
static func makeConstraints(for container: Container) | |
static func addSubviews(for container: Container) | |
} | |
protocol ConfiguratorType { | |
associatedtype ViewModel | |
associatedtype Container | |
static func configure(with viewModel: ViewModel, for container: Container) | |
} | |
protocol StyleType { | |
associatedtype Configurator: ConfiguratorType | |
associatedtype Layout: LayoutMakerType | |
} | |
protocol DescribesSimpleViewStyle: StyleType { | |
associatedtype Layout = DefaultSimpleViewLayoutMaker | |
associatedtype Configurator = DefaultSimpleViewConfigurator | |
associatedtype IconStyle: IconViewStyleType = IconViewStyle.Medium.SuperEllipse | |
/// Шрифт заголовка | |
static var titleFont: UIFont { get } | |
/// Цвет заголовка | |
static var titleColor: UIColor { get } | |
/// Шрифт подзаголовка | |
static var subtitleFont: UIFont { get } | |
/// Цвет подзаголовка | |
static var subtitleColor: UIColor { get } | |
} | |
extension DescribesSimpleViewStyle { | |
static var titleFont: UIFont { UIFont.systemFont(ofSize: 17) } | |
static var titleColor: UIColor { UIColor.black } | |
static var subtitleFont: UIFont { UIFont.systemFont(ofSize: 13) } | |
static var subtitleColor: UIColor { UIColor.lightGray } | |
} | |
struct DefaultSimpleViewConfigurator: ConfiguratorType { | |
static func configure( | |
with viewModel: DefaultSimpleViewViewModel, | |
for container: SimpleViewContainer | |
) { | |
container.iconView.image = viewModel.image | |
container.titleLabel.text = viewModel.title | |
container.subtitleLabel.text = viewModel.subtitle | |
} | |
} | |
struct NoIconSimpleViewConfigurator: ConfiguratorType { | |
static func configure( | |
with viewModel: NoIconSimpleViewViewModel, | |
for container: SimpleViewContainer | |
) { | |
container.titleLabel.text = viewModel.title | |
container.subtitleLabel.text = viewModel.subtitle | |
} | |
} | |
struct DefaultSimpleViewLayoutMaker: LayoutMakerType { | |
static func addSubviews(for container: SimpleViewContainer) { | |
container.view.addSubview(container.iconView) | |
container.view.addSubview(container.titleLabel) | |
container.view.addSubview(container.subtitleLabel) | |
} | |
static func makeConstraints(for container: SimpleViewContainer) { | |
//... | |
} | |
} | |
struct NoIconSimpleViewLayoutMaker: LayoutMakerType { | |
static func addSubviews(for container: SimpleViewContainer) { | |
container.view.addSubview(container.titleLabel) | |
container.view.addSubview(container.subtitleLabel) | |
} | |
static func makeConstraints(for container: SimpleViewContainer) { | |
//... | |
} | |
} | |
enum SimpleViewStyle { | |
struct Default<IconStyle: IconViewStyleType>: DescribesSimpleViewStyle { } | |
struct Revert: DescribesSimpleViewStyle { | |
typealias Layout = NoIconSimpleViewLayoutMaker | |
typealias Configurator = NoIconSimpleViewConfigurator | |
static var titleFont: UIFont { UIFont.systemFont(ofSize: 13) } | |
static var titleColor: UIColor { UIColor.lightGray } | |
static var subtitleFont: UIFont { UIFont.systemFont(ofSize: 17) } | |
static var subtitleColor: UIColor { UIColor.black } | |
} | |
struct NoIcon: DescribesSimpleViewStyle { | |
typealias Layout = NoIconSimpleViewLayoutMaker | |
typealias Configurator = NoIconSimpleViewConfigurator | |
} | |
} | |
struct SimpleViewContainer { | |
/// Ссылка на себя | |
var view: UIView | |
/// Subviews | |
var titleLabel: UILabel | |
var subtitleLabel: UILabel | |
var iconView: IconViewProtocol | |
} | |
class SimpleView<Style: DescribesSimpleViewStyle>: UIView | |
where Style.Configurator.Container == SimpleViewContainer, | |
Style.Layout.Container == SimpleViewContainer { | |
private lazy var iconView = IconView<Style.IconStyle>() | |
private lazy var titleLabel: UILabel = { | |
let label = UILabel() | |
label.font = Style.titleFont | |
return label | |
}() | |
private lazy var subtitleLabel: UILabel = { | |
let label = UILabel() | |
label.font = Style.titleFont | |
return label | |
}() | |
private var container: SimpleViewContainer { | |
SimpleViewContainer( | |
view: self, | |
titleLabel: titleLabel, | |
subtitleLabel: subtitleLabel, | |
iconView: iconView | |
) | |
} | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
addSubviews() | |
addConstraints() | |
} | |
required init?(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
func configure(with viewModel: Style.Configurator.ViewModel) { | |
Style.Configurator.configure(with: viewModel, for: container) | |
} | |
} | |
private extension SimpleView { | |
func addSubviews() { | |
Style.Layout.addSubviews(for: container) | |
} | |
func addConstraints() { | |
Style.Layout.addSubviews(for: container) | |
} | |
} | |
/// IconView | |
protocol IconViewProtocol: UIImageView { } | |
class IconView<IconStyle: IconViewStyleType>: UIImageView, IconViewProtocol { | |
} | |
struct IconViewViewModel { } | |
struct IconViewContainer { } | |
struct SuperEllipseConfigurator: ConfiguratorType { | |
typealias ViewModel = IconViewViewModel | |
typealias Container = IconViewContainer | |
static func configure(with viewModel: IconViewViewModel, for container: IconViewContainer) { | |
} | |
} | |
struct RoundConfigurator: ConfiguratorType { | |
typealias ViewModel = IconViewViewModel | |
typealias Container = IconViewContainer | |
static func configure(with viewModel: IconViewViewModel, for container: IconViewContainer) { | |
} | |
} | |
struct RectangleConfigurator: ConfiguratorType { | |
typealias ViewModel = IconViewViewModel | |
typealias Container = IconViewContainer | |
static func configure(with viewModel: IconViewViewModel, for container: IconViewContainer) { | |
} | |
} | |
struct SuperEllipseLayoutMaker: LayoutMakerType { | |
static func addSubviews(for container: IconViewContainer) { | |
} | |
static func makeConstraints(for container: IconViewContainer) { | |
} | |
} | |
struct RoundLayoutMaker: LayoutMakerType { | |
static func addSubviews(for container: IconViewContainer) { | |
} | |
static func makeConstraints(for container: IconViewContainer) { | |
} | |
} | |
struct RectangleLayoutMaker: LayoutMakerType { | |
static func addSubviews(for container: IconViewContainer) { | |
} | |
static func makeConstraints(for container: IconViewContainer) { | |
} | |
} | |
enum IconViewSize { | |
case s, m, l | |
} | |
protocol IconViewSizeStyleType { | |
static var size: IconViewSize { get } | |
} | |
enum IconViewSizeStyle { | |
struct Small: IconViewSizeStyleType { | |
static var size: IconViewSize { .s } | |
typealias SuperEllipse = IconViewShapeStyle.SuperEllipse<Self> | |
typealias Round = IconViewShapeStyle.Round<Self> | |
typealias Rectangle = IconViewShapeStyle.Rectangle<Self> | |
} | |
struct Medium: IconViewSizeStyleType { | |
static var size: IconViewSize { .m } | |
typealias SuperEllipse = IconViewShapeStyle.SuperEllipse<Self> | |
typealias Round = IconViewShapeStyle.Round<Self> | |
typealias Rectangle = IconViewShapeStyle.Rectangle<Self> | |
} | |
struct Large: IconViewSizeStyleType { | |
static var size: IconViewSize { .l } | |
typealias SuperEllipse = IconViewShapeStyle.SuperEllipse<Self> | |
typealias Round = IconViewShapeStyle.Round<Self> | |
typealias Rectangle = IconViewShapeStyle.Rectangle<Self> | |
} | |
} | |
protocol IconViewShapeStyleType: IconViewSizeStyleType, StyleType { | |
associatedtype Size: IconViewSizeStyleType | |
} | |
extension IconViewShapeStyleType { | |
static var size: IconViewSize { Size.size } | |
} | |
enum IconViewShapeStyle { | |
enum SuperEllipse<Size: IconViewSizeStyleType>: IconViewShapeStyleType, IconViewStyleType { | |
typealias Configurator = SuperEllipseConfigurator | |
typealias Layout = SuperEllipseLayoutMaker | |
} | |
enum Round<Size: IconViewSizeStyleType>: IconViewShapeStyleType, IconViewStyleType { | |
typealias Configurator = RoundConfigurator | |
typealias Layout = RoundLayoutMaker | |
} | |
enum Rectangle<Size: IconViewSizeStyleType>: IconViewShapeStyleType, IconViewStyleType { | |
typealias Configurator = RectangleConfigurator | |
typealias Layout = RectangleLayoutMaker | |
} | |
} | |
typealias IconViewStyle = IconViewSizeStyle | |
protocol IconViewStyleType: IconViewShapeStyleType { } | |
let iconView = IconView<IconViewStyle.Small.Rectangle>() | |
let simpleView = SimpleView< | |
SimpleViewStyle.Default<IconViewStyle.Small.SuperEllipse> | |
>() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment