Skip to content

Commit 5b5b6cb

Browse files
committed
Gardening
1 parent 09a8ece commit 5b5b6cb

File tree

4 files changed

+155
-132
lines changed

4 files changed

+155
-132
lines changed
+2-100
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,3 @@
1-
// Based on: Swift Talk 319, Inspecting HStack Layout (2022-08-26)
2-
// <https://talk.objc.io/episodes/S01E319-inspecting-hstack-layout>
3-
//
4-
// License:
5-
//
6-
// MIT License
7-
//
8-
// Copyright (c) 2022 objc.io
9-
//
10-
// Permission is hereby granted, free of charge, to any person obtaining a copy
11-
// of this software and associated documentation files (the "Software"), to deal
12-
// in the Software without restriction, including without limitation the rights
13-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14-
// copies of the Software, and to permit persons to whom the Software is
15-
// furnished to do so, subject to the following conditions:
16-
//
17-
// The above copyright notice and this permission notice shall be included in all
18-
// copies or substantial portions of the Software.
19-
//
20-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26-
// SOFTWARE.
27-
//
28-
// ---
29-
//
30-
// Significantly modified by Ole Begemann
31-
321
import SwiftUI
332

343
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
@@ -43,7 +12,8 @@ extension View {
4312
.environment(\.debugLayoutSelectedViewID, selection)
4413
}
4514

46-
/// Monitor the layout proposals and responses for this view and add them to the log.
15+
/// Monitor the layout proposals and responses for this view and add them
16+
/// to the log.
4717
public func debugLayout(
4818
_ label: String,
4919
file: StaticString = #fileID,
@@ -58,71 +28,3 @@ extension View {
5828
.modifier(DebugLayoutSelectionHighlight(viewID: label))
5929
}
6030
}
61-
62-
/// A custom layout that adds the layout proposals and responses for a view to a log for display.
63-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
64-
struct DebugLayout: Layout {
65-
var label: String
66-
67-
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
68-
assert(subviews.count == 1)
69-
logLayoutStep(label, step: .proposal(proposal))
70-
let response = subviews[0].sizeThatFits(proposal)
71-
logLayoutStep(label, step: .response(response))
72-
return response
73-
}
74-
75-
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
76-
subviews[0].place(at: bounds.origin, proposal: proposal)
77-
}
78-
}
79-
80-
/// Draws a highlight (dashed border) around the view that's selected
81-
/// in the DebugLayout log table.
82-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
83-
fileprivate struct DebugLayoutSelectionHighlight: ViewModifier {
84-
var viewID: String
85-
@Environment(\.debugLayoutSelectedViewID) private var selection: String?
86-
87-
func body(content: Content) -> some View {
88-
content
89-
.overlay {
90-
let isSelected = viewID == selection
91-
if isSelected {
92-
Rectangle()
93-
.strokeBorder(style: StrokeStyle(lineWidth: 2, dash: [5]))
94-
.foregroundColor(.pink)
95-
}
96-
}
97-
}
98-
}
99-
100-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
101-
extension CGFloat {
102-
var pretty: String {
103-
String(format: "%.1f", self)
104-
}
105-
}
106-
107-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
108-
extension CGSize {
109-
var pretty: String {
110-
let thinSpace: Character = "\u{2009}"
111-
return "\(width.pretty)\(thinSpace)×\(thinSpace)\(height.pretty)"
112-
}
113-
}
114-
115-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
116-
extension Optional where Wrapped == CGFloat {
117-
var pretty: String {
118-
self?.pretty ?? "nil"
119-
}
120-
}
121-
122-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
123-
extension ProposedViewSize {
124-
var pretty: String {
125-
let thinSpace: Character = "\u{2009}"
126-
return "\(width.pretty)\(thinSpace)×\(thinSpace)\(height.pretty)"
127-
}
128-
}

Sources/LayoutInspector/DebugLayoutLog.swift Sources/LayoutInspector/DebugLayoutImpl.swift

+87-32
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,92 @@
1+
// Based on: Swift Talk 319, Inspecting HStack Layout (2022-08-26)
2+
// <https://talk.objc.io/episodes/S01E319-inspecting-hstack-layout>
3+
//
4+
// License:
5+
//
6+
// MIT License
7+
//
8+
// Copyright (c) 2022 objc.io
9+
//
10+
// Permission is hereby granted, free of charge, to any person obtaining a copy
11+
// of this software and associated documentation files (the "Software"), to deal
12+
// in the Software without restriction, including without limitation the rights
13+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
// copies of the Software, and to permit persons to whom the Software is
15+
// furnished to do so, subject to the following conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be included in all
18+
// copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
// SOFTWARE.
27+
//
28+
// ---
29+
//
30+
// Significantly modified by Ole Begemann
31+
132
import SwiftUI
233

