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

Visual Format Languageが 簡単に書けるSwiftライブラリ yavfl

53e469a19bcb4584c87789d237128ca0?s=47 matuyuji
April 11, 2015

Visual Format Languageが 簡単に書けるSwiftライブラリ yavfl

iOS, OS XでAuto LayoutのVisual Format Languageを簡潔に書けるようにするためのライブラリyavfl (https://github.com/safx/yavfl) の実装を簡単に紹介します。

53e469a19bcb4584c87789d237128ca0?s=128

matuyuji

April 11, 2015
Tweet

Transcript

  1. Visual Format Language͕ ؆୯ʹॻ͚ΔSwiftϥΠϒϥϦ yavfl @matuyuji Cocoaษڧձؔ੢ 2015.4.11

  2. @matuyuji safx-dev.blogspot.jp ⌚

  3. Auto Layout

  4. Auto Layout

  5. Auto LayoutΛදݱ͢Δ • XcodeͷετʔϦʔϘʔυ্Ͱ੍໿Λ௥Ճ͢Δ • NSLayoutConstraintͷ constraintWithItem:attribute:relatedBy:toItem:attrib ute:multiplier:constant:Ͱ̍ͭͷ੍໿Λॻ͘ • constraintsWithVisualFormat:options:metrics:view

    sͰෳ਺ͷ੍໿Λॻ͘
  6. Visual Format Language

  7. Visual Format Language |-[find]-[findNext]-[findField(>=20)]-| “Auto Layout Guide” from Apple V:[topField]-10-[bottomField]

  8. 2ͭͷViewΛಉ͡෯Ͱԣʹฒ΂Δ Visual Format Language let dic = ["l": label, "t":

    textField] label.setTranslatesAutoresizingMaskIntoConstraints(false) textField.setTranslatesAutoresizingMaskIntoConstraints(false) let c1 = NSLayoutConstraint.constraintsWithVisualFormat(“V:|-40-[l(32)]", options: nil, metrics: nil, views: dic) let c2 = NSLayoutConstraint.constraintsWithVisualFormat(“V:|-40-[t(32)]", options: nil, metrics: nil, views: dic) let c3 = NSLayoutConstraint.constraintsWithVisualFormat(“|-20-[l(==t)]-10-[t]-|", options: nil, metrics: nil, views: dic) self.view.addConstraints(c1) self.view.addConstraints(c2) self.view.addConstraints(c3)
  9. yavfl: Yet Another VFL visualFormat(label, textField) { l, t in

    .V ~ |-40-[l,==32] .V ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } ΍ Ϳ ; Δ
  10. Visual Format Language yavfl visualFormat(label, textField) { l, t in

    .V ~ |-40-[l,==32] .V ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } let dic = ["l": label, "t": textField] label.setTranslatesAutoresizingMaskIntoConstraints(false) textField.setTranslatesAutoresizingMaskIntoConstraints(false) let c1 = NSLayoutConstraint.constraintsWithVisualFormat(“V:|-40-[l(32)]", options: nil, metrics: nil, views: dic) let c2 = NSLayoutConstraint.constraintsWithVisualFormat(“V:|-40-[t(32)]", options: nil, metrics: nil, views: dic) let c3 = NSLayoutConstraint.constraintsWithVisualFormat(“|-20-[l(==t)]-10-[t]-|", options: nil, metrics: nil, views: dic) self.view.addConstraints(c1) self.view.addConstraints(c2) self.view.addConstraints(c3)
  11. ಛ௃ • Visual Format Language෩ͷݴޠ • (VFLͷ) Predicate΍PriorityͳͲʹ΋ҰԠରԠ • ίϯύΠϧ࣌ʹνΣοΫ͞ΕΔͷͰ҆৺

    • XcodeͰͷิ׬ • Mac, iOS྆ରԠ • CocoaPodsͰ؆୯ಋೖ • Swift 1.2ඇରԠ
  12. yavfl.playground ༡ͼ৔׬උ

  13. Inside yavfl

  14. yavflͷॲཧ (֓ཁ) • SwiftͷݴޠػೳΛ͍Ζ͍Ζ࢖ͬͯɺ • Visual Format LanguageͷจࣈྻΛ࡞ͬͯɺ • NSLayoutConstraintͷ

    constraintsWithVisualFormat:options:metrics:views Λద༻͢Δ͚ͩ ςετ͕ɺZBWqͷදهˠ7'-ͷදهͷ ม׵͕ਖ਼͍͜͠ͱͷνΣοΫ͚ͩͰࡁΉ
  15. yavflͰ࢖ͬͨSwiftͷݴޠػೳ • Trailing Closures • enum • Custom Operators •

    LiteralConvertible • Build Configurations
  16. Trailing Closures

  17. Trailing Closures ࠷ޙͷΫϩʔδϟ͕ؔ਺ͷׅހ֎ʹॻ͚Δ [1,2,3].map({ $0 * $0 }) [1,2,3].map {

    $0 * $0 }
  18. Trailing Closures visualFormat(label, textField) { l, t in .V ~

    |-40-[l,==32] .V ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } func visualFormat(v1: UIView, v2: UIView, closure:(ViewExpression, ViewExpression) -> ())
  19. visualFormat func visualFormat(v1: YAVView, v2: YAVView, closure: (ViewExpression, ViewExpression) ->

    ()) { v1.yav_setTranslatesAutoresizingMaskIntoConstraints(false) v2.yav_setTranslatesAutoresizingMaskIntoConstraints(false) closure(.View(LayoutViewName(v1, 1)), .View(LayoutViewName(v2, 2))) v1.updateConstraints() v2.updateConstraints() }
  20. visualFormat(label, textField) { l, t in .V ~ |-40-[l,==32] .V

    ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } { l, t in .V ~ |-40-[l,==32] .V ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| }
  21. visualFormat(label, textField) { l, t in .V ~ |-40-[l,==32] .V

    ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } .H ~ |-20-[l,==t]-10-[t]-| enum
  22. enum

  23. enum enum LayoutOrientation : String { case V = "V"

    case H = "H" }
  24. Associated Values enum Shape { case Circle(Int) case Rectangle(Int, Int)

    }
  25. Visual Format String Grammarʹج͍ͮͨ஋Λఆٛ enum VisualFormat { case Superview case

    View(LayoutView) case Connection case Predicate(LayoutPredicate) case Number(Int) case Composition([VisualFormat]) case Options(NSLayoutFormatOptions) } | [l] - 20 ==t
  26. LiteralConvertible

  27. IntegerLiteralConvertible enum VisualFormat : IntegerLiteralConvertible { case Superview case View(LayoutView)

    case Connection case Predicate(LayoutPredicate) case Number(Int) case Composition([VisualFormat]) case Options(NSLayoutFormatOptions) } let vf: VisualFormat = 3 IntegerLiteralConvertible case Number(Int) public init(integerLiteral value: IntegerLiteralType) { self = .Number(value) } }
  28. enum VisualFormat : IntegerLiteralConvertible, ArrayLiteralConvertible { case Superview case View(LayoutView)

    case Connection case Predicate(LayoutPredicate) case Number(Int) case Composition([VisualFormat]) case Options(NSLayoutFormatOptions) public init(integerLiteral value: IntegerLiteralType) { self = .Number(value) } public init(arrayLiteral elements: ViewExpression...) { self = .View(LayoutView(elements)) } } nshipster.com/swift-literal-convertible Swift Literal Convertibles let w1: ViewExpression = .View(LayoutViewName(v1, 1)) let w2: ViewExpression = .View(LayoutViewName(v2, 2)) let vf: VisualFormat = [w1,==w2]
  29. visualFormat(label, textField) { l, t in .V ~ |-40-[l,==32] .V

    ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } .H ~ |-20-[l,==t]-10-[t]-| (ArrayLiteralConvertible) VisualFormat (IntegerLiteralConvertible) VisualFormat LayoutOrientation
  30. Custom Operators

  31. Custom Operators • લஔ (prefix), ޙஔ (postfix), தஔ (infix) •

    ༏ઌॱҐ (precedence) • ݁߹ੑ (associativity) a + b * c a ~ (b ~ c) (a ~ b) ~ c a ~ b ~ c ++a a++ a + b a + (b * c) (a + b) * c
  32. visualFormat(label, textField) { l, t in .V ~ |-40-[l,==32] .V

    ~ |-40-[t,==32] .H ~ |-20-[l,==t]-10-[t]-| } .H ~ |-20-[l,==t]-10-[t]-| prefix postfix infix
  33. prefix operator |- {} public prefix func |- (e: VisualFormat)

    -> VisualFormat { return VisualFormat(composition: .Superview, .Connection, e) } .H ~ |-20-[l,==t]-10-[t]-| .Number(20)
  34. .H ~ |-20-[l,==t]-10-[t]-| .H ~ |- - - - -|

    20 l 10 t - | 20 - t | l 10 .H ~ - - - - | 20 - t | l 10 .H ~ - - - public func -(lhs: VisualFormat, rhs: VisualFormat) -> VisualFormat { return VisualFormat(composition: lhs, .Connection, rhs) }
  35. .H ~ |-20-[l,==t]-10-[t]-| public func ~(lhs: LayoutOrientation, rhs: VisualFormat) ->

    [AnyObject] { if let superView = rhs.superView? { let exp = lhs.description + ":" + rhs.description let dic = rhs.viewsDictionary let opts = rhs.options let c = NSLayoutConstraint.constraintsWithVisualFormat(exp, options: opts, metrics: nil, views: dic) superView.addConstraints(c) return c } return [] } - | 20 - t | l 10 .H ~ - - - H:|-20-[l(==t)]-10-[t]-| ==t
  36. Grammar of Operators (from Language Reference) operator → operator-head operator-characters

    opt operator → dot-operator-head dot-operator-characters opt operator-head → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | ? operator-head → U+00A1–U+00A7 operator-head → U+00A9 or U+00AB operator-head → U+00AC or U+00AE operator-head → U+00B0–U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7 operator-head → U+2016–U+2017 or U+2020–U+2027 ɿ # $ @ : , ͳͲ͸࢖͑ͳ͍
  37. mattt/Euler Swift Custom Operators for Mathematical Notation infix operator ∈

    { associativity left } func ∈ <T: Equatable> (left: T, right: [T]) -> Bool { return contains(right, left) } print(2 ∈ [2,3,4]) // true print(1 ∈ [2,3,4]) // false
  38. Build Configurations

  39. Build Configurations CݴޠͷϚΫϩΈ͍ͨͳͷ SwiftͰϚΫϩͬΆ͍͜ͱΛ͢Δ yashigani.hatenablog.com/entry/macros-in-swift #if os(OSX) // OSXͰͷΈ΍Γ͍ͨॲཧ #endif

  40. Build Configurations OSͷҧ͍Λٵऩ #if os(iOS) import UIKit public typealias YAVView

    = UIView #else import AppKit public typealias YAVView = NSView #endif extension YAVView { private func yav_setTranslatesAutoresizingMaskIntoConstraints(flag: Bool) { #if os(iOS) setTranslatesAutoresizingMaskIntoConstraints(flag) #else translatesAutoresizingMaskIntoConstraints = flag #endif } }
  41. OTHER_SWIFT_FLAGS="-DCOCOAPODS" • CocoaPodsͰ͸Ϟδϡʔϧ໊͕ৗʹFoo • ඇCocoaPodsͰ͸ɺMac؀ڥͷΈFooOSXʹͳΔ #if os(iOS) || COCOAPODS import

    Foo #else import FooOSX #endif
  42. ·ͱΊ

  43. ·ͱΊ • Auto LayoutϥΠϒϥϦyavflͰ͸ɺ • Trailing ClosureͷதͰɺ • ArrayLiteralConvertibleͳͲͰద౰ͳenum஋ʹม͑ͭͭɺ •

    Custom OperatorͰͦΕΒΛ഑ྻʹ·ͱΊ͔ͯΒɺ • ͦΕΛจࣈྻʹม׵ͯ͠NSLayoutConstraint. constraintsWithVisualFormatΛ࢖ͬͯ·͢ɻ
  44. Visual Format Language Ͱ͸ॻ͚ͳ͍΋ͷ΋͋Δ • ΞεϖΫτൺͷࢦఆͳͲ imageView.width = 2 *

    imageView.height “Auto Layout Guide” from Apple
  45. ྨࣅϓϩδΣΫτ 0xc010d/VFLToolbox robb/Cartography ConstraintΛॻͨ͘ΊͷϥΠϒϥϦ matteocrippa/awesome-swift view.addConstraints(V:|-[view1]-4-[view2]-|) layout(view1, view2) { view1,

    view2 in view1.width == view1.superview!.width * 0.5 view2.width == view1.width - 50 }