SlideShare a Scribd company logo
1 of 78
Download to read offline
Интерфейс по кусочкам
Зимин Александр
iOS Developer
UI Engineer
CocoaHeads
Russia
macOS Developer
Co-Founder
Организатор
Маркетинг, Дизайн, Аналитика, …
VIPER
Swift
RxFastline
Promises MVVM
Protocol-Oriented Programming
Realm
План
• Проблемы
План
• Проблемы
• Цвета и шрифты
Дизайнер
Разработчик
План
• Проблемы
• Цвета и шрифты
• Передача ассетов
План
• Проблемы
• Цвета и шрифты
• Передача ассетов
• Свизлинг для дизайна
План
• Проблемы
• Цвета и шрифты
• Передача ассетов
• Свизлинг для дизайна
• Планы на будущее
Проблемы
• Дизайн:
• Может поменяться
• Много рутины
• Не вызывает runtime или compile ошибки
Цвета
7F98BB
7F98BB
7F98BB
3870BE
3870BE
3870BE
&%*k?
&%*k?
extension UIColor {
static var appBlue: UIColor {
return UIColor(hexString: "7F98BB")
}
}
extension UIColor {
static var mainColor: UIColor {
return UIColor(hexString: "7F98BB")
}
}
&%*k?
extension UIColor {
enum ColorType: String {
case gray = "8C8C8C"
case darkGray = "6C6C6C"
}
convenience init(_ colorType: ColorType) {
self.init(hexString: colorType.rawValue)
}
}
extension UIColor {
enum ComponentType {
case navigationBar
case separator
var colorType: ColorType {
switch self {
case .navigationBar:
return .gray
case .separator:
return .darkGray
}
}
}
convenience init(componentType: ComponentType) {
self.init(componentType.colorType)
}
}
extension UIColor {
enum ComponentType {
case navigationBar
case separator
var colorType: ColorType {
switch self {
case .navigationBar:
return .gray
case .separator:
return .darkGray
}
}
}
convenience init(componentType: ComponentType) {
self.init(componentType.colorType)
}
}
Не может содержать HEX строк!
let grayColor = UIColor(.gray)
let separatorColor =
UIColor(componentType: .separator)
Шрифты
extension UIFont {
enum FontType {
case gothamProMedium(size: CGFloat)
case gothamPro(size: CGFloat)
}
convenience init(_ fontType: FontType) {
switch fontType {
case let .gothamProMedium(size):
self.init(name: "GothamPro-Medium", size: size)!
case let .gothamPro(size):
self.init(name: "GothamPro", size: size)!
}
}
}
https://github.com/krzysztofzablocki/Sourcery
Передача ассетов
x3 Labels
Switcher
States
Label
Button
http://utom.design/measure/
https://zeplin.io
https://zeplin.io
Свизлинг
viewDidLoad customViewDidLoad
viewDidLoad customViewDidLoad
• При вызове viewDidLoad вызывается customViewDidLoad
• При вызове customViewDidLoad вызывается viewDidLoad
private var swizzleTextValue: Void = {
swizzleMethods(objectClass: UILabel.self,
originalSelector: #selector(setter: UILabel.text),
swizzledSelector: #selector(UILabel.setStaticText(_:)))
}()
extension UILabel {
open override class func initialize() {
_ = swizzleTextValue
}
@objc
fileprivate func setStaticText(_ text: String?) {
setStaticText(text)
// You actions
}
}
private var swizzleTextValue: Void = {
swizzleMethods(objectClass: UILabel.self,
originalSelector: #selector(setter: UILabel.text),
swizzledSelector: #selector(UILabel.setStaticText(_:)))
}()
extension UILabel {
open override class func initialize() {
_ = swizzleTextValue
}
@objc
fileprivate func setStaticText(_ text: String?) {
setStaticText(text)
// You actions
}
}
private var swizzleTextValue: Void = {
swizzleMethods(objectClass: UILabel.self,
originalSelector: #selector(setter: UILabel.text),
swizzledSelector: #selector(UILabel.setStaticText(_:)))
}()
extension UILabel {
open override class func initialize() {
_ = swizzleTextValue
}
@objc
fileprivate func setStaticText(_ text: String?) {
setStaticText(text)
// You actions
}
}
Плохо!
extension UILabel {
static func swizzleText() {
_ = swizzleTextValue
}
@objc
fileprivate func setStaticText(_ text: String?) {
setStaticText(text)
// You actions
}
}
extension UILabel {
static func swizzleText() {
_ = swizzleTextValue
}
// ...
}
• Вызывать swizzleText():
• Где нибудь в AppDelegate или его
проксировании
• Вызывать в load() у UILabel в Obj-c
Свизлинг
Это безопасно?
Это быстро?
let value = clock()
for i in 0..<1000000 {
cell.textLabel?.text = "(i)"
}
let result = Double(clock() - value)
print(result)
До свизлинга Плосле свизлинга
2463609.0 2665523.0
2238332.0 2565639.0
2240086.0 2560924.0
2278225.0 2579298.0
2247269.0 2561646.0
2246613.0 2563499.0
2228041.0 2571111.0
15%
Локализация
private var swizzleTextValue: Void = {
swizzleMethods(objectClass: UILabel.self,
originalSelector: #selector(setter: UILabel.text),
swizzledSelector: #selector(UILabel.setLocalizedText(_:)))
}()
extension UILabel {
static func swizzleText() {
_ = swizzleTextValue
}
@objc
fileprivate func setLocalizedText(_ text: String?) {
if let text = text, !isInsideButton {
setLocalizedText(text.tripleLengthPseudolanguage)
} else {
setLocalizedText(text)
}
}
private var isInsideButton: Bool {
let superview = self.superview
return superview is UIButton
}
}
Керн
@implementation UINavigationItem (AZTitleConfiguration)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SwizzleInstanceMethod(self, @selector(setTitle:), @selector(az_setTitleWithConfiguration:));
});
}
@end
// We setup view with our own label, because we want to use kern spacing and other appearance staf
- (void)az_setTitleWithConfiguration:(NSString *)title
{
if (title == nil) {
title = @"";
}
[self az_setTitleWithConfiguration:[title uppercaseString]];
NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString]
attributes:attributes];
// … fill self.titleView if already UILabel
UILabel *titleLabel = [UILabel new];
titleLabel.attributedText = attributedString;
[titleLabel sizeToFit];
self.titleView = titleLabel;
}
// We setup view with our own label, because we want to use kern spacing and other appearance staf
- (void)az_setTitleWithConfiguration:(NSString *)title
{
if (title == nil) {
title = @"";
}
[self az_setTitleWithConfiguration:[title uppercaseString]];
NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString]
attributes:attributes];
// … fill self.titleView if already UILabel
UILabel *titleLabel = [UILabel new];
titleLabel.attributedText = attributedString;
[titleLabel sizeToFit];
self.titleView = titleLabel;
}
// We setup view with our own label, because we want to use kern spacing and other appearance staf
- (void)az_setTitleWithConfiguration:(NSString *)title
{
if (title == nil) {
title = @"";
}
[self az_setTitleWithConfiguration:[title uppercaseString]];
NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString]
attributes:attributes];
// … fill self.titleView if already UILabel
UILabel *titleLabel = [UILabel new];
titleLabel.attributedText = attributedString;
[titleLabel sizeToFit];
self.titleView = titleLabel;
}
// We setup view with our own label, because we want to use kern spacing and other appearance staf
- (void)az_setTitleWithConfiguration:(NSString *)title
{
if (title == nil) {
title = @"";
}
[self az_setTitleWithConfiguration:[title uppercaseString]];
NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString]
attributes:attributes];
// … fill self.titleView if already UILabel
UILabel *titleLabel = [UILabel new];
titleLabel.attributedText = attributedString;
[titleLabel sizeToFit];
self.titleView = titleLabel;
}
if ([self.titleView isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel*)self.titleView;
label.attributedText = attributedString;
return;
}
Лейаут
@interface NSLayoutConstraint (UCConstraintBuild)
@property (nonatomic, assign) IBInspectable CGFloat
screenWidthPercent;
@property (nonatomic, assign) IBInspectable CGFloat
screenHeightPercent;
@end
@implementation NSLayoutConstraint (UCConstraintBuild)
- (void)setScreenWidthPercent:(CGFloat)screenWidthPercent
{
self.constant = round([self screenBounds].size.width * screenWidthPercent);
}
- (CGFloat)screenWidthPercent
{
return (self.constant / [self screenBounds].size.width);
}
@end
Планы на будущее
https://github.com/krzyzanowskim/Natalie
https://github.com/SwiftGen/SwiftGen
{
"colors": [
{
"name": "lightWhite",
"color": "F5F5F5"
}
],
"fonts": [
{
"name": "bold",
"fontName": "ProximaNova-Extrabld"
}
]
}
"code_templates": {
"font": "UIFont(.$name, size: $size)",
"textColor": "UIColor(.$)",
"backgroundColor": "UIColor(.$)"
}
"styles": {
"text.default": {
"font.size": 20,
"font.name": "SFUIText-Light",
"textColor": "red"
},
"text.title": {
"parents": ["text.default"],
"font.name": "bold",
"textColor": "$themeColor"
}
}
extension UILabel: Stylable {
func apply(style: Style) {
// ...
}
}
label.apply(style: Styles.Text.Title)
One more thing
@objc protocol Foo {
init()
}
final class Bar: Foo {
init() {}
deinit { print("destroying") }
}
let x: Foo.Type = Bar.self
_ = x.init()
@objc protocol Foo {
init()
}
final class Bar: Foo {
init() {}
deinit { print("destroying") }
}
let x: Foo.Type = Bar.self
_ = x.init()
protocol Foo {
init()
}
final class Bar: Foo {
init() {}
deinit { print("destroying") }
}
let x: Foo.Type = Bar.self
_ = x.init()
https://bugs.swift.org/browse/SR-3935
Спасибо за
внимание!
Зимин Александр
azimin@me.com
@ZiminAlex

