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
+
1
32
import SwiftUI
2
33
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
+
3
90
@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
4
91
func logLayoutStep( _ label: String , step: LogEntry . Step ) {
5
92
DispatchQueue . main. async {
@@ -39,25 +126,6 @@ func logLayoutStep(_ label: String, step: LogEntry.Step) {
39
126
}
40
127
}
41
128
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
-
61
129
@available ( macOS 13 . 0 , iOS 16 . 0 , tvOS 16 . 0 , watchOS 9 . 0 , * )
62
130
public final class LogStore : ObservableObject {
63
131
public static let shared : LogStore = . init( )
@@ -75,16 +143,3 @@ public final class LogStore: ObservableObject {
75
143
}
76
144
}
77
145
}
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
- }
0 commit comments