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

AutoLayout以外の選択肢

Ca8210ff0ece2bb6f9fff5fd0770ea64?s=47 Muukii
March 28, 2018

 AutoLayout以外の選択肢

Ca8210ff0ece2bb6f9fff5fd0770ea64?s=128

Muukii

March 28, 2018
Tweet

Transcript

  1. AutoLayoutҎ֎ͷબ୒ࢶ @muukii Akiba.swift 2018.03.28

  2. About Me ‣ muukii <Hiroshi Kimura> ‣ iOS Engineer at

    eureka, Inc. ‣ Pairs Global Team ‣ GitHub : @muukii ‣ https://muukii.me ☕ ⌚
  3. None
  4. Agenda › AutoLayoutͷύϑΥʔϚϯε໰୊ͱ୅ସҊ › ͲΜͳ࣌ʹAutoLayoutͷύϑΥʔϚϯε͕໰୊ʹͳΔͷ͔ › ผͷϨΠΞ΢τΤϯδϯͷ঺հ › TextureGroup/Texture (AsyncDisplayKitʣʹ͍ͭͯ঺հ

    › ࢖͍ํͷงғؾ (࣌ؒ͋Ε͹) › AutoLayoutʹର͢Δײ૝
  5. ίʔυͰAutoLayoutॻ͘ͳΒ › SnapKit/SnapKit › PureLayout/PureLayout › robb/Cartography <= Couples ͱ

    Pairs ೔ຊ൛Ͱ࢖༻த › stevestreza/Relayout › freshOS/Stevia › roberthein/TinyConstraints › nakiostudio/EasyPeasy <= ࠓͷͱ͜Ζ͓ؾʹೖΓͰւ֎൛PairsͰར༻த › Raizlabs/Anchorage › and more
  6. AutoLayoutͷύϑΥʔϚϯε

  7. AutoLayout͸੍໿ͷ਺͕૿͑Δͱ஗͘ͳ͍ͬͯ͘ Ҿ༻ : https://github.com/layoutBox/FlexLayout#performance AutoLayout ManualLayout

  8. AutoLayout͕஗͘ͳΔ࣌ › ੍໿ͷ਺͕ଟ͍࣌ (੍໿ಉ࢜ͷܨ͕Γ͕ଟ͍࣌) › ಺༰ʹԠͯ͡େ͖͕͞มԽ͢Δίϯϙʔωϯτ͕ଟؚ͘·ΕΔ࣌ › intrinsicContentSizeΛར༻ͨ͠ϨΠΞ΢τ › UILabelͳͲͰwidth,

    heightͷ੍໿Λ͚ͭͳ͍࣌
  9. ͜ΜͳϨΠΞ΢τ

  10. ஗͍ͱԿ͕ى͖Δʁ › UIεϨου͕ϒϩοΫ͞ΕΔ͕࣌ؒ௕͘ͳ͍ͬͯ͘ › ಈ͖͕஗͘ͳΓɺ׈Β͔ʹΞϓϦ(UI)͕ૢ࡞Ͱ͖ͳ͘ͳΔ › UXͷ௿Լʹͭͳ͕͍ͬͯ͘ › ετϨε͕ཷ·Δ? ›

    ໨͕ർΕΔ?
  11. 1. AutoLayout͕ϘτϧωοΫʹͳΔέʔε › εΫϩʔϧ͢ΔUI › ϦετܥͷUI (UICollectionView/UITableView)ͷCellͷϨΠΞ΢τʹ࢖༻͞ Ε͍ͯΔ࣌ › ྫ

    : iOSͷ௨஌ηϯλʔ (࠷ۙͷσόΠεͩͱΘ͔ΓͮΒ͍͔΋) › εΫϩʔϧ࣌ʹCellͷத਎ͷϨΠΞ΢τ͕஗͍ͱද͕ࣔ஗ΕΔ › ϑϨʔϜ͕མͪΔ › δϟϯϓ͍ͯ͠ΔΑ͏ʹݟ͑Δ › ໨Ͱ௥͏ͷ͕େม
  12. 2. AutoLayout͕ϘτϧωοΫʹͳΔέʔε › ը໘ભҠͷͱ͖ › ભҠઌͷViewControllerͷϨΠΞ΢τ͕ෳࡶͩͱॳظදࣔͷϨΠΞ ΢τʹ͕͔͔࣌ؒΔ › ભҠΛߦ͏ϘλϯΛλοϓ͔ͯ͠ΒҰॠݻ·ΓભҠ͕ߦΘΕΔ Α͏ͳݟͨ໨ʹͳΔ

    › ൓Ԡ͕஗͘ײ͡ΔͷͰɺ΋ͬ͞Γͨ͠ΞϓϦʹݟ͑Δ
  13. Ͳͷ͙Β͍໰୊ʹͳΔͷ͔? › ϨΠΞ΢τ͚ͩͰ͋Ε͹ͦΜͳʹ໰୊ʹ͸ͳΒͳ͍ › ͔͠͠ɺͦͷ΄͔ͷϩδοΫ͕ೖΔ͜ͱʹΑΓϨΠΞ΢τͷॲཧ ͕࣌ؒϘτϧωοΫͱͳΔ

  14. UI͕ߴ଎ʹಈ࡞͢ΔͨΊʹ͸ʁ › ը໘ͷϦϑϨογϡϨʔτ60Hz => ΠϕϯτϧʔϓͰϒϩοΫͯ͠ྑ͍࣌ؒ͸ 16ms (1000ms / 60) ›

    16msΛ௒͍͑ͯ͘ͱϑϨʔϜ͕ͲΜͲΜམ͍ͪͯ͘(υϩοϓϑϨʔϜ) › ͜ͷؒʹUIKitࣗମͷॲཧ΋ೖΔͷͰࢲ͕ͨͪॻ͘ίʔυ͸10ms͙Β͍ʹ͸͍ͨ͠ ΋ͷ › ࠷ۙͷiPad Pro͸120HzʹͳͬͯΔͷͰɺ΋ͬͱ଎͘͠ͳ͍ͱσΟεϓϨΠͷՁ஋ ͸ൃش͞Εͳ͍ɻૢ࡞ʹΑΔεΫϩʔϧͷ଎͞ʹCellͷද͕ࣔ௥͍͔ͭͳ͚Ε͹ͳ Βͳ͍ɻ ͦ΋ͦ΋
  15. UI͕ߴ଎ʹಈ࡞͢ΔͨΊʹ͸ʁ › ͔͠͠ɺͲΜͳʹࣗ෼ͨͪͷίʔυΛ࠷దԽͯ͠΋ͦͷݶք͸͋Δ › AutoLayoutͷίετΛݮΒ͢ͷ͸೉͍͠ ϨΠΞ΢τॲཧ ϨΠΞ΢τॲཧ ͦͷଞϩδοΫ ͦͷଞϩδοΫ 16ms

    ࠷దԽ 0ms
  16. ϨΠΞ΢τҎ֎ͷϩδοΫ͸ͦΜͳʹߴ଎ԽͰ͖ͳ͍ › Α΄Ͳॏ͍ॲཧΛॻ͍͍ͯͳ͍ݶΓɺ͢Ͱʹߴ଎ͳίʔυʹͳͬͯ ͍ΔՄೳੑ͸ߴ͍ › ϨΠΞ΢τॲཧΛͲ͏ʹ͔͠ͳ͍ݶΓυϩοϓϑϨʔϜ͸ආ͚Β Εͳ͍ ࣮ࡍͷͱ͜Ζ

  17. ϨΠΞ΢τॲཧ͕ߴ଎ʹͳΕ͹ɺͦͷଞͷ ϩδοΫʹCPUϦιʔεΛඅ΍͢͜ͱ͕Ͱ ͖ΔΑ͏ʹͳΔ ϨΠΞ΢τॲཧ ͦͷଞϩδοΫ 16ms ͜͏͍ͨ͠ 0ms ͦͷଞϩδοΫ

  18. AutoLayoutΛ΍ΊͯΈΔ

  19. ͱ͸͍͑ɺView.frame͸৮Γͨ ͘ͳ͍

  20. ผͷϨΠΞ΢τΤϯδϯ › facebook/Yoga (ւ֎൛PairsͰ࢖༻த) › TextureGroup/Texture (ւ֎൛PairsͰ࢖༻த) › mirego/PinLayout (ͪΐͬͱ৮ͬͯߟ͑த)

    › linkedin/LayoutKit › mirego/PinLayout › mamaral/Neon
  21. facebook/yoga *

  22. Yoga › FacebookʹΑͬͯ։ൃ͞Ε͍ͯΔ › ඇৗʹߴ଎ͳΫϩεϓϥοτϑΥʔϜͳϨΠΞ΢τΤϯδϯ › AutoLayoutͱൺֱͯ͠8ഒఔ౓଎͍ › https://github.com/layoutBox/FlexLayout#performance

  23. Yoga › CSS Flexbox Layoutͱඇৗʹ͍ۙಈ࡞ͱࢦఆํࣜ › جຊ͸UIStackViewͷverticalͱhorizontalΛ૊Έ߹ΘͤͯϨΠΞ΢ τ͢Δײ֮ › ίΞͱͳΔίʔυ͸C++Ͱ3000ߦ΄Ͳ

    › ؤுΕ͹ಡΊͦ͏Ͱ͢ɻ
  24. Yoga › UIView.sizeThatFitsΛݺͼग़ͯ͠಺༰ʹԠͨ͡αΠζΛܾఆ͢Δ › ReactNativeͰ΋࢖ΘΕ͍ͯΔ › ͜Ε͸ͭ·ΓɺReactNative੡ΞϓϦͷํ͕UIͷಈ࡞͕଎͍Մೳੑ ͕͋Δͱ͍͏͜ͱ

  25. Yoga › APIͱͯ͠͸Objective-C༻ͳͷͰSwift͔Β͸ͪΐͬͱ࢖͍ͮΒ͍ › layoutBox/FlexLayoutͱ͍͏Swift༻ʹYogaΛϥοϓͨ͠΋ͷ͕͋ Δ › muukii/Mondrian͍ͬͯ͏ͷ΋࡞ͬͯΈͯΔ(ࠓࢭ·ͬͯΔ)

  26. TextureGroup/Texture

  27. Texture › ݩͷ໊͸AsyncDisplayKit › PinterestͷiOSΞϓϦͰશ໘తʹ࢖༻͞Ε͍ͯΔɻ › Pinterestͷૉ੖Β͍͠ಈ͖͸Textureͷύϫʔ

  28. Texture › UIKitΛϥοϓ͢ΔܗͰUIύϑΥʔϚϯεΛ࠷େ·ͰҾ্͖͛ΔϥΠ ϒϥϦ › Objective-C++ʹΑΔ࣮૷

  29. Texture › ϥΠϒϥϦར༻ऀ͸UIKitΦϒδΣΫτΛૢ࡞͢ΔͷͰ͸ͳ͘ɺNodeͱ͍͏ΦϒδΣΫ τΛ௨ͯ͠UIΛૢ࡞͢Δɻ › Node͸εϨουηʔϑͰ͋ΓɺόοΫάϥ΢ϯυεϨου͔ΒมߋՄೳ › Node -> UIView΁ͷల։͸Texture͕ߦ͏

    › ඇಉظͰUIΛߋ৽͢Δ (AsyncʹDisplay͢Δ) › MainThread͕UIૢ࡞Ͱ๩͚͠Ε͹ߋ৽ΛݟૹΔ › UI͕ݻ·Βͳ͍ › ήʔϜΤϯδϯͬΆ͍ߟ͑ํ
  30. Nodeͷछྨ › ASDisplayNode : NSObject › ASTextNode : ASDisplayNode -

    UILabelͷ୅ΘΓ › ASImageNode : ASDisplayNode - UIImageViewͷ୅ΘΓ › ASButtonNode : ASDisplayNode - UIButtonͷ୅ΘΓ › ASCellNode : ASDisplayNode - UICollectionViewCell/UITableViewCellͷ୅ ΘΓ › and more… Texture
  31. ϨΠΞ΢τ › ϨΠΞ΢τ͸Node಺ʹίʔυͰهड़ › CSS FlexboxͷۭؾײΛҾ͖ܧ͍Ͱ͓Γɺ͞ΒʹUIϨΠΞ΢τʮ͋ Δ͋ΔʯʹରԠ͠΍͍͢هड़ํ๏͕༻ҙ͞Ε͍ͯΔɻ › ׳Εͯ͘ΔͱUIͷ։ൃͷޮ཰͕Α͘ͳΔ ›

    ܭࢉ଎౓͸YogaฒΈʹ଎͍͔ͱࢥΘΕΔ Texture
  32. ڻ͘΄Ͳͷ࠷దԽ › ՄೳͳݶΓόοΫάϥ΢ϯυεϨουͰॲཧΛߦ͏ › UIͷϨεϙϯε଎౓͕࠷େԽ › UILabel΍UIImageView͸࢖ΘΕͣʹϨΠϠʔʹϨϯμϦϯά › λοϓ൑ఆͷඞཁͷͳ͍΋ͷ͸CALayerͱͯ͠දࣔ (layerBacking)

    (UIViewΑΓϝϞϦফඅ͕཈͑ΒΕΔͨΊ) › όοΫάϥ΢ϯυͰϝϞϦղ์Λߦ͏Ωϡʔ (releaseʹ΋CPUίετ͕͔͔ΔͨΊ) › Nodeʹهड़ͨ͠ϨΠΞ΢τ͸όοΫάϥ΢ϯυͰαΠζܭࢉ͕Մೳ › ConcurrentͰ࣮ߦ͞ΕΔͨΊɺNode͕࣋ͭσʔλ͕ଞεϨουʹґଘ͍ͯ͠ͳ͚Ε͹ɺCPUͷίΞ͕ϑ ϧͰ࢖͑Δ › iPhoneXͳΒ6ݸCellಉ࣌ʹܭࢉ Texture
  33. TextureΛ࢖࣮ͬͨ૷ͷงғؾ https://github.com/muukii/PlayTexture ࡉ͔͘આ໌͢Δͱ࿩͕௕͘ͳͬͯ͠·͏ͷͰงғؾ͚ͩʹ͓͖ͯ͠·͢ɻ

  34. None
  35. None
  36. UIKit class CardView : UIView { } ௨ৗ௨ΓUIKitͰViewΛ࡞ΔͳΒUIViewͷαϒ ΫϥεΛ࡞੒ͯ͠ViewΛ͍͖ͭͬͯ͘·͢

  37. Texture // Textureʹͳ͚ͬͨͲModuleͷ໊લ͸ݹ͍·· import AsyncDisplayKit class CardNode : ASDisplayNode {

    } TextureͰ͸UIViewͷ୅ΘΓʹ ASDisplayNodeͷαϒΫϥεΛ࡞੒͠·͢ɻ
  38. import AsyncDisplayKit class CardNode : ASDisplayNode { private let imageNode:

    ASImageNode = .init() private let titleNode: ASTextNode = .init() private let detailNode: ASTextNode = .init() } ίϯϙʔωϯτΛఆٛ
  39. class CardNode : ASDisplayNode { … init(image: UIImage, title: String,

    detail: String) { super.init() titleNode.attributedText = NSAttributedString( string: title, attributes: […] ) detailNode.attributedText = NSAttributedString( string: detail, attributes: […] ) imageNode.image = image 
 addSubnode(imageNode)
 addSubnode(titleNode) addSubnode(detailNode) // addSubnodeͷ୅ΘΓʹ `automaticallyManagesSubnodes = true` Ͱ΋Α͍ } }
  40. class CardNode : ASDisplayNode { … override func layoutSpecThatFits(_ constrainedSize:

    ASSizeRange) -> ASLayoutSpec { // ⭐ ͜͜ʹϨΠΞ΢τΛهड़͢Δ } }
  41. override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { return ASRatioLayoutSpec(

    ratio: 1.6, child: ASBackgroundLayoutSpec( child: ASInsetLayoutSpec( insets: .init(top: 8, left: 8, bottom: 8, right: 8), child: ASStackLayoutSpec( direction: .vertical, spacing: 8, justifyContent: .end, alignItems: .start, children: [ titleNode, detailNode, ] ) ), background: imageNode ) ) }
  42. override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { return ASRatioLayoutSpec(

    ratio: 1.6, child: ASBackgroundLayoutSpec( child: ASInsetLayoutSpec( insets: .init(top: 8, left: 8, bottom: 8, right: 8), child: ASStackLayoutSpec( direction: .vertical, spacing: 8, justifyContent: .end, alignItems: .start, children: [ titleNode, detailNode, ] ) ), background: imageNode ) ) }
  43. let info = ASStackLayoutSpec( direction: .vertical, spacing: 8, justifyContent: .end,

    alignItems: .start, children: [ titleNode, detailNode, ] ) Vertical
  44. let body = ASBackgroundLayoutSpec( child: info, background: imageNode ) let

    info = ASStackLayoutSpec(…
  45. class CardNode : ASDisplayNode { … override func layoutSpecThatFits(_ constrainedSize:

    ASSizeRange) -> ASLayoutSpec { return ASRatioLayoutSpec( ratio: 1.6, // ॎ௕ͷΧʔυͰදࣔ͢ΔͨΊbodyͷΞεϖΫτൺΛࢦఆ͢Δ child: body ) } }
  46. let node = CardNode( image: UIImage(named: "sample")!, title: "Lorem Ipsum",

    detail: "Lorem Ipsum……" ) view.addSubnode(node) let layout = node.calculateLayoutThatFits( ASSizeRange( min: .init(width: 0.0, height: 0), max: .init(width: 240.0, height: .infinity) ) ) node.frame.size = layout.size In ViewController ࣮ࡍɺ͜͏͍͏ίʔυ͸͋·Γॻ͔ͳ͍Ͱ͢ɻ CardNodeΛॳظԽ ViewController.viewʹaddSubnode nodeͷαΠζܭࢉΛߦ͏ αΠζͷRangeΛ༩͑Δ UIView.sizeThatFitsͷײ֮
  47. None
  48. let insetInfo = ASInsetLayoutSpec( insets: UIEdgeInsets( top: 8, left: 8,

    bottom: 8, right: 8 ), child: info ) let info = ASStackLayoutSpec( direction: .vertical, spacing: 8, justifyContent: .end, alignItems: .start, children: [ titleNode, detailNode, ] )
  49. let body = ASBackgroundLayoutSpec( child: info, background: imageNode ) let

    body = ASBackgroundLayoutSpec( child: insetInfo, background: imageNode )
  50. None
  51. override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { return ASRatioLayoutSpec(

    ratio: 1.6, child: ASBackgroundLayoutSpec( child: ASInsetLayoutSpec( insets: .init(top: 8, left: 8, bottom: 8, right: 8), child: ASStackLayoutSpec( direction: .vertical, spacing: 8, justifyContent: .end, alignItems: .start, children: [ titleNode, detailNode, ] ) ), background: imageNode ) ) } શ෦·ͱΊΔͱ
  52. DispatchQueue.global().async { let layout = node.calculateLayoutThatFits( ASSizeRange( min: .init(width: 0.0,

    height: 0), max: .init(width: 240.0, height: .infinity) ) ) DispatchQueue.main.async { node.frame.size = layout.size } } όοΫάϥ΢ϯυͰαΠζܭࢉ
  53. ·ͱΊ › AutoLayout͸ॊೈͰڧྗͳϨΠΞ΢τΤϯδϯ͕ͩɺ·ͩύϑΥʔϚϯε໘Ͱ՝ ୊͕͋Δɻ › ҰԠɺ࠷ۙͷiOSͰ͸ϨΠΞ΢τॲཧΛগͳͤ͘͞Δ޻෉͸ೖ͖͍ͬͯͯΔ › UICollectionViewͷestimated~ͱ͔ › ݱঢ়ɺCPUͷΫϩοΫ਺ͷ৳ͼʹ͸ظ଴ͮ͠Β͍ͷͰɺUIύϑΥʔϚϯε޲্ͷ

    ͨΊʹ͸ϚϧνεϨουʹΑΔΞϓϩʔν͕ඞਢʹͳͬͯ͘Δ͸ͣɻ › ͔͠͠ɺUIKit͸ݱ࣌఺Ͱ͸ϚϧνεϨουʹ͸ରԠ͍ͯ͠ͳ͍ɻ
  54. ·ͱΊ › ͜ͷݱঢ়ʹରͯ͠ɺFacebook,Instagram,PinterestͷΑ͏ͳαʔϏε͸ϓϩμΫτʹ͓ ͍ͯAutoLayoutͷ࢖༻Λආ͚͓ͯΓɺͦΕͧΕ͕UIύϑΥʔϚϯεΛ࠷େԽ͢ΔOSS Λެ։͍ͯ͠Δ › Pinterest - Texture ›

    Facebook - ComponentKit, ReactNative, Texture › Instagram - IGListKit (ͪΐͬͱझࢫ͸ҧ͏͚Ͳ) › ͜ͷঢ়گʹରͯ͠ɺࠓޙApple͕औΔUIKit΁ͷߟָ͕͑͠ΈͰ͢ɻAutoLayoutؤுͬ ͯཉ͍͠Ͱ͢Ͷʂ
  55. Thank you