Skip to content

Commit 108fe80

Browse files
committed
First stab at indentation
I.e., visualize the structure of the view tree in the layout log table
1 parent d6058d6 commit 108fe80

File tree

2 files changed

+41
-16
lines changed

2 files changed

+41
-16
lines changed

LayoutInspector/DebugLayout.swift

+5-4
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ extension View {
1515
}
1616

1717
/// Monitor the layout proposals and responses for this view and add them to the log.
18-
func debugLayout(_ label: String) -> some View {
19-
DebugLayout(label: label) {
18+
func debugLayout(_ label: String, indent: Int = 0) -> some View {
19+
DebugLayout(label: label, indent: indent) {
2020
self
2121
}
2222
.modifier(DebugLayoutSelectionHighlight(viewID: label))
@@ -26,12 +26,13 @@ extension View {
2626
/// A custom layout that adds the layout proposals and responses for a view to a log for display.
2727
struct DebugLayout: Layout {
2828
var label: String
29+
var indent: Int
2930

3031
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
3132
assert(subviews.count == 1)
32-
logLayoutStep(label, step: .proposal(proposal))
33+
logLayoutStep(label, step: .proposal(proposal), indent: indent)
3334
let response = subviews[0].sizeThatFits(proposal)
34-
logLayoutStep(label, step: .response(response))
35+
logLayoutStep(label, step: .response(response), indent: indent)
3536
return response
3637
}
3738

LayoutInspector/DebugLayoutLog.swift

+36-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import SwiftUI
22

3-
func logLayoutStep(_ label: String, step: LogItem.Step) {
3+
func logLayoutStep(_ label: String, step: LogItem.Step, indent: Int) {
44
DispatchQueue.main.async {
5-
// Coalesce layout steps if the response follow immediately after the proposal
5+
// Coalesce layout steps if the response follows immediately after the proposal
66
// for the same view.
77
//
88
// In this case, proposal and response can be shown in a single row in the log.
@@ -15,7 +15,7 @@ func logLayoutStep(_ label: String, step: LogItem.Step) {
1515
lastLogItem.step = .proposalAndResponse(proposal: proposal, response: response)
1616
LogStore.shared.log.append(lastLogItem)
1717
} else {
18-
LogStore.shared.log.append(.init(label: label, step: step))
18+
LogStore.shared.log.append(.init(label: label, step: step, indent: indent))
1919
}
2020
}
2121
}
@@ -53,6 +53,7 @@ struct LogItem: Identifiable {
5353
var id: UUID = .init()
5454
var label: String
5555
var step: Step
56+
var indent: Int
5657

5758
var proposal: ProposedViewSize? {
5859
switch step {
@@ -86,6 +87,9 @@ struct DebugLayoutLogView: View {
8687
@Binding var selection: String?
8788
@ObservedObject var logStore: LogStore
8889

90+
private static let tableRowHorizontalPadding: CGFloat = 8
91+
private static let tableRowVerticalPadding: CGFloat = 4
92+
8993
init(selection: Binding<String?>? = nil, logStore: LogStore = LogStore.shared) {
9094
if let binding = selection {
9195
self._selection = binding
@@ -99,27 +103,33 @@ struct DebugLayoutLogView: View {
99103
var body: some View {
100104
ScrollView(.vertical) {
101105
Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 0, verticalSpacing: 0) {
106+
// Table header row
102107
GridRow {
103108
Text("View")
104109
Text("Proposal")
105110
Text("Response")
106111
}
107112
.font(.headline)
108-
.padding(.vertical, 4)
109-
.padding(.horizontal, 8)
113+
.padding(.vertical, Self.tableRowVerticalPadding)
114+
.padding(.horizontal, Self.tableRowHorizontalPadding)
110115

116+
// Table header separator line
111117
Rectangle().fill(.secondary)
112118
.frame(height: 1)
113119
.gridCellUnsizedAxes(.horizontal)
114-
.padding(.vertical, 4)
115-
.padding(.horizontal, 8)
120+
.padding(.vertical, Self.tableRowVerticalPadding)
121+
.padding(.horizontal, Self.tableRowHorizontalPadding)
116122

123+
// Table rows
117124
ForEach(logStore.log) { item in
118125
let isSelected = selection == item.label
119126
GridRow {
120-
Text(item.label)
121-
.font(.body)
122-
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
127+
HStack(spacing: 0) {
128+
indentation(level: item.indent)
129+
Text(item.label)
130+
.font(.body)
131+
}
132+
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
123133

124134
Text(item.proposal?.pretty ?? "")
125135
.monospacedDigit()
@@ -132,8 +142,8 @@ struct DebugLayoutLogView: View {
132142
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
133143
}
134144
.font(.callout)
135-
.padding(.vertical, 4)
136-
.padding(.horizontal, 8)
145+
.padding(.vertical, Self.tableRowVerticalPadding)
146+
.padding(.horizontal, Self.tableRowHorizontalPadding)
137147
.foregroundColor(isSelected ? .white : nil)
138148
.background(isSelected ? Color.accentColor : .clear)
139149
.contentShape(Rectangle())
@@ -146,4 +156,18 @@ struct DebugLayoutLogView: View {
146156
}
147157
.background(Color(uiColor: .secondarySystemBackground))
148158
}
159+
160+
private func indentation(level: Int) -> some View {
161+
ForEach(0 ..< level, id: \.self) { _ in
162+
Color.clear
163+
.frame(width: 16)
164+
.overlay(alignment: .leading) {
165+
Rectangle()
166+
.frame(width: 1)
167+
.padding(.leading, 4)
168+
// Compensate for cell padding, we want continuous vertical lines.
169+
.padding(.vertical, -Self.tableRowVerticalPadding)
170+
}
171+
}
172+
}
149173
}

0 commit comments

Comments
 (0)