Skip to content

Commit 7de2965

Browse files
authored
Merge pull request #122 from cashapp/entin/viewDidLayoutSubviews
Stop using viewDidLayoutSubviews
2 parents c9bf8ea + 091cd71 commit 7de2965

7 files changed

+356
-181
lines changed

Example/AccessibilitySnapshot/AccessibilityCustomActionsViewController.swift

+42-30
Original file line numberDiff line numberDiff line change
@@ -19,49 +19,61 @@ import UIKit
1919

2020
final class AccessibilityCustomActionsViewController: AccessibilityViewController {
2121

22-
// MARK: - Life Cycle
23-
24-
init() {
25-
self.views = [
26-
.init(includeLabel: true, includeHint: true),
27-
.init(includeLabel: true, includeHint: false),
28-
.init(includeLabel: false, includeHint: true),
29-
.init(includeLabel: false, includeHint: false),
30-
]
22+
// MARK: - UIViewController
3123

32-
super.init(nibName: nil, bundle: nil)
24+
override func loadView() {
25+
view = View(
26+
views: [
27+
.init(includeLabel: true, includeHint: true),
28+
.init(includeLabel: true, includeHint: false),
29+
.init(includeLabel: false, includeHint: true),
30+
.init(includeLabel: false, includeHint: false),
31+
]
32+
)
3333
}
3434

35-
@available(*, unavailable)
36-
required init?(coder: NSCoder) {
37-
fatalError("init(coder:) has not been implemented")
38-
}
35+
}
3936

40-
// MARK: - Private Properties
37+
// MARK: -
4138

42-
private let views: [CustomActionView]
39+
private extension AccessibilityCustomActionsViewController {
4340

44-
// MARK: - UIViewController
41+
final class View: UIView {
4542

46-
override func viewDidLoad() {
47-
super.viewDidLoad()
43+
// MARK: - Life Cycle
4844

49-
views.forEach(view.addSubview)
50-
}
45+
init(views: [CustomActionView], frame: CGRect = .zero) {
46+
self.views = views
5147

52-
override func viewDidLayoutSubviews() {
53-
super.viewDidLayoutSubviews()
48+
super.init(frame: frame)
5449

55-
views.forEach { $0.frame.size = .init(width: view.bounds.width / 2, height: 50) }
50+
views.forEach(addSubview)
51+
}
5652

57-
let statusBarHeight = UIApplication.shared.statusBarFrame.height
53+
@available(*, unavailable)
54+
required init?(coder: NSCoder) {
55+
fatalError("init(coder:) has not been implemented")
56+
}
57+
58+
// MARK: - Private Properties
59+
60+
private let views: [CustomActionView]
5861

59-
var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
60-
for subview in views {
61-
distributionSpecifiers.append(subview)
62-
distributionSpecifiers.append(1.flexible)
62+
// MARK: - UIView
63+
64+
override func layoutSubviews() {
65+
views.forEach { $0.frame.size = .init(width: bounds.width / 2, height: 50) }
66+
67+
let statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
68+
69+
var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
70+
for subview in views {
71+
distributionSpecifiers.append(subview)
72+
distributionSpecifiers.append(1.flexible)
73+
}
74+
applySubviewDistribution(distributionSpecifiers)
6375
}
64-
view.applySubviewDistribution(distributionSpecifiers)
76+
6577
}
6678

6779
}

Example/AccessibilitySnapshot/AccessibilityPathViewController.swift

+82-59
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,56 @@ import UIKit
1919

2020
final class AccessibilityPathViewController: AccessibilityViewController {
2121

22-
// MARK: - Life Cycle
23-
24-
init() {
25-
views = [
26-
AccessibilityPathView(relativePath: UIBezierPath(
27-
roundedRect: CGRect(x: 0, y: 0, width: 60, height: 40),
28-
cornerRadius: 20
29-
)),
30-
AccessibilityPathView(relativePath: UIBezierPath(
31-
arcCenter: CGPoint(x: 30, y: 20),
32-
radius: 20,
33-
startAngle: 0,
34-
endAngle: 1.57,
35-
clockwise: true
36-
)),
37-
AccessibilityPathView(relativePath: UIBezierPath(
38-
ovalIn: CGRect(x: 0, y: 0, width: 60, height: 40)
39-
)),
40-
AccessibilityPathView(relativePath: UIBezierPath(
41-
cgPath: {
42-
let path = CGMutablePath()
43-
path.move(to: .zero)
44-
path.addQuadCurve(
45-
to: .init(x: 60, y: 40),
46-
control: .init(x: 15, y: 30)
47-
)
48-
return path
49-
}()
50-
)),
51-
AccessibilityPathView(relativePath: UIBezierPath(
52-
cgPath: {
53-
let path = CGMutablePath()
54-
path.move(to: CGPoint(x: 0, y: 40))
55-
path.addLine(to: CGPoint(x: 20, y: 15))
56-
path.addLine(to: CGPoint(x: 40, y: 25))
57-
path.addLine(to: CGPoint(x: 60, y: 0))
58-
return path
59-
}()
60-
)),
61-
]
62-
63-
super.init(nibName: nil, bundle: nil)
64-
}
65-
66-
@available(*, unavailable)
67-
required init?(coder: NSCoder) {
68-
fatalError("init(coder:) has not been implemented")
69-
}
70-
7122
// MARK: - Private Properties
7223

73-
private let views: [AccessibilityPathView]
24+
private var views: [AccessibilityPathView] {
25+
return (view as! View).views
26+
}
7427

7528
// MARK: - UIViewController
7629

30+
override func loadView() {
31+
view = View(
32+
views: [
33+
AccessibilityPathView(relativePath: UIBezierPath(
34+
roundedRect: CGRect(x: 0, y: 0, width: 60, height: 40),
35+
cornerRadius: 20
36+
)),
37+
AccessibilityPathView(relativePath: UIBezierPath(
38+
arcCenter: CGPoint(x: 30, y: 20),
39+
radius: 20,
40+
startAngle: 0,
41+
endAngle: 1.57,
42+
clockwise: true
43+
)),
44+
AccessibilityPathView(relativePath: UIBezierPath(
45+
ovalIn: CGRect(x: 0, y: 0, width: 60, height: 40)
46+
)),
47+
AccessibilityPathView(relativePath: UIBezierPath(
48+
cgPath: {
49+
let path = CGMutablePath()
50+
path.move(to: .zero)
51+
path.addQuadCurve(
52+
to: .init(x: 60, y: 40),
53+
control: .init(x: 15, y: 30)
54+
)
55+
return path
56+
}()
57+
)),
58+
AccessibilityPathView(relativePath: UIBezierPath(
59+
cgPath: {
60+
let path = CGMutablePath()
61+
path.move(to: CGPoint(x: 0, y: 40))
62+
path.addLine(to: CGPoint(x: 20, y: 15))
63+
path.addLine(to: CGPoint(x: 40, y: 25))
64+
path.addLine(to: CGPoint(x: 60, y: 0))
65+
return path
66+
}()
67+
)),
68+
]
69+
)
70+
}
71+
7772
override func viewDidLoad() {
7873
super.viewDidLoad()
7974

@@ -82,21 +77,49 @@ final class AccessibilityPathViewController: AccessibilityViewController {
8277
subview.isAccessibilityElement = true
8378
subview.accessibilityLabel = "Label"
8479
subview.frame.size = .init(width: 60, height: 40)
85-
view.addSubview(subview)
8680
}
8781
}
8882

89-
override func viewDidLayoutSubviews() {
90-
super.viewDidLayoutSubviews()
83+
}
9184

92-
let statusBarHeight = UIApplication.shared.statusBarFrame.height
85+
// MARK: -
9386

94-
var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
95-
for subview in views {
96-
distributionSpecifiers.append(subview)
97-
distributionSpecifiers.append(1.flexible)
87+
private extension AccessibilityPathViewController {
88+
89+
final class View: UIView {
90+
91+
// MARK: - Life Cycle
92+
93+
init(views: [AccessibilityPathView], frame: CGRect = .zero) {
94+
self.views = views
95+
96+
super.init(frame: frame)
97+
98+
views.forEach(addSubview(_:))
99+
}
100+
101+
@available(*, unavailable)
102+
required init?(coder: NSCoder) {
103+
fatalError("init(coder:) has not been implemented")
98104
}
99-
view.applySubviewDistribution(distributionSpecifiers)
105+
106+
// MARK: - Public Properties
107+
108+
let views: [AccessibilityPathView]
109+
110+
// MARK: - UIView
111+
112+
override func layoutSubviews() {
113+
let statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
114+
115+
var distributionSpecifiers: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
116+
for subview in views {
117+
distributionSpecifiers.append(subview)
118+
distributionSpecifiers.append(1.flexible)
119+
}
120+
applySubviewDistribution(distributionSpecifiers)
121+
}
122+
100123
}
101124

102125
}

Example/AccessibilitySnapshot/ButtonAccessibilityTraitsViewController.swift

+60-28
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,16 @@ final class ButtonAccessibilityTraitsViewController: AccessibilityViewController
8282

8383
// MARK: - Private Properties
8484

85-
private let buttons = (0..<27).map { _ in UIButton() }
85+
private var buttons: [UIButton] {
86+
return (view as! View).buttons
87+
}
8688

8789
// MARK: - UIViewController
8890

91+
override func loadView() {
92+
view = View()
93+
}
94+
8995
override func viewDidLoad() {
9096
super.viewDidLoad()
9197

@@ -95,7 +101,6 @@ final class ButtonAccessibilityTraitsViewController: AccessibilityViewController
95101
button.setTitle(numberFormatter.string(from: NSNumber(value: (index + 1))), for: .normal)
96102
button.setTitleColor(.black, for: .normal)
97103
button.isAccessibilityElement = true
98-
view.addSubview(button)
99104
}
100105

101106
view.accessibilityElements = buttons
@@ -156,42 +161,69 @@ final class ButtonAccessibilityTraitsViewController: AccessibilityViewController
156161
buttons[26].accessibilityTraits = accessibilityTraits.reduce(.none, { $0.union($1) })
157162
}
158163

159-
override func viewDidLayoutSubviews() {
160-
super.viewDidLayoutSubviews()
164+
}
165+
166+
// MARK: -
161167

162-
buttons.forEach { $0.sizeToFit() }
168+
extension ButtonAccessibilityTraitsViewController {
163169

164-
let statusBarHeight = UIApplication.shared.statusBarFrame.height
170+
final class View: UIView {
165171

166-
let additionalButtonSpacers = (buttons.count % 3 == 0) ? 0 : (3 - (buttons.count % 3))
167-
let buttonsPerColumn = (buttons.count + additionalButtonSpacers) / 3
172+
// MARK: - Life Cycle
168173

169-
let outerMargin = view.bounds.width / 5
174+
override init(frame: CGRect) {
175+
super.init(frame: frame)
170176

171-
var leftColSubviewDistribution: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
172-
for button in buttons[0..<buttonsPerColumn] {
173-
leftColSubviewDistribution.append(button)
174-
leftColSubviewDistribution.append(1.flexible)
177+
buttons.forEach(addSubview(_:))
175178
}
176-
view.applySubviewDistribution(leftColSubviewDistribution, alignment: .leading(inset: outerMargin))
177179

178-
var centerColSubviewDistribution: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
179-
for button in buttons[buttonsPerColumn..<(2 * buttonsPerColumn)] {
180-
centerColSubviewDistribution.append(button)
181-
centerColSubviewDistribution.append(1.flexible)
180+
@available(*, unavailable)
181+
required init?(coder: NSCoder) {
182+
fatalError("init(coder:) has not been implemented")
182183
}
183-
view.applySubviewDistribution(centerColSubviewDistribution)
184184

185-
var rightColSubviewDistribution: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
186-
for button in buttons[(2 * buttonsPerColumn)..<buttons.count] {
187-
rightColSubviewDistribution.append(button)
188-
rightColSubviewDistribution.append(1.flexible)
189-
}
190-
for _ in 0..<additionalButtonSpacers {
191-
rightColSubviewDistribution.append(buttons[0].frame.height.fixed)
192-
rightColSubviewDistribution.append(1.flexible)
185+
// MARK: - Public Properties
186+
187+
let buttons = (0..<27).map { _ in UIButton() }
188+
189+
// MARK: - UIView
190+
191+
override func layoutSubviews() {
192+
buttons.forEach { $0.sizeToFit() }
193+
194+
let statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
195+
196+
let additionalButtonSpacers = (buttons.count % 3 == 0) ? 0 : (3 - (buttons.count % 3))
197+
let buttonsPerColumn = (buttons.count + additionalButtonSpacers) / 3
198+
199+
let outerMargin = bounds.width / 5
200+
201+
var leftColSubviewDistribution: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
202+
for button in buttons[0..<buttonsPerColumn] {
203+
leftColSubviewDistribution.append(button)
204+
leftColSubviewDistribution.append(1.flexible)
205+
}
206+
applySubviewDistribution(leftColSubviewDistribution, alignment: .leading(inset: outerMargin))
207+
208+
var centerColSubviewDistribution: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
209+
for button in buttons[buttonsPerColumn..<(2 * buttonsPerColumn)] {
210+
centerColSubviewDistribution.append(button)
211+
centerColSubviewDistribution.append(1.flexible)
212+
}
213+
applySubviewDistribution(centerColSubviewDistribution)
214+
215+
var rightColSubviewDistribution: [ViewDistributionSpecifying] = [ statusBarHeight.fixed, 1.flexible ]
216+
for button in buttons[(2 * buttonsPerColumn)..<buttons.count] {
217+
rightColSubviewDistribution.append(button)
218+
rightColSubviewDistribution.append(1.flexible)
219+
}
220+
for _ in 0..<additionalButtonSpacers {
221+
rightColSubviewDistribution.append(buttons[0].frame.height.fixed)
222+
rightColSubviewDistribution.append(1.flexible)
223+
}
224+
applySubviewDistribution(rightColSubviewDistribution, alignment: .trailing(inset: outerMargin))
193225
}
194-
view.applySubviewDistribution(rightColSubviewDistribution, alignment: .trailing(inset: outerMargin))
226+
195227
}
196228

197229
}

0 commit comments

Comments
 (0)