Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BonMot Talk @ Swift Office Hours 2017

BonMot Talk @ Swift Office Hours 2017

Learn about BonMot, a library for making beautiful typography on iOS. Full Keynote deck is posted at https://cl.ly/jJqc.

Zev Eisenberg

February 23, 2017
Tweet

More Decks by Zev Eisenberg

Other Decks in Technology

Transcript

  1. Adobe Illustrator OpenType Panel Figure Style Figure Position Contextual Alternates

    Discretionary Ligatures Swash Stylistic Alternates Titling Alternates Ordinals Fractions Standard Ligatures
  2. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text ¶ Hard to style ranges of strings
  3. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text ¶ Hard to style ranges of strings ¶ Special characters are hard to read
  4. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text ¶ Hard to style ranges of strings ¶ Special characters are hard to read ¶ Dynamic Type API is limited
  5. ¶ Unpredictable styling of common views UILabel font lineBreakMode shadowColor

    shadowOffset text textAlignment textColor UITextField font - - - text textAlignment textColor UIButton - - titleShadowColor - title - titleColor +titleLabel
  6. ¶ NSAttributedString is hard to use let label = UILabel()

    
 let paragraphStyle = NSMutableParagraphStyle()
 paragraphStyle.lineHeightMultiple = 0.7
 paragraphStyle.alignment = .center
 Swift Office Hours
  7. ¶ NSAttributedString is hard to use let label = UILabel()

    
 let paragraphStyle = NSMutableParagraphStyle()
 paragraphStyle.lineHeightMultiple = 0.7
 paragraphStyle.alignment = .center
 let attributes = [
 NSFontAttributeName: UIFont.systemFont(ofSize: 150),
 NSForegroundColorAttributeName: UIColor.red,
 NSParagraphStyleAttributeName: paragraphStyle,
 ]
 Swift Office Hours
  8. ¶ NSAttributedString is hard to use let label = UILabel()

    
 let paragraphStyle = NSMutableParagraphStyle()
 paragraphStyle.lineHeightMultiple = 0.7
 paragraphStyle.alignment = .center
 let attributes = [
 NSFontAttributeName: UIFont.systemFont(ofSize: 150),
 NSForegroundColorAttributeName: UIColor.red,
 NSParagraphStyleAttributeName: paragraphStyle,
 ]
 label.attributedText = NSAttributedString(
 string: "Swift Office Hours",
 attributes: attributes) Swift Office Hours
  9. ¶ NSAttributedString is hard to use let label = UILabel()


    let style = StringStyle(
 .font(UIFont.systemFont(ofSize: 150)),
 .color(.red),
 .lineHeightMultiple(0.7),
 .alignment(.center)
 )
 Swift Office Hours easy
  10. ¶ NSAttributedString is hard to use let label = UILabel()


    let style = StringStyle(
 .font(UIFont.systemFont(ofSize: 150)),
 .color(.red),
 .lineHeightMultiple(0.7),
 .alignment(.center)
 )
 let string = "Swift Office Hours".styled(with: style)
 Swift Office Hours easy
  11. ¶ NSAttributedString is hard to use let label = UILabel()


    let style = StringStyle(
 .font(UIFont.systemFont(ofSize: 150)),
 .color(.red),
 .lineHeightMultiple(0.7),
 .alignment(.center)
 )
 let string = "Swift Office Hours".styled(with: style)
 label.attributedText = string Swift Office Hours easy
  12. let label = UILabel()
 let style = StringStyle(
 .font(UIFont.systemFont(ofSize: 150)),


    .color(.red),
 .lineHeightMultiple(0.7),
 .alignment(.center),
 .tracking(.point(15))
 )
 let string = "Swift Office Hours".styled(with: style)
 label.attributedText = string Swift Office Hours ¶ NSAttributedString is hard to use easy
  13. let label = UILabel()
 let style = StringStyle(
 .font(UIFont.systemFont(ofSize: 150)),


    .color(.red),
 .lineHeightMultiple(0.7),
 .alignment(.center),
 .tracking(.adobe(1000))
 )
 let string = "Swift Office Hours".styled(with: style)
 label.attributedText = string ¶ NSAttributedString is hard to use easy Swift Office Hours
  14. Figure Styles 0123456789 1111111111 0123456789 1111111111 0123456789 1111111111 0123456789 1111111111

    uppercase lowercase proportional monospaced let originalDescriptor = myFont.fontDescriptor let featureSettings = [ [ UIFontFeatureTypeIdentifierKey: kNumberSpacingType, UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector, ], ] let attributes = [ UIFontDescriptorFeatureSettingsAttribute: featureSettings, ] let fontDescriptor = originalDescriptor.addingAttributes(attributes) let font = UIFont(descriptor: fontDescriptor, size: 0)
  15. let label = UILabel()
 let style = StringStyle(
 .font(UIFont.systemFont(ofSize: 150)),


    .color(.red),
 .lineHeightMultiple(0.7),
 .alignment(.center),
 .tracking(.adobe(1000)),
 .numberSpacing(.monospaced), .numberCase(.upper)
 )
 let string = "Swift Office Hours".styled(with: style)
 label.attributedText = string ¶ NSAttributedString is hard to use easy Swift Office Hours
  16. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() Office Hours
  17. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = swift Office Hours
  18. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) swift Office Hours
  19. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) let imageString = NSAttributedString(attachment: attachment) swift Office Hours
  20. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) let imageString = NSAttributedString(attachment: attachment) let fullString = NSMutableAttributedString(string: " Office Hours") swift Office Hours
  21. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) let imageString = NSAttributedString(attachment: attachment) let fullString = NSMutableAttributedString(string: " Office Hours") fullString.insert(imageString, at: 0) swift Office Hours
  22. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) let imageString = NSAttributedString(attachment: attachment) let fullString = NSMutableAttributedString(string: " Office Hours") fullString.insert(imageString, at: 0) UNCAUGHT EXCEPTION (NSInvalidUnarchiveOperationException): *** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (UIImage) for key (NS.image); the class may be defined in source code or a library that is not linked swift Office Hours
  23. ¶ Hard to wrap images with text let attachment =

    NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) let imageString = NSAttributedString(attachment: attachment) let fullString = NSMutableAttributedString(string: " Office Hours") fullString.insert(imageString, at: 0) UNCAUGHT EXCEPTION (NSInvalidUnarchiveOperationException): *** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (UIImage) for key (NS.image); the class may be defined in source code or a library that is not linked rdar://30605012 swift Office Hours
  24. Office Hours ¶ Hard to wrap images with text Surprisingly

    easy let fullString = NSAttributedString.composed(of: [ , " Office Hours", ]) let attachment = NSTextAttachment() attachment.image = attachment.bounds = CGRect(origin: .zero, size: myImage.size) let imageString = NSAttributedString(attachment: attachment) let fullString = NSMutableAttributedString(string: " Office Hours") fullString.insert(imageString, at: 0) swift swift
  25. ¶ Hard to style ranges of strings I went to

    Swift Office Hours, and it was not too cold!
  26. ¶ Hard to style ranges of strings I went to

    Swift Office Hours, and it was not too cold! Je suis allé à Swift Office Hours, et il n’était pas trop froid!
  27. 8:21 PM - 13 Mar 2015 NSAttributedString is great because

    it’s the result of years of research that have shown coupling between data and presentation to be ideal. @hyperspacemark Mark Adams ¶ Hard to style ranges of strings
  28. ¶ Hard to style ranges of strings let string =

    "I went to Swift Office Hours, and it was <strong>not</strong> too cold!"
 Trivial
  29. ¶ Hard to style ranges of strings let string =

    "I went to Swift Office Hours, and it was <strong>not</strong> too cold!"
 let strong = StringStyle(
 .font(myBoldFont)
 )
 Trivial
  30. ¶ Hard to style ranges of strings let string =

    "I went to Swift Office Hours, and it was <strong>not</strong> too cold!"
 let strong = StringStyle(
 .font(myBoldFont)
 )
 let style = StringStyle(
 .font(myNormalFont),
 .xmlRules([
 .style("strong", strong),
 ]))
 Trivial
  31. ¶ Hard to style ranges of strings let string =

    "I went to Swift Office Hours, and it was <strong>not</strong> too cold!"
 let strong = StringStyle(
 .font(myBoldFont)
 )
 let style = StringStyle(
 .font(myNormalFont),
 .xmlRules([
 .style("strong", strong),
 ]))
 let attributed = string.styled(with: style) Trivial
  32. ¶ Hard to style ranges of strings let string =

    "Je suis allé à Swift Office Hours, et il <strong>n’</strong>était <strong>pas</strong> trop froid!"
 let strong = StringStyle(
 .font(myBoldFont)
 )
 let style = StringStyle(
 .font(myNormalFont),
 .xmlRules([
 .style("strong", strong),
 ]))
 let attributed = string.styled(with: style) Trivial
  33. ¶ Special characters are hard to read func testSpecialCharacters() {

    // U+00A0: no-break space let test = "Swift\u{00A0}Office\u{00A0}Hours" XCTAssertEqual(test, "Swift Office Hours") }
  34. ¶ Special characters are hard to read func testSpecialCharacters() {

    // U+00A0: no-break space let test = "Swift\u{00A0}Office\u{00A0}Hours" XCTAssertEqual(test, "Swift Office Hours") } ("Swift Office Hours") is not equal to ("Swift Office Hours")
  35. ¶ Special characters are hard to read func testSpecialCharacters() {


    let string = "Swift<BON:noBreakSpace/>Office<BON:noBreakSpace/>Hours" painless
  36. ¶ Special characters are hard to read func testSpecialCharacters() {


    let string = "Swift<BON:noBreakSpace/>Office<BON:noBreakSpace/>Hours" let attributed = string.styled(with: .xmlRules([])) painless
  37. ¶ Special characters are hard to read func testSpecialCharacters() {


    let string = "Swift<BON:noBreakSpace/>Office<BON:noBreakSpace/>Hours" let attributed = string.styled(with: .xmlRules([])) let test = attributed.bonMotDebugString painless
  38. ¶ Special characters are hard to read func testSpecialCharacters() {


    let string = "Swift<BON:noBreakSpace/>Office<BON:noBreakSpace/>Hours" let attributed = string.styled(with: .xmlRules([])) let test = attributed.bonMotDebugString XCTAssertEqual(test, "Swift Office Hours")
 } painless
  39. ¶ Special characters are hard to read func testSpecialCharacters() {


    let string = "Swift<BON:noBreakSpace/>Office<BON:noBreakSpace/>Hours" let attributed = string.styled(with: .xmlRules([])) let test = attributed.bonMotDebugString XCTAssertEqual(test, "Swift Office Hours")
 } painless ("Swift<BON:noBreakSpace/>Office<BON:noBreakSpace/>Hours") is not equal to ("Swift Office Hours")
  40. ¶ Dynamic Type API is limited let font = UIFont.preferredFont(forTextStyle:

    .headline)
 NotificationCenter.default.addObserver(
 self,
 selector: #selector(changedSize),
 name: .UIContentSizeCategoryDidChange,
 object: nil)

  41. ¶ Dynamic Type API is limited let font = UIFont.preferredFont(forTextStyle:

    .headline)
 NotificationCenter.default.addObserver(
 self,
 selector: #selector(changedSize),
 name: .UIContentSizeCategoryDidChange,
 object: nil)
 override func traitCollectionDidChange(_ previous: UITraitCollection?) {
 super.traitCollectionDidChange(previous)
 ... // iOS 10+
 }
  42. ¶ Dynamic Type API is limited rad 0 5 10

    15 20 25 30 35 40 45 50 55 60 XS S M L (default) XL XXL XXXL AccessibilityM AccessibilityL AccessibilityXL AccessibilityXXL AccessibilityXXXL Caption2 Headline Caption1 Title2 Footnote Title3 Callout Body Subhead
  43. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text Surprisingly easy Predictable-ish easy
  44. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text ¶ Hard to style ranges of strings Trivial Surprisingly easy Predictable-ish easy
  45. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text ¶ Hard to style ranges of strings ¶ Special characters are hard to read painless Trivial Surprisingly easy Predictable-ish easy
  46. ¶ Unpredictable styling of common views ¶ NSAttributedString is hard

    to use ¶ Hard to wrap images with text ¶ Hard to style ranges of strings ¶ Special characters are hard to read ¶ Dynamic Type API is limited rad painless Trivial Surprisingly easy Predictable-ish easy
  47. struct StringStyle { var font: BONFont? var backgroundColor: BONColor? var

    color: BONColor? var underline: (NSUnderlineStyle, BONColor?)? var strikethrough: (NSUnderlineStyle, BONColor?)? var baselineOffset: CGFloat? var lineSpacing: CGFloat? var paragraphSpacingAfter: CGFloat? var alignment: NSTextAlignment? var firstLineHeadIndent: CGFloat? var lineHeightMultiple: CGFloat? // ✂ snip 40 lines var numberCase: NumberCase? var numberSpacing: NumberSpacing? var tracking: Tracking? } StringStyle (360 lines)
  48. StringStyle (360 lines) attributes.update(possibleValue: font, forKey: NSFontAttributeName) attributes.update(possibleValue: link, forKey:

    NSLinkAttributeName) attributes.update(possibleValue: backgroundColor, forKey: NSBackgroundColorAttributeName) attributes.update(possibleValue: color, forKey: NSForegroundColorAttributeName) attributes.update(possibleValue: underline?.0.rawValue, forKey: NSUnderlineStyleAttributeName) attributes.update(possibleValue: underline?.1, forKey: NSUnderlineColorAttributeName) // etc
  49. StringStyle (360 lines) let style = StringStyle() style.font = UIFont.systemFont(ofSize:

    150) style.color = .red style.lineHeightMultiple = 0.7 style.alignment = .center let string = "Swift Office Hours".styled(with: style)
  50. StringStyle.Part (300 lines) extension StringStyle { public enum Part {

    case font(BONFont) case backgroundColor(BONColor) case color(BONColor) case underline(NSUnderlineStyle, BONColor?) case strikethrough(NSUnderlineStyle, BONColor?) case baselineOffset(CGFloat) case alignment(NSTextAlignment) case tracking(Tracking) case lineSpacing(CGFloat) case paragraphSpacingAfter(CGFloat) case firstLineHeadIndent(CGFloat) case numberSpacing(NumberSpacing) case numberCase(NumberCase) case fractions(Fractions) // ✂ snip many lines } }
  51. StringStyle.Part (300 lines) extension StringStyle { public init(_ parts: Part...)

    { self.init(parts) } public init(_ parts: [Part]) { self.init() for part in parts { update(part: part) } } }
  52. StringStyle.Part (300 lines) let style = StringStyle( .font(UIFont.systemFont(ofSize: 150)), .color(.red),

    .lineHeightMultiple(0.7), .alignment(.center) ) let string = "Swift Office Hours".styled(with: style)
  53. iOS + macOS + tvOS + watchOS Compatibility (50 lines)

    #if os(OSX) import AppKit public typealias BONColor = NSColor public typealias BONImage = NSImage public typealias BONTextField = NSTextField public typealias BONFont = NSFont public typealias BONFontDescriptor = NSFontDescriptor let BONFontDescriptorFeatureSettingsAttribute = NSFontFeatureSettingsAttribute let BONFontFeatureTypeIdentifierKey = NSFontFeatureTypeIdentifierKey let BONFontFeatureSelectorIdentifierKey = NSFontFeatureSelectorIdentifierKey #else import UIKit public typealias BONColor = UIColor public typealias BONImage = UIImage public typealias BONFont = UIFont public typealias BONFontDescriptor = UIFontDescriptor let BONFontDescriptorFeatureSettingsAttribute = UIFontDescriptorFeatureSettingsAttribute let BONFontFeatureTypeIdentifierKey = UIFontFeatureTypeIdentifierKey let BONFontFeatureSelectorIdentifierKey = UIFontFeatureSelectorIdentifierKey #if os(iOS) || os(tvOS) public typealias BONTextField = UITextField #endif #endif
  54. Swift 2.x + 3.0 Compatibility (1,000 lines) ¶ Inspired by

    Olivier Halligon’s OHHTTPStubs. https://www.raizlabs.com/dev/2016/10/swift-2-x-3-0-compatibility/
  55. Swift 2.x + 3.0 Compatibility (1,000 lines) ¶ Inspired by

    Olivier Halligon’s OHHTTPStubs. ¶ Write as though you’re targeting 3.0. https://www.raizlabs.com/dev/2016/10/swift-2-x-3-0-compatibility/
  56. Swift 2.x + 3.0 Compatibility (1,000 lines) ¶ Inspired by

    Olivier Halligon’s OHHTTPStubs. ¶ Write as though you’re targeting 3.0. ¶ Where possible, first arguments should have argument labels. https://www.raizlabs.com/dev/2016/10/swift-2-x-3-0-compatibility/
  57. Swift 2.x + 3.0 Compatibility (1,000 lines) ¶ Inspired by

    Olivier Halligon’s OHHTTPStubs. ¶ Write as though you’re targeting 3.0. ¶ Where possible, first arguments should have argument labels. ¶ Backfill Swift 3-style method names in Swift 2.x extensions. https://www.raizlabs.com/dev/2016/10/swift-2-x-3-0-compatibility/