Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
iOSのレイアウトサイクルを知ろう
Search
Elvis Shi
May 24, 2018
Programming
3
1.9k
iOSのレイアウトサイクルを知ろう
iOS(UIKit)のレイアウトサイクルについてのお話
Elvis Shi
May 24, 2018
Tweet
Share
More Decks by Elvis Shi
See All by Elvis Shi
@Environment(\.keyPath)那么好我不允许你们不知道! / atEnvironment keyPath is so good and you should know it!
lovee
0
240
ゼロから始めるPreferenceの実装 / Let's implement Preferences from scratch
lovee
0
100
Kotlin エンジニアへ送る:Swift 案件に参加させられる日に備えて~似てるけど色々違う Swift の仕様 / from Kotlin to Swift
lovee
1
330
個人アプリを2年ぶりにアプデしたから褒めて / I just updated my personal app, praise me!
lovee
0
580
How did I build an Open-Source SwiftUI Toast Library
lovee
1
140
SwiftUIで使いやすいToastの作り方 / How to build a Toast system which is easy to use in SwiftUI
lovee
3
1.1k
SwiftUIで二重スクロール作ってみた / When I tried to make a dual-scroll-ish view in SwiftUI
lovee
1
340
Observation のあれこれ / A brief introduction about Observation
lovee
3
410
ChatGPT 時代の勉強 / Learning under ChatGPT era
lovee
27
8.9k
Other Decks in Programming
See All in Programming
AIコードレビューがチームの"文脈"を 読めるようになるまで
marutaku
0
360
Why Kotlin? 電子カルテを Kotlin で開発する理由 / Why Kotlin? at Henry
agatan
2
7.3k
AIコーディングエージェント(Gemini)
kondai24
0
230
Rubyで鍛える仕組み化プロヂュース力
muryoimpl
0
130
【CA.ai #3】ワークフローから見直すAIエージェント — 必要な場面と“選ばない”判断
satoaoaka
0
250
宅宅自以為的浪漫:跟 AI 一起為自己辦的研討會寫一個售票系統
eddie
0
510
Cap'n Webについて
yusukebe
0
130
AtCoder Conference 2025「LLM時代のAHC」
imjk
2
490
手が足りない!兼業データエンジニアに必要だったアーキテクチャと立ち回り
zinkosuke
0
720
堅牢なフロントエンドテスト基盤を構築するために行った取り組み
shogo4131
8
2.4k
AIコーディングエージェント(skywork)
kondai24
0
180
組み合わせ爆発にのまれない - 責務分割 x テスト
halhorn
1
150
Featured
See All Featured
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
The Pragmatic Product Professional
lauravandoore
37
7.1k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
1.6k
Docker and Python
trallard
47
3.7k
GitHub's CSS Performance
jonrohan
1032
470k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.6k
4 Signs Your Business is Dying
shpigford
186
22k
Designing Experiences People Love
moore
143
24k
Into the Great Unknown - MozCon
thekraken
40
2.2k
Transcript
ϨΠΞταΠΫϧΛ Ζ͏ forαϙʔλʔζ$P-BCษڧձ K15؊
var employedBy = "YUMEMI Inc." var job = "iOS Developer"
var favoriteLanguage = "Swift" var twitter = "@lovee" var qiita = "lovee" var github = "el-hoshino" var additionalInfo = """ ࣗশ Auto Layout ઈରࡴ͢ϚϯͰ͢ """ class Me: Developable, Talkable { }
J04ʢ6*,JUʣͷϨΠΞταΠΫϧͲͷΑ͏ʹಈ͍ͯΔ͔ ͲͷλΠϛϯάͰϨΠΞτΛΈͳ͓͖͔͢ "VUP-BZPVUͰϨΠΞτΛΉ࣌ͱίʔυͰϨΠΞτ Ή࣌ʹͦΕͧΕԿΛؾΛ͚ͭΔ͖͔ /PU"VUP-BZPVUͷ͍ܰએʢΛ͍ͤͯͩ͘͞͞Xʣ ࠓ͢͜ͱ
͜ͷϨΠΞτ۩ମతʹͲͷΑ͏ʹΜͰ͍Δͷ͔ έʔεόΠέʔε͗͢ΔͷͰΧόʔ͢Δͷແཧ Ͱฉ͖͍ͨͷ͋Ε࣭ٙԠ࠙ձͷ࣌ʹରԠ͍ͨ͠ "VUP-BZPVUͷΈ ڵຯֶ͋ͬͯಘҙͳϑϨϯζੋඇҴݟ͞ΜͷൃදΛ IUUQTXXXZPVUVCFDPNXBUDI W5)0OTZ%,&:2UT ࠓ͞ͳ͍͜ͱ
ੲͷ iPhone ղ૾͕ 1 छྨ͔͠ͳ͘ɺ ը໘ϨΠΞτ͕ͱͯ؆୯ͩͬͨɻ
UIView ͷੜ࣌ʹ frame Λࢦఆ͠ɺ ϨΠΞτͷมԽߟ͑ͳͯ͘Α͔ͬͨɻ ը໘ճస͕ແޮͳΒ let view = UIView(frame:
CGRect(x: 0, ɹɹy: 0, ɹɹwidth: 320, ɹɹheight: 480)) Swiftɿ UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; Objective-Cɿ
్த͔Βը໘छྨ͕૿͑ɺ ϨΠΞτ͕໘ʹͳΓ࢝Ίͨɻ
ࠓछྨ͕ଟ͗ͯ͢ɺ ԿΛͲ͏͢Ε͍͍͔… ?
ϨΠΞτ αΠΫϧ
ϨΠΞτίετ͕ߴ͍ʢॏ͍ʣ ͳΔ͘ϨΠΞτΛඞཁͳ࣌ʹඞཁ࠷ݶ͚ͩߦ͍͍ͨ ҙਤ͠ͳ͍ॴͰ࠶ϨΠΞτΛىͤͨ͘͜͞ͳ͍ ϨΠΞτΛࢥ͏Ͳ͓Γʹ࣮ݱ͢Δͷʹඞཁ ϨΠΞτઃఆͯ͠Δ͚ͲɺαΠΫϧ͕ճΒͳ͔͍ͬͨͤ Ͱݟ͕ͨఆͱҧ͏͜ͱΛ͍͗ͨ ͳͥϨΠΞταΠΫϧ͕ॏཁ͔
UIViewController UIView viewDidDisappear() viewWillDisappear() layout runloop viewDidAppear() viewDidLayoutSubviews() layoutSubviews() viewWillLayoutSubviews()
viewWillAppear() viewDidLoad() loadView()
UIViewController UIView Needs layout? viewDidLayoutSubviews() layoutSubviews() updateViewConstraints() updateConstraints() yes no
Needs update constraint? viewWillLayoutSubviews() yes sleep no Needs layout? no yes Disappear? viewDidDisappear() viewWillDisappear() layout runloop viewDidAppear() viewDidLayoutSubviews() layoutSubviews() viewWillLayoutSubviews() viewWillAppear() viewDidLoad() loadView()
setNeedsLayout() Λݺͼग़ͨ͠ σόΠε͕ճసͨ͠ ࢠϏϡʔΛՃআΛͨ͠ ࣗͷϏϡʔ֊ͷ੍͕ߋ৽ͨ͠ ʢUIScrollView Ͱ͋ΔࢠϏϡʔʣ͕εΫϩʔϧͨ͠ ࣗͷࢠϏϡʔͷ frame
transform ͳͲ͕มߋ͞Εͨ ͳͲͳͲ… Ͳ͏ͬͯఆ͍ͯ͠Δͷ͔ʁ Needs layout?
setNeedsLayout() Λݺͼग़ͨ͠ σόΠε͕ճసͨ͠ ࢠϏϡʔΛՃআΛͨ͠ ࣗͷϏϡʔ֊ͷ੍͕ߋ৽ͨ͠ ʢUIScrollView Ͱ͋ΔࢠϏϡʔʣ͕εΫϩʔϧͨ͠ ࣗͷࢠϏϡʔͷ frame
transform ͳͲ͕มߋ͞Εͨ ͳͲͳͲ… Ͳ͏ͬͯఆ͍ͯ͠Δͷ͔ʁ Needs layout? ࣗͷ frame transform ඞͣʹมߋͯ͠Β͓͏ʂ ࣗͷ frame transform Λมߋ͢Ε ͷ layoutSubviews() ݺΕΔʂ
setNeedsLayout() Λݺͼग़ͨ͠ σόΠε͕ճసͨ͠ ࢠϏϡʔΛՃআΛͨ͠ ࣗͷϏϡʔ֊ͷ੍͕ߋ৽ͨ͠ ʢUIScrollView Ͱ͋ΔࢠϏϡʔʣ͕εΫϩʔϧͨ͠ ࣗͷࢠϏϡʔͷ frame
transform ͳͲ͕มߋ͞Εͨ ͳͲͳͲ… Ͳ͏ͬͯఆ͍ͯ͠Δͷ͔ʁ Needs layout? ࠶ϨΠΞτ setNeedsLayout ͷ ޙͰͳ͘ɺ࣍ͷϥϯϧʔϓ ϨΠΞτίετ͕ߴ͍ ͘͠ layoutIfNeeded ݺͼग़࣌͠
Ͳ͏ϨΠΞτ ͢Ε͍͍͔
جຊUIViewͷlayoutSubviewsΛΦʔόʔϥΠυ͠ɺͦ ͷதͰϨΠΞτ͢Ε͍͍ ϏϡʔͷframeϓϩύςΟʔɺ͕ཧ͖͢ ༰ʹԠͯ͡αΠζ͕มΘΔϏϡʔɺsizeThatFitsͰ ͳΜͱ͔ͳΔ label.frame.size = label.sizeThatFits(bounds.size) ඞཁʹԠͯ͡ΦʔόʔϥΠυՄ ࠶ϨΠΞτ͕ඞཁʹͳͬͨΒsetNeedsLayoutΛݺͿ
ϨΠΞτ͕ඞཁͱ͍͏ϑϥάཱ͕͚ͭͩͳͷͰɺҰͷ ॲཧʹෳճݺΜͰϨΠΞτޙͰҰճ͚ͩճΔ ίʔυʢ"VUP-BZPVUͳ͠ʣͰΉ߹
ίʔυʢ"VUP-BZPVUͳ͠ʣͰΉ߹ class ViewController: UIViewController { lazy var myView = View(frame:
UIScreen.main.bounds) override func loadView() { self.view = self.myView } override func viewDidLoad() { super.viewDidLoad() self.myView.button.addTarget(self, action: #selector(self.switchButtonSize), for: .touchUpInside) } @objc func switchButtonSize() { self.myView.showsLargeButton = !self.myView.showsLargeButton self.view.setNeedsLayout() } } class View: UIView { let button = UIButton() var showsLargeButton = false override func layoutSubviews() { if showsLargeButton { self.button.frame = self.bounds } else { self.button.frame.size = .init(width: 100, height: 100) self.button.frame.origin = .zero } } }
ίʔυʢ"VUP-BZPVUͳ͠ʣͰΉ߹ class ViewController: UIViewController { lazy var myView = View(frame:
UIScreen.main.bounds) override func loadView() { self.view = self.myView } override func viewDidLoad() { super.viewDidLoad() self.myView.button.addTarget(self, action: #selector(self.switchButtonSize), for: .touchUpInside) } @objc func switchButtonSize() { self.myView.showsLargeButton = !self.myView.showsLargeButton self.view.setNeedsLayout() } } class View: UIView { let button = UIButton() var showsLargeButton = false override func layoutSubviews() { if showsLargeButton { self.button.frame = self.bounds } else { self.button.frame.size = .init(width: 100, height: 100) self.button.frame.origin = .zero } } }
"VUP-BZPVUͰΉ߹ class ViewController: UIViewController { lazy var button: UIButton =
{ let button = UIButton() button.translatesAutoresizingMaskIntoConstraints = false button.addTarget(self, action: #selector(self.switchButtonSize), for: .touchUpInside) return button }() var showsLargeButton = false lazy var top = button.topAnchor.constraint(equalTo: view.topAnchor) lazy var left = button.leftAnchor.constraint(equalTo: view.leftAnchor) lazy var bottom = button.bottomAnchor.constraint(equalTo: view.bottomAnchor) lazy var right = button.rightAnchor.constraint(equalTo: view.rightAnchor) lazy var width = button.widthAnchor.constraint(equalToConstant: 100) lazy var height = button.heightAnchor.constraint(equalToConstant: 100) override func viewDidLoad() { super.viewDidLoad() self.view.addSubview(button) self.view.setNeedsUpdateConstraints() } override func updateViewConstraints() { self.updateButtonConstraints() super.updateViewConstraints() } @objc func switchButtonSize() { self.showsLargeButton = !self.showsLargeButton self.updateButtonConstraints() } func updateButtonConstraints() { top.isActive = true left.isActive = true if showsLargeButton { bottom.isActive = true right.isActive = true width.isActive = false height.isActive = false } else { bottom.isActive = false right.isActive = false width.isActive = true height.isActive = true } } }
"VUP-BZPVUͰΉ߹ @objc func switchButtonSize() { self.showsLargeButton = !self.showsLargeButton self.updateButtonConstraints() }
func updateButtonConstraints() { top.isActive = true left.isActive = true if showsLargeButton { bottom.isActive = true right.isActive = true width.isActive = false height.isActive = false } else { bottom.isActive = false right.isActive = false width.isActive = true height.isActive = true } }
ϨΠΞτʢʹݶΒͣ6*ܥશൠʣϝΠϯεϨουͰʂ ϨΠΞταΠΫϧͰϨΠΞτҾ͖ى͜͞ͳ͍ setNeedsLayoutsetNeedsUpdateConstraintsΛ ݺͳ͍ ϨΠΞταΠΫϧͷݺͼग़͠ΛͳΔ͘࠷খݶʹ ࢠϏϡʔͷՃͳΔ͘viewDidLoad͔ viewWillLayoutSubviewsʹ layoutIfNeededΛΉΈʹݺͳ͍ ຊʹඞཁͳ͚࣌ͩݺͿ ҙʢڞ௨ʣ
ΞχϝʔγϣϯϒϩοΫͱ͔
sizeThatFitsΛ͏·͘׆༻͠ɺϨΠΞτॲཧΛ࠷খݶʹ ͑Δ sizeThatFitsͷΦʔόʔϥΠυ͢Δ࣌ʹɺதͰͳΔ͘ ϨΠΞτॲཧͤ͞ͳ͍ ࣗࣗͰࣗࣗͷframeΛมߋ͠ͳ͍ʂ ҙʢίʔυʣ
ϏϡʔؔΛ͏·͘׆༻੍ͯ͠ΛΉ ؔੑΛਖ਼͘͠ઃఆͰ͖ͯΕsetNeedsLayout(); layoutIfNeeded()ͷίϯϘཁΒͳ͍ͣʂ ੍ͷߋ৽සΛͳΔ͘࠷খݶʹ ՄೳͳݶΓ੍Λੜʗআ͢ΔͷͰͳ͘ɺಉ੍͡ Λྲྀ༻͠constantsΛมߋ͢Δ͔ɺ੍ΛisActiveͰ Γସ͑Δ isActiveͰΓସ͑Δ߹੍ͷอ࣋ʹҙ ίϯςϯπʹԠͨ͡αΠζઃఆintrinsicContentSize ΛΦʔόʔϥΠυ
ҙʢ"VUP-BZPVUʣ
એ
/PU"VUP-BZPVUઈࢍެ։த IUUQTHJUIVCDPNFMIPTIJOP/PU"VUP-BZPVU
ίʔυʢ/PU"VUP-BZPVUʣͰΉ߹ class ViewController: UIViewController { lazy var myView = View(frame:
UIScreen.main.bounds) override func loadView() { self.view = self.myView } override func viewDidLoad() { super.viewDidLoad() self.myView.button.addTarget(self, action: #selector(self.switchButtonSize), for: .touchUpInside) } @objc func switchButtonSize() { self.myView.showsLargeButton = !self.myView.showsLargeButton self.view.setNeedsLayout() } } class View: UIView { let button = UIButton() var showsLargeButton = false override func layoutSubviews() { if showsLargeButton { self.nal.layout(button, by: { $0.stickOnParent() }) } else { self.nal.layout(button, by: { $0 .setTopLeft(by: { $0.topLeft }) .setSize(to: .init(width: 100, height: 100)) }) } } }
J04ΞϓϦઃܭύλʔϯೖࣥචܾఆ IUUQTQFBLTDDJ04@BSDIJUFDUVSF
גࣜձࣾΏΊΈɺΤϯδχΞืूத IUUQTXXXZVNFNJDPKQKBQBHFSFDSVJU