More Related Content

More from CodeFest

Никита Прокопов
Никита ПрокоповНикита Прокопов
Никита ПрокоповCodeFest
 
Денис Баталов
Денис БаталовДенис Баталов
Денис БаталовCodeFest
 
Елена Гальцина
Елена ГальцинаЕлена Гальцина
Елена ГальцинаCodeFest
 
Александр Калашников
Александр КалашниковАлександр Калашников
Александр КалашниковCodeFest
 
Ирина Иванова
Ирина ИвановаИрина Иванова
Ирина ИвановаCodeFest
 
Marko Berković
Marko BerkovićMarko Berković
Marko BerkovićCodeFest
 
Денис Кортунов
Денис КортуновДенис Кортунов
Денис КортуновCodeFest
 
Сергей Крапивенский
Сергей КрапивенскийСергей Крапивенский
Сергей КрапивенскийCodeFest
 
Сергей Игнатов
Сергей ИгнатовСергей Игнатов
Сергей ИгнатовCodeFest
 
Николай Крапивный
Николай КрапивныйНиколай Крапивный
Николай КрапивныйCodeFest
 
Alexander Graebe
Alexander GraebeAlexander Graebe
Alexander GraebeCodeFest
 
Вадим Смирнов
Вадим СмирновВадим Смирнов
Вадим СмирновCodeFest
 
Константин Осипов
Константин ОсиповКонстантин Осипов
Константин ОсиповCodeFest
 
Raffaele Rialdi
Raffaele RialdiRaffaele Rialdi
Raffaele RialdiCodeFest
 
Максим Пугачев
Максим ПугачевМаксим Пугачев
Максим ПугачевCodeFest
 
Rene Groeschke
Rene GroeschkeRene Groeschke
Rene GroeschkeCodeFest
 
Иван Бондаренко
Иван БондаренкоИван Бондаренко
Иван БондаренкоCodeFest
 
Mete Atamel
Mete AtamelMete Atamel
Mete AtamelCodeFest
 
Алексей Акулович
Алексей АкуловичАлексей Акулович
Алексей АкуловичCodeFest
 
Артем Титаренко
Артем ТитаренкоАртем Титаренко
Артем ТитаренкоCodeFest
 

More from CodeFest (20)

Никита Прокопов
Никита ПрокоповНикита Прокопов
Никита Прокопов
 
Денис Баталов
Денис БаталовДенис Баталов
Денис Баталов
 
Елена Гальцина
Елена ГальцинаЕлена Гальцина
Елена Гальцина
 
Александр Калашников
Александр КалашниковАлександр Калашников
Александр Калашников
 
Ирина Иванова
Ирина ИвановаИрина Иванова
Ирина Иванова
 
Marko Berković
Marko BerkovićMarko Berković
Marko Berković
 
Денис Кортунов
Денис КортуновДенис Кортунов
Денис Кортунов
 
Сергей Крапивенский
Сергей КрапивенскийСергей Крапивенский
Сергей Крапивенский
 
Сергей Игнатов
Сергей ИгнатовСергей Игнатов
Сергей Игнатов
 
Николай Крапивный
Николай КрапивныйНиколай Крапивный
Николай Крапивный
 
Alexander Graebe
Alexander GraebeAlexander Graebe
Alexander Graebe
 
Вадим Смирнов
Вадим СмирновВадим Смирнов
Вадим Смирнов
 
Константин Осипов
Константин ОсиповКонстантин Осипов
Константин Осипов
 
Raffaele Rialdi
Raffaele RialdiRaffaele Rialdi
Raffaele Rialdi
 
Максим Пугачев
Максим ПугачевМаксим Пугачев
Максим Пугачев
 
Rene Groeschke
Rene GroeschkeRene Groeschke
Rene Groeschke
 
Иван Бондаренко
Иван БондаренкоИван Бондаренко
Иван Бондаренко
 
Mete Atamel
Mete AtamelMete Atamel
Mete Atamel
 
Алексей Акулович
Алексей АкуловичАлексей Акулович
Алексей Акулович
 
Артем Титаренко
Артем ТитаренкоАртем Титаренко
Артем Титаренко
 

Александр Зимин

  • 2. iOS Developer UI Engineer CocoaHeads Russia macOS Developer Co-Founder Организатор Маркетинг, Дизайн, Аналитика, …
  • 5. План • Проблемы • Цвета и шрифты Дизайнер Разработчик
  • 6. План • Проблемы • Цвета и шрифты • Передача ассетов
  • 7. План • Проблемы • Цвета и шрифты • Передача ассетов • Свизлинг для дизайна
  • 8. План • Проблемы • Цвета и шрифты • Передача ассетов • Свизлинг для дизайна • Планы на будущее
  • 10. • Дизайн: • Может поменяться • Много рутины • Не вызывает runtime или compile ошибки
  • 13. extension UIColor { static var appBlue: UIColor { return UIColor(hexString: "7F98BB") } }
  • 14. extension UIColor { static var mainColor: UIColor { return UIColor(hexString: "7F98BB") } }
  • 15. &%*k?
  • 16. extension UIColor { enum ColorType: String { case gray = "8C8C8C" case darkGray = "6C6C6C" } convenience init(_ colorType: ColorType) { self.init(hexString: colorType.rawValue) } }
  • 17. extension UIColor { enum ComponentType { case navigationBar case separator var colorType: ColorType { switch self { case .navigationBar: return .gray case .separator: return .darkGray } } } convenience init(componentType: ComponentType) { self.init(componentType.colorType) } }
  • 18. extension UIColor { enum ComponentType { case navigationBar case separator var colorType: ColorType { switch self { case .navigationBar: return .gray case .separator: return .darkGray } } } convenience init(componentType: ComponentType) { self.init(componentType.colorType) } } Не может содержать HEX строк!
  • 19. let grayColor = UIColor(.gray) let separatorColor = UIColor(componentType: .separator)
  • 21. extension UIFont { enum FontType { case gothamProMedium(size: CGFloat) case gothamPro(size: CGFloat) } convenience init(_ fontType: FontType) { switch fontType { case let .gothamProMedium(size): self.init(name: "GothamPro-Medium", size: size)! case let .gothamPro(size): self.init(name: "GothamPro", size: size)! } } }
  • 24.
  • 25.
  • 27.
  • 28.
  • 29.
  • 30.
  • 36. viewDidLoad customViewDidLoad • При вызове viewDidLoad вызывается customViewDidLoad • При вызове customViewDidLoad вызывается viewDidLoad
  • 37. private var swizzleTextValue: Void = { swizzleMethods(objectClass: UILabel.self, originalSelector: #selector(setter: UILabel.text), swizzledSelector: #selector(UILabel.setStaticText(_:))) }() extension UILabel { open override class func initialize() { _ = swizzleTextValue } @objc fileprivate func setStaticText(_ text: String?) { setStaticText(text) // You actions } }
  • 38. private var swizzleTextValue: Void = { swizzleMethods(objectClass: UILabel.self, originalSelector: #selector(setter: UILabel.text), swizzledSelector: #selector(UILabel.setStaticText(_:))) }() extension UILabel { open override class func initialize() { _ = swizzleTextValue } @objc fileprivate func setStaticText(_ text: String?) { setStaticText(text) // You actions } }
  • 39. private var swizzleTextValue: Void = { swizzleMethods(objectClass: UILabel.self, originalSelector: #selector(setter: UILabel.text), swizzledSelector: #selector(UILabel.setStaticText(_:))) }() extension UILabel { open override class func initialize() { _ = swizzleTextValue } @objc fileprivate func setStaticText(_ text: String?) { setStaticText(text) // You actions } } Плохо!
  • 40.
  • 41.
  • 42. extension UILabel { static func swizzleText() { _ = swizzleTextValue } @objc fileprivate func setStaticText(_ text: String?) { setStaticText(text) // You actions } }
  • 43. extension UILabel { static func swizzleText() { _ = swizzleTextValue } // ... } • Вызывать swizzleText(): • Где нибудь в AppDelegate или его проксировании • Вызывать в load() у UILabel в Obj-c
  • 47. let value = clock() for i in 0..<1000000 { cell.textLabel?.text = "(i)" } let result = Double(clock() - value) print(result)
  • 48.
  • 49. До свизлинга Плосле свизлинга 2463609.0 2665523.0 2238332.0 2565639.0 2240086.0 2560924.0 2278225.0 2579298.0 2247269.0 2561646.0 2246613.0 2563499.0 2228041.0 2571111.0 15%
  • 51.
  • 52. private var swizzleTextValue: Void = { swizzleMethods(objectClass: UILabel.self, originalSelector: #selector(setter: UILabel.text), swizzledSelector: #selector(UILabel.setLocalizedText(_:))) }()
  • 53. extension UILabel { static func swizzleText() { _ = swizzleTextValue } @objc fileprivate func setLocalizedText(_ text: String?) { if let text = text, !isInsideButton { setLocalizedText(text.tripleLengthPseudolanguage) } else { setLocalizedText(text) } } private var isInsideButton: Bool { let superview = self.superview return superview is UIButton } }
  • 54.
  • 56. @implementation UINavigationItem (AZTitleConfiguration) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SwizzleInstanceMethod(self, @selector(setTitle:), @selector(az_setTitleWithConfiguration:)); }); } @end
  • 57. // We setup view with our own label, because we want to use kern spacing and other appearance staf - (void)az_setTitleWithConfiguration:(NSString *)title { if (title == nil) { title = @""; } [self az_setTitleWithConfiguration:[title uppercaseString]]; NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes]; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString] attributes:attributes]; // … fill self.titleView if already UILabel UILabel *titleLabel = [UILabel new]; titleLabel.attributedText = attributedString; [titleLabel sizeToFit]; self.titleView = titleLabel; }
  • 58. // We setup view with our own label, because we want to use kern spacing and other appearance staf - (void)az_setTitleWithConfiguration:(NSString *)title { if (title == nil) { title = @""; } [self az_setTitleWithConfiguration:[title uppercaseString]]; NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes]; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString] attributes:attributes]; // … fill self.titleView if already UILabel UILabel *titleLabel = [UILabel new]; titleLabel.attributedText = attributedString; [titleLabel sizeToFit]; self.titleView = titleLabel; }
  • 59. // We setup view with our own label, because we want to use kern spacing and other appearance staf - (void)az_setTitleWithConfiguration:(NSString *)title { if (title == nil) { title = @""; } [self az_setTitleWithConfiguration:[title uppercaseString]]; NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes]; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString] attributes:attributes]; // … fill self.titleView if already UILabel UILabel *titleLabel = [UILabel new]; titleLabel.attributedText = attributedString; [titleLabel sizeToFit]; self.titleView = titleLabel; }
  • 60. // We setup view with our own label, because we want to use kern spacing and other appearance staf - (void)az_setTitleWithConfiguration:(NSString *)title { if (title == nil) { title = @""; } [self az_setTitleWithConfiguration:[title uppercaseString]]; NSDictionary *attributes = [[UINavigationBar appearance] titleTextAttributes]; NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[title uppercaseString] attributes:attributes]; // … fill self.titleView if already UILabel UILabel *titleLabel = [UILabel new]; titleLabel.attributedText = attributedString; [titleLabel sizeToFit]; self.titleView = titleLabel; } if ([self.titleView isKindOfClass:[UILabel class]]) { UILabel *label = (UILabel*)self.titleView; label.attributedText = attributedString; return; }
  • 62.
  • 63. @interface NSLayoutConstraint (UCConstraintBuild) @property (nonatomic, assign) IBInspectable CGFloat screenWidthPercent; @property (nonatomic, assign) IBInspectable CGFloat screenHeightPercent; @end
  • 64. @implementation NSLayoutConstraint (UCConstraintBuild) - (void)setScreenWidthPercent:(CGFloat)screenWidthPercent { self.constant = round([self screenBounds].size.width * screenWidthPercent); } - (CGFloat)screenWidthPercent { return (self.constant / [self screenBounds].size.width); } @end
  • 65.
  • 69. { "colors": [ { "name": "lightWhite", "color": "F5F5F5" } ], "fonts": [ { "name": "bold", "fontName": "ProximaNova-Extrabld" } ] }
  • 70. "code_templates": { "font": "UIFont(.$name, size: $size)", "textColor": "UIColor(.$)", "backgroundColor": "UIColor(.$)" }
  • 71. "styles": { "text.default": { "font.size": 20, "font.name": "SFUIText-Light", "textColor": "red" }, "text.title": { "parents": ["text.default"], "font.name": "bold", "textColor": "$themeColor" } }
  • 72. extension UILabel: Stylable { func apply(style: Style) { // ... } } label.apply(style: Styles.Text.Title)
  • 74. @objc protocol Foo { init() } final class Bar: Foo { init() {} deinit { print("destroying") } } let x: Foo.Type = Bar.self _ = x.init()
  • 75. @objc protocol Foo { init() } final class Bar: Foo { init() {} deinit { print("destroying") } } let x: Foo.Type = Bar.self _ = x.init()
  • 76. protocol Foo { init() } final class Bar: Foo { init() {} deinit { print("destroying") } } let x: Foo.Type = Bar.self _ = x.init()