78
Интерфейс по кусочкам Зимин Александр

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

Embed Size (px)

Citation preview

Интерфейс по кусочкам Зимин Александр

iOS Developer UI Engineer

CocoaHeadsRussia

macOS Developer Co-Founder Организатор

Маркетинг, Дизайн, Аналитика, …

VIPERSwift

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

Спасибо за внимание!

Зимин Александр [email protected]

@ZiminAlex