34+
/// A custom layout that saves the layout proposals and responses for a view
35+
/// to a log.
36+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
37+
struct DebugLayout: Layout {
38+
var label: String
39+
40+
func sizeThatFits(
41+
proposal: ProposedViewSize,
42+
subviews: Subviews,
43+
cache: inout ()
44+
) -> CGSize {
45+
assert(subviews.count == 1)
46+
logLayoutStep(label, step: .proposal(proposal))
47+
let response = subviews[0].sizeThatFits(proposal)
48+
logLayoutStep(label, step: .response(response))
49+
return response
50+
}
51+
52+
func placeSubviews(
53+
in bounds: CGRect,
54+
proposal: ProposedViewSize,
55+
subviews: Subviews,
56+
cache: inout ()
57+
) {
58+
subviews[0].place(at: bounds.origin, proposal: proposal)
59+
}
60+
}
61+
62+
/// A custom layout that clears the DebugLayout log at the point where it's
63+
/// placed in the view tree.
64+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
65+
struct ClearDebugLayoutLog: Layout {
66+
func sizeThatFits(
67+
proposal: ProposedViewSize,
68+
subviews: Subviews,
69+
cache: inout ()
70+
) -> CGSize {
71+
assert(subviews.count == 1)
72+
DispatchQueue.main.async {
73+
LogStore.shared.log.removeAll()
74+
LogStore.shared.viewLabels.removeAll()
75+
}
76+
return subviews[0].sizeThatFits(proposal)
77+
}
78+
79+
func placeSubviews(
80+
in bounds: CGRect,
81+
proposal: ProposedViewSize,
82+
subviews: Subviews,
83+
cache: inout ()
84+
) {
85+
assert(subviews.count == 1)
86+
subviews[0].place(at: bounds.origin, proposal: proposal)
87+
}
88+
}
89+
390
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
491
func logLayoutStep(_ label: String, step: LogEntry.Step) {
592
DispatchQueue.main.async {
@@ -39,25 +126,6 @@ func logLayoutStep(_ label: String, step: LogEntry.Step) {
39126
}
40127
}
41128

42-
/// A custom layout that clears the DebugLayout log
43-
/// at the point where it's placed in the view tree.
44-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
45-
struct ClearDebugLayoutLog: Layout {
46-
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
47-
assert(subviews.count == 1)
48-
DispatchQueue.main.async {
49-
LogStore.shared.log.removeAll()
50-
LogStore.shared.viewLabels.removeAll()
51-
}
52-
return subviews[0].sizeThatFits(proposal)
53-
}
54-
55-
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
56-
assert(subviews.count == 1)
57-
subviews[0].place(at: bounds.origin, proposal: proposal)
58-
}
59-
}
60-
61129
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
62130
public final class LogStore: ObservableObject {
63131
public static let shared: LogStore = .init()
@@ -75,16 +143,3 @@ public final class LogStore: ObservableObject {
75143
}
76144
}
77145
}
78-
79-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
80-
struct DebugLayoutSelectedViewID: EnvironmentKey {
81-
static var defaultValue: String? { nil }
82-
}
83-
84-
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
85-
extension EnvironmentValues {
86-
var debugLayoutSelectedViewID: String? {
87-
get { self[DebugLayoutSelectedViewID.self] }
88-
set { self[DebugLayoutSelectedViewID.self] = newValue }
89-
}
90-
}
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import CoreGraphics
2+
import SwiftUI
3+
4+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
5+
extension CGFloat {
6+
var pretty: String {
7+
String(format: "%.1f", self)
8+
}
9+
}
10+
11+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
12+
extension CGSize {
13+
var pretty: String {
14+
let thinSpace: Character = "\u{2009}"
15+
return "\(width.pretty)\(thinSpace)×\(thinSpace)\(height.pretty)"
16+
}
17+
}
18+
19+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
20+
extension Optional where Wrapped == CGFloat {
21+
var pretty: String {
22+
self?.pretty ?? "nil"
23+
}
24+
}
25+
26+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
27+
extension ProposedViewSize {
28+
var pretty: String {
29+
let thinSpace: Character = "\u{2009}"
30+
return "\(width.pretty)\(thinSpace)×\(thinSpace)\(height.pretty)"
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import SwiftUI
2+
3+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
4+
struct DebugLayoutSelectedViewID: EnvironmentKey {
5+
static var defaultValue: String? { nil }
6+
}
7+
8+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
9+
extension EnvironmentValues {
10+
var debugLayoutSelectedViewID: String? {
11+
get { self[DebugLayoutSelectedViewID.self] }
12+
set { self[DebugLayoutSelectedViewID.self] = newValue }
13+
}
14+
}
15+
16+
/// Draws a highlight (dashed border) around the view that's selected
17+
/// in the DebugLayout log table.
18+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
19+
struct DebugLayoutSelectionHighlight: ViewModifier {
20+
var viewID: String
21+
@Environment(\.debugLayoutSelectedViewID) private var selection: String?
22+
23+
func body(content: Content) -> some View {
24+
content
25+
.overlay {
26+
let isSelected = viewID == selection
27+
if isSelected {
28+
Rectangle()
29+
.strokeBorder(style: StrokeStyle(lineWidth: 2, dash: [5]))
30+
.foregroundColor(.pink)
31+
}
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)