Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
AutoLayout以外の選択肢
Muukii
March 28, 2018
Programming
13
4.5k
AutoLayout以外の選択肢
Muukii
March 28, 2018
Tweet
Share
More Decks by Muukii
See All by Muukii
Pairs iOSとトレンドの技術
muukii0803
0
480
Thoughts about build flow
muukii0803
2
150
スマホアプリ開発で大切なこと
muukii0803
3
68
エンジニアとして働くために
muukii0803
0
92
Q. Textureは部分的に導入できますか?
muukii0803
3
1.6k
安定したチャットを実現するための アプリとAPI設計
muukii0803
17
5.9k
快適なUIを持つアプリを作るために できること
muukii0803
12
2.1k
iOS エンジニアが考える Webアプリ開発
muukii0803
3
340
Decodable - Pairs Taiwan
muukii0803
1
700
Other Decks in Programming
See All in Programming
無限スクロールビューライブラリ 二つの設計思想比較
harumak
0
170
IE Graduation Certificate
jxck
6
4.8k
Overview of The Modern Data Stack / モダンデータスタック概論
satoshihirose
6
3.3k
Node-RED 3.0 新機能紹介
utaani
0
140
Beyond Micro Frontends: Frontend Moduliths for the Enterprise @enterjs2022
manfredsteyer
PRO
0
160
チームでカレーを作ろう!アジャイルカレークッキング
akitotsukahara
0
800
Licences open source : entre guerre de clochers et radicalité
pylapp
2
490
Gitlab CIでMRを自動生成する
forcia_dev_pr
0
110
Terraform Plan/Apply結果の自動通知
ymmy02
0
280
Managing Error Messages with your Oracle Database REST APIs
thatjeffsmith
0
120
VisualProgramming_GoogleHome_LINE
nearmugi
1
220
"What's new in Swift"の要約 / swift_5_7_summary
uhooi
1
310
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
16
8.3k
Scaling GitHub
holman
451
140k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
212
20k
Testing 201, or: Great Expectations
jmmastey
21
5.4k
How to train your dragon (web standard)
notwaldorf
58
3.9k
The Language of Interfaces
destraynor
148
20k
The MySQL Ecosystem @ GitHub 2015
samlambert
238
11k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
351
21k
KATA
mclloyd
7
8.7k
We Have a Design System, Now What?
morganepeng
35
3k
Unsuck your backbone
ammeep
659
55k
Making Projects Easy
brettharned
98
4.3k
Transcript
AutoLayoutҎ֎ͷબࢶ @muukii Akiba.swift 2018.03.28
About Me ‣ muukii <Hiroshi Kimura> ‣ iOS Engineer at
eureka, Inc. ‣ Pairs Global Team ‣ GitHub : @muukii ‣ https://muukii.me ☕ ⌚
None
Agenda › AutoLayoutͷύϑΥʔϚϯεͱସҊ › ͲΜͳ࣌ʹAutoLayoutͷύϑΥʔϚϯε͕ʹͳΔͷ͔ › ผͷϨΠΞτΤϯδϯͷհ › TextureGroup/Texture (AsyncDisplayKitʣʹ͍ͭͯհ
› ͍ํͷงғؾ (࣌ؒ͋Ε) › AutoLayoutʹର͢Δײ
ίʔυͰAutoLayoutॻ͘ͳΒ › SnapKit/SnapKit › PureLayout/PureLayout › robb/Cartography <= Couples ͱ
Pairs ຊ൛Ͱ༻த › stevestreza/Relayout › freshOS/Stevia › roberthein/TinyConstraints › nakiostudio/EasyPeasy <= ࠓͷͱ͜Ζ͓ؾʹೖΓͰւ֎൛PairsͰར༻த › Raizlabs/Anchorage › and more
AutoLayoutͷύϑΥʔϚϯε
AutoLayout੍ͷ͕૿͑Δͱ͘ͳ͍ͬͯ͘ Ҿ༻ : https://github.com/layoutBox/FlexLayout#performance AutoLayout ManualLayout
AutoLayout͕͘ͳΔ࣌ › ੍ͷ͕ଟ͍࣌ (੍ಉ࢜ͷܨ͕Γ͕ଟ͍࣌) › ༰ʹԠͯ͡େ͖͕͞มԽ͢Δίϯϙʔωϯτ͕ଟؚ͘·ΕΔ࣌ › intrinsicContentSizeΛར༻ͨ͠ϨΠΞτ › UILabelͳͲͰwidth,
heightͷ੍Λ͚ͭͳ͍࣌
͜ΜͳϨΠΞτ
͍ͱԿ͕ى͖Δʁ › UIεϨου͕ϒϩοΫ͞ΕΔ͕࣌ؒ͘ͳ͍ͬͯ͘ › ಈ͖͕͘ͳΓɺΒ͔ʹΞϓϦ(UI)͕ૢ࡞Ͱ͖ͳ͘ͳΔ › UXͷԼʹͭͳ͕͍ͬͯ͘ › ετϨε͕ཷ·Δ? ›
͕ർΕΔ?
1. AutoLayout͕ϘτϧωοΫʹͳΔέʔε › εΫϩʔϧ͢ΔUI › ϦετܥͷUI (UICollectionView/UITableView)ͷCellͷϨΠΞτʹ༻͞ Ε͍ͯΔ࣌ › ྫ
: iOSͷ௨ηϯλʔ (࠷ۙͷσόΠεͩͱΘ͔ΓͮΒ͍͔) › εΫϩʔϧ࣌ʹCellͷதͷϨΠΞτ͕͍ͱද͕ࣔΕΔ › ϑϨʔϜ͕མͪΔ › δϟϯϓ͍ͯ͠ΔΑ͏ʹݟ͑Δ › Ͱ͏ͷ͕େม
2. AutoLayout͕ϘτϧωοΫʹͳΔέʔε › ը໘ભҠͷͱ͖ › ભҠઌͷViewControllerͷϨΠΞτ͕ෳࡶͩͱॳظදࣔͷϨΠΞ τʹ͕͔͔࣌ؒΔ › ભҠΛߦ͏ϘλϯΛλοϓ͔ͯ͠ΒҰॠݻ·ΓભҠ͕ߦΘΕΔ Α͏ͳݟͨʹͳΔ
› Ԡ͕͘ײ͡ΔͷͰɺͬ͞Γͨ͠ΞϓϦʹݟ͑Δ
Ͳͷ͙Β͍ʹͳΔͷ͔? › ϨΠΞτ͚ͩͰ͋ΕͦΜͳʹʹͳΒͳ͍ › ͔͠͠ɺͦͷ΄͔ͷϩδοΫ͕ೖΔ͜ͱʹΑΓϨΠΞτͷॲཧ ͕࣌ؒϘτϧωοΫͱͳΔ
UI͕ߴʹಈ࡞͢ΔͨΊʹʁ › ը໘ͷϦϑϨογϡϨʔτ60Hz => ΠϕϯτϧʔϓͰϒϩοΫͯ͠ྑ͍࣌ؒ 16ms (1000ms / 60) ›
16msΛ͍͑ͯ͘ͱϑϨʔϜ͕ͲΜͲΜམ͍ͪͯ͘(υϩοϓϑϨʔϜ) › ͜ͷؒʹUIKitࣗମͷॲཧೖΔͷͰࢲ͕ͨͪॻ͘ίʔυ10ms͙Β͍ʹ͍ͨ͠ ͷ › ࠷ۙͷiPad Pro120HzʹͳͬͯΔͷͰɺͬͱ͘͠ͳ͍ͱσΟεϓϨΠͷՁ ൃش͞Εͳ͍ɻૢ࡞ʹΑΔεΫϩʔϧͷ͞ʹCellͷද͕͍͔ࣔͭͳ͚Εͳ Βͳ͍ɻ ͦͦ
UI͕ߴʹಈ࡞͢ΔͨΊʹʁ › ͔͠͠ɺͲΜͳʹࣗͨͪͷίʔυΛ࠷దԽͯͦ͠ͷݶք͋Δ › AutoLayoutͷίετΛݮΒ͢ͷ͍͠ ϨΠΞτॲཧ ϨΠΞτॲཧ ͦͷଞϩδοΫ ͦͷଞϩδοΫ 16ms
࠷దԽ 0ms
ϨΠΞτҎ֎ͷϩδοΫͦΜͳʹߴԽͰ͖ͳ͍ › Α΄Ͳॏ͍ॲཧΛॻ͍͍ͯͳ͍ݶΓɺ͢Ͱʹߴͳίʔυʹͳͬͯ ͍ΔՄೳੑߴ͍ › ϨΠΞτॲཧΛͲ͏ʹ͔͠ͳ͍ݶΓυϩοϓϑϨʔϜආ͚Β Εͳ͍ ࣮ࡍͷͱ͜Ζ
ϨΠΞτॲཧ͕ߴʹͳΕɺͦͷଞͷ ϩδοΫʹCPUϦιʔεΛඅ͢͜ͱ͕Ͱ ͖ΔΑ͏ʹͳΔ ϨΠΞτॲཧ ͦͷଞϩδοΫ 16ms ͜͏͍ͨ͠ 0ms ͦͷଞϩδοΫ
AutoLayoutΛΊͯΈΔ
ͱ͍͑ɺView.frame৮Γͨ ͘ͳ͍
ผͷϨΠΞτΤϯδϯ › facebook/Yoga (ւ֎൛PairsͰ༻த) › TextureGroup/Texture (ւ֎൛PairsͰ༻த) › mirego/PinLayout (ͪΐͬͱ৮ͬͯߟ͑த)
› linkedin/LayoutKit › mirego/PinLayout › mamaral/Neon
facebook/yoga *
Yoga › FacebookʹΑͬͯ։ൃ͞Ε͍ͯΔ › ඇৗʹߴͳΫϩεϓϥοτϑΥʔϜͳϨΠΞτΤϯδϯ › AutoLayoutͱൺֱͯ͠8ഒఔ͍ › https://github.com/layoutBox/FlexLayout#performance
Yoga › CSS Flexbox Layoutͱඇৗʹ͍ۙಈ࡞ͱࢦఆํࣜ › جຊUIStackViewͷverticalͱhorizontalΛΈ߹ΘͤͯϨΠΞ τ͢Δײ֮ › ίΞͱͳΔίʔυC++Ͱ3000ߦ΄Ͳ
› ؤுΕಡΊͦ͏Ͱ͢ɻ
Yoga › UIView.sizeThatFitsΛݺͼग़ͯ͠༰ʹԠͨ͡αΠζΛܾఆ͢Δ › ReactNativeͰΘΕ͍ͯΔ › ͜Εͭ·ΓɺReactNativeΞϓϦͷํ͕UIͷಈ࡞͕͍Մೳੑ ͕͋Δͱ͍͏͜ͱ
Yoga › APIͱͯ͠Objective-C༻ͳͷͰSwift͔Βͪΐͬͱ͍ͮΒ͍ › layoutBox/FlexLayoutͱ͍͏Swift༻ʹYogaΛϥοϓͨ͠ͷ͕͋ Δ › muukii/Mondrian͍ͬͯ͏ͷ࡞ͬͯΈͯΔ(ࠓࢭ·ͬͯΔ)
TextureGroup/Texture
Texture › ݩͷ໊AsyncDisplayKit › PinterestͷiOSΞϓϦͰશ໘తʹ༻͞Ε͍ͯΔɻ › PinterestͷૉΒ͍͠ಈ͖Textureͷύϫʔ
Texture › UIKitΛϥοϓ͢ΔܗͰUIύϑΥʔϚϯεΛ࠷େ·ͰҾ্͖͛ΔϥΠ ϒϥϦ › Objective-C++ʹΑΔ࣮
Texture › ϥΠϒϥϦར༻ऀUIKitΦϒδΣΫτΛૢ࡞͢ΔͷͰͳ͘ɺNodeͱ͍͏ΦϒδΣΫ τΛ௨ͯ͠UIΛૢ࡞͢Δɻ › NodeεϨουηʔϑͰ͋ΓɺόοΫάϥϯυεϨου͔ΒมߋՄೳ › Node -> UIViewͷల։Texture͕ߦ͏
› ඇಉظͰUIΛߋ৽͢Δ (AsyncʹDisplay͢Δ) › MainThread͕UIૢ࡞Ͱ͚͠Εߋ৽ΛݟૹΔ › UI͕ݻ·Βͳ͍ › ήʔϜΤϯδϯͬΆ͍ߟ͑ํ
Nodeͷछྨ › ASDisplayNode : NSObject › ASTextNode : ASDisplayNode -
UILabelͷΘΓ › ASImageNode : ASDisplayNode - UIImageViewͷΘΓ › ASButtonNode : ASDisplayNode - UIButtonͷΘΓ › ASCellNode : ASDisplayNode - UICollectionViewCell/UITableViewCellͷ ΘΓ › and more… Texture
ϨΠΞτ › ϨΠΞτNodeʹίʔυͰهड़ › CSS FlexboxͷۭؾײΛҾ͖ܧ͍Ͱ͓Γɺ͞ΒʹUIϨΠΞτʮ͋ Δ͋ΔʯʹରԠ͍͢͠هड़ํ๏͕༻ҙ͞Ε͍ͯΔɻ › ׳Εͯ͘ΔͱUIͷ։ൃͷޮ͕Α͘ͳΔ ›
ܭࢉYogaฒΈʹ͍͔ͱࢥΘΕΔ Texture
ڻ͘΄Ͳͷ࠷దԽ › ՄೳͳݶΓόοΫάϥϯυεϨουͰॲཧΛߦ͏ › UIͷϨεϙϯε͕࠷େԽ › UILabelUIImageViewΘΕͣʹϨΠϠʔʹϨϯμϦϯά › λοϓఆͷඞཁͷͳ͍ͷCALayerͱͯ͠දࣔ (layerBacking)
(UIViewΑΓϝϞϦফඅ͕͑ΒΕΔͨΊ) › όοΫάϥϯυͰϝϞϦղ์Λߦ͏Ωϡʔ (releaseʹCPUίετ͕͔͔ΔͨΊ) › Nodeʹهड़ͨ͠ϨΠΞτόοΫάϥϯυͰαΠζܭࢉ͕Մೳ › ConcurrentͰ࣮ߦ͞ΕΔͨΊɺNode͕࣋ͭσʔλ͕ଞεϨουʹґଘ͍ͯ͠ͳ͚ΕɺCPUͷίΞ͕ϑ ϧͰ͑Δ › iPhoneXͳΒ6ݸCellಉ࣌ʹܭࢉ Texture
TextureΛ࣮ͬͨͷงғؾ https://github.com/muukii/PlayTexture ࡉ͔͘આ໌͢Δͱ͕͘ͳͬͯ͠·͏ͷͰงғؾ͚ͩʹ͓͖ͯ͠·͢ɻ
None
None
UIKit class CardView : UIView { } ௨ৗ௨ΓUIKitͰViewΛ࡞ΔͳΒUIViewͷαϒ ΫϥεΛ࡞ͯ͠ViewΛ͍͖ͭͬͯ͘·͢
Texture // Textureʹͳ͚ͬͨͲModuleͷ໊લݹ͍·· import AsyncDisplayKit class CardNode : ASDisplayNode {
} TextureͰUIViewͷΘΓʹ ASDisplayNodeͷαϒΫϥεΛ࡞͠·͢ɻ
import AsyncDisplayKit class CardNode : ASDisplayNode { private let imageNode:
ASImageNode = .init() private let titleNode: ASTextNode = .init() private let detailNode: ASTextNode = .init() } ίϯϙʔωϯτΛఆٛ
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` ͰΑ͍ } }
class CardNode : ASDisplayNode { … override func layoutSpecThatFits(_ constrainedSize:
ASSizeRange) -> ASLayoutSpec { // ⭐ ͜͜ʹϨΠΞτΛهड़͢Δ } }
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 ) ) }
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 ) ) }
let info = ASStackLayoutSpec( direction: .vertical, spacing: 8, justifyContent: .end,
alignItems: .start, children: [ titleNode, detailNode, ] ) Vertical
let body = ASBackgroundLayoutSpec( child: info, background: imageNode ) let
info = ASStackLayoutSpec(…
class CardNode : ASDisplayNode { … override func layoutSpecThatFits(_ constrainedSize:
ASSizeRange) -> ASLayoutSpec { return ASRatioLayoutSpec( ratio: 1.6, // ॎͷΧʔυͰදࣔ͢ΔͨΊbodyͷΞεϖΫτൺΛࢦఆ͢Δ child: body ) } }
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ͷײ֮
None
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, ] )
let body = ASBackgroundLayoutSpec( child: info, background: imageNode ) let
body = ASBackgroundLayoutSpec( child: insetInfo, background: imageNode )
None
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 ) ) } શ෦·ͱΊΔͱ
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 } } όοΫάϥϯυͰαΠζܭࢉ
·ͱΊ › AutoLayoutॊೈͰڧྗͳϨΠΞτΤϯδϯ͕ͩɺ·ͩύϑΥʔϚϯε໘Ͱ՝ ͕͋Δɻ › ҰԠɺ࠷ۙͷiOSͰϨΠΞτॲཧΛগͳͤ͘͞Δೖ͖͍ͬͯͯΔ › UICollectionViewͷestimated~ͱ͔ › ݱঢ়ɺCPUͷΫϩοΫͷ৳ͼʹظͮ͠Β͍ͷͰɺUIύϑΥʔϚϯε্ͷ
ͨΊʹϚϧνεϨουʹΑΔΞϓϩʔν͕ඞਢʹͳͬͯ͘Δͣɻ › ͔͠͠ɺUIKitݱ࣌ͰϚϧνεϨουʹରԠ͍ͯ͠ͳ͍ɻ
·ͱΊ › ͜ͷݱঢ়ʹରͯ͠ɺFacebook,Instagram,PinterestͷΑ͏ͳαʔϏεϓϩμΫτʹ͓ ͍ͯAutoLayoutͷ༻Λආ͚͓ͯΓɺͦΕͧΕ͕UIύϑΥʔϚϯεΛ࠷େԽ͢ΔOSS Λެ։͍ͯ͠Δ › Pinterest - Texture ›
Facebook - ComponentKit, ReactNative, Texture › Instagram - IGListKit (ͪΐͬͱझࢫҧ͏͚Ͳ) › ͜ͷঢ়گʹରͯ͠ɺࠓޙApple͕औΔUIKitͷߟָ͕͑͠ΈͰ͢ɻAutoLayoutؤுͬ ͯཉ͍͠Ͱ͢Ͷʂ
Thank you