Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
iOSのレイアウトサイクルを知ろう
Search
Elvis Shi
May 24, 2018
Programming
3
1.8k
iOSのレイアウトサイクルを知ろう
iOS(UIKit)のレイアウトサイクルについてのお話
Elvis Shi
May 24, 2018
Tweet
Share
More Decks by Elvis Shi
See All by Elvis Shi
個人アプリを2年ぶりにアプデしたから褒めて / I just updated my personal app, praise me!
lovee
0
400
How did I build an Open-Source SwiftUI Toast Library
lovee
1
84
SwiftUIで使いやすいToastの作り方 / How to build a Toast system which is easy to use in SwiftUI
lovee
3
900
SwiftUIで二重スクロール作ってみた / When I tried to make a dual-scroll-ish view in SwiftUI
lovee
1
290
Observation のあれこれ / A brief introduction about Observation
lovee
3
370
ChatGPT 時代の勉強 / Learning under ChatGPT era
lovee
27
8.7k
属人化しない為の勉強会作り / To make tech meetups with less personal dependencies
lovee
0
310
偏見と妄想で語るスクリプト言語としての Swift / Swift as a Scripting Language
lovee
2
840
danger-swift-kantoku
lovee
1
540
Other Decks in Programming
See All in Programming
The Weight of Data: Rethinking Cloud-Native Systems for the Age of AI
hollycummins
0
270
Signal-Based Data FetchingWith the New httpResource
manfredsteyer
PRO
0
160
複数ドメインに散らばってしまった画像…! 運用中のPHPアプリに後からCDNを導入する…!
suguruooki
0
470
AWS で実現する安全な AI エージェントの作り方 〜 Bedrock Engineer の実装例を添えて 〜 / how-to-build-secure-ai-agents
gawa
8
700
いまさら聞けない生成AI入門: 「生成AIを高速キャッチアップ」
soh9834
15
4.5k
Enterprise Web App. Development (1): Build Tool Training Ver. 5
knakagawa
1
110
Kubernetesで実現できるPlatform Engineering の現在地
nwiizo
3
1.9k
[NG India] Event-Based State Management with NgRx SignalStore
markostanimirovic
0
110
ベクトル検索システムの気持ち
monochromegane
31
9.8k
Day0 初心者向けワークショップ実践!ソフトウェアテストの第一歩
satohiroyuki
0
830
Chrome Extension Techniques from Hell
moznion
1
160
gen_statem - OTP's Unsung Hero
whatyouhide
1
190
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
91
6k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
34
2.2k
The Invisible Side of Design
smashingmag
299
50k
Facilitating Awesome Meetings
lara
54
6.3k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
331
21k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.3k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
How to Think Like a Performance Engineer
csswizardry
23
1.5k
Making Projects Easy
brettharned
116
6.1k
Rails Girls Zürich Keynote
gr2m
94
13k
Bash Introduction
62gerente
611
210k
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