Skip to content

Instantly share code, notes, and snippets.

@ABashkirova
Last active June 7, 2021 10:01
Show Gist options
  • Save ABashkirova/007e8a692e30b95c9af4f3fda20ca73d to your computer and use it in GitHub Desktop.
Save ABashkirova/007e8a692e30b95c9af4f3fda20ca73d to your computer and use it in GitHub Desktop.
UI Components
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