forked from ole/swiftui-layout-inspector
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDebugLayout.swift
93 lines (81 loc) · 2.78 KB
/
DebugLayout.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Based on: Swift Talk 319, Inspecting HStack Layout (2022-08-26)
// <https://talk.objc.io/episodes/S01E319-inspecting-hstack-layout>
import SwiftUI
extension View {
/// Start debugging the layout algorithm for this subtree.
///
/// This clears the debug layout log.
public func startDebugLayout(selection: String? = nil) -> some View {
ClearDebugLayoutLog {
self
}
.environment(\.debugLayoutSelectedViewID, selection)
}
/// Monitor the layout proposals and responses for this view and add them to the log.
public func debugLayout(
_ label: String,
file: StaticString = #fileID,
line: UInt = #line
) -> some View {
DebugLayout(label: label) {
self
}
.onAppear {
LogStore.shared.registerViewLabelAndWarnIfNotUnique(label, file: file, line: line)
}
.modifier(DebugLayoutSelectionHighlight(viewID: label))
}
}
/// A custom layout that adds the layout proposals and responses for a view to a log for display.
struct DebugLayout: Layout {
var label: String
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
assert(subviews.count == 1)
logLayoutStep(label, step: .proposal(proposal))
let response = subviews[0].sizeThatFits(proposal)
logLayoutStep(label, step: .response(response))
return response
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
subviews[0].place(at: bounds.origin, proposal: proposal)
}
}
/// Draws a highlight (dashed border) around the view that's selected
/// in the DebugLayout log table.
fileprivate struct DebugLayoutSelectionHighlight: ViewModifier {
var viewID: String
@Environment(\.debugLayoutSelectedViewID) private var selection: String?
func body(content: Content) -> some View {
content
.overlay {
let isSelected = viewID == selection
if isSelected {
Rectangle()
.strokeBorder(style: StrokeStyle(lineWidth: 2, dash: [5]))
.foregroundColor(.pink)
}
}
}
}
extension CGFloat {
var pretty: String {
String(format: "%.1f", self)
}
}
extension CGSize {
var pretty: String {
let thinSpace: Character = "\u{2009}"
return "\(width.pretty)\(thinSpace)×\(thinSpace)\(height.pretty)"
}
}
extension Optional where Wrapped == CGFloat {
var pretty: String {
self?.pretty ?? "nil"
}
}
extension ProposedViewSize {
var pretty: String {
let thinSpace: Character = "\u{2009}"
return "\(width.pretty)\(thinSpace)×\(thinSpace)\(height.pretty)"
}
}