@@ -13,14 +13,14 @@ public struct IntrospectionScope: OptionSet {
1313
1414extension View {
1515 @ViewBuilder
16- public func introspect< SwiftUIViewType: IntrospectableViewType , PlatformSpecificView : PlatformView > (
16+ public func introspect< SwiftUIViewType: IntrospectableViewType , PlatformSpecificEntity : PlatformEntity > (
1717 _ viewType: SwiftUIViewType ,
18- on platforms: ( PlatformViewVersions < SwiftUIViewType , PlatformSpecificView > ) ... ,
18+ on platforms: ( PlatformViewVersions < SwiftUIViewType , PlatformSpecificEntity > ) ... ,
1919 scope: IntrospectionScope ? = nil ,
20- customize: @escaping ( PlatformSpecificView ) -> Void
20+ customize: @escaping ( PlatformSpecificEntity ) -> Void
2121 ) -> some View {
2222 if platforms. contains ( where: \. isCurrent) {
23- let id = UUID ( )
23+ let id = IntrospectionAnchorID ( )
2424 self . background (
2525 IntrospectionAnchorView (
2626 id: id
@@ -29,17 +29,17 @@ extension View {
2929 )
3030 . overlay (
3131 IntrospectionView (
32- selector: { ( view : PlatformView ) in
32+ selector: { entity in
3333 let scope = scope ?? viewType. scope
3434 if
3535 scope. contains ( . receiver) ,
36- let target = view . receiver ( ofType: PlatformSpecificView . self, anchorID: id)
36+ let target = entity . receiver ( ofType: PlatformSpecificEntity . self, anchorID: id)
3737 {
3838 return target
3939 }
4040 if
4141 scope. contains ( . ancestor) ,
42- let target = view . ancestor ( ofType: PlatformSpecificView . self)
42+ let target = entity . ancestor ( ofType: PlatformSpecificEntity . self)
4343 {
4444 return target
4545 }
@@ -53,149 +53,138 @@ extension View {
5353 self
5454 }
5555 }
56-
57- @ViewBuilder
58- public func introspect< SwiftUIViewType: IntrospectableViewType , PlatformSpecificViewController: PlatformViewController > (
59- _ viewType: SwiftUIViewType ,
60- on platforms: ( PlatformViewVersions < SwiftUIViewType , PlatformSpecificViewController > ) ... ,
61- scope: IntrospectionScope ? = nil ,
62- customize: @escaping ( PlatformSpecificViewController ) -> Void
63- ) -> some View {
64- if platforms. contains ( where: \. isCurrent) {
65- self . overlay (
66- IntrospectionView (
67- selector: { ( viewController: PlatformViewController ) in
68- let scope = scope ?? viewType. scope
69- if
70- scope. contains ( . receiver) ,
71- let target = viewController. receiver ( ofType: PlatformSpecificViewController . self)
72- {
73- return target
74- }
75- if
76- scope. contains ( . ancestor) ,
77- let target = viewController. ancestor ( ofType: PlatformSpecificViewController . self)
78- {
79- return target
80- }
81- return nil
82- } ,
83- customize: customize
84- )
85- . frame ( width: 0 , height: 0 )
86- )
87- } else {
88- self
89- }
90- }
9156}
9257
93- extension PlatformView {
94- fileprivate func receiver< PlatformSpecificView: PlatformView > (
95- ofType type: PlatformSpecificView . Type ,
96- anchorID: IntrospectionAnchorView . ID
97- ) -> PlatformSpecificView ? {
98- let frontView = self
99- guard
100- let backView = Array ( frontView. superviews) . last? . viewWithTag ( anchorID. hashValue) ,
101- let superview = backView. nearestCommonSuperviewWith ( frontView)
102- else {
103- return nil
104- }
58+ public protocol PlatformEntity : AnyObject {
59+ associatedtype Base : PlatformEntity
10560
106- return superview
107- . subviewsBetween ( backView, and: frontView)
108- . compactMap { $0 as? PlatformSpecificView }
109- . first
110- }
61+ @_spi ( Internals)
62+ var ancestor : Base ? { get }
11163
112- fileprivate func ancestor< PlatformSpecificView: PlatformView > (
113- ofType type: PlatformSpecificView . Type
114- ) -> PlatformSpecificView ? {
115- self . superviews. lazy. compactMap { $0 as? PlatformSpecificView } . first
116- }
64+ @_spi ( Internals)
65+ var descendants : [ Base ] { get }
66+
67+ @_spi ( Internals)
68+ func isDescendant( of other: Base ) -> Bool
69+
70+ @_spi ( Internals)
71+ func entityWithTag( _ tag: Int ) -> Base ?
11772}
11873
119- extension PlatformView {
120- private var superviews : some Sequence < PlatformView > {
121- sequence ( first: self , next: \. superview) . dropFirst ( )
74+ extension PlatformEntity {
75+ @_spi ( Internals)
76+ public var ancestors : some Sequence < Base > {
77+ sequence ( first: self ~ , next: { $0. ancestor~ } ) . dropFirst ( )
12278 }
12379
124- private func nearestCommonSuperviewWith( _ other: PlatformView ) -> PlatformView ? {
125- var nearestAncestor : PlatformView ? = self
80+ @_spi ( Internals)
81+ public var allDescendants : [ Base ] {
82+ self . descendants. reduce ( [ self ~ ] ) { $0 + $1. allDescendants~ }
83+ }
84+
85+ @_spi ( Internals)
86+ public func nearestCommonAncestor( with other: Base ) -> Base ? {
87+ var nearestAncestor : Base ? = self ~
12688
127- while let currentView = nearestAncestor, !other. isDescendant ( of: currentView ) {
128- nearestAncestor = currentView . superview
89+ while let currentEntity = nearestAncestor, !other . isDescendant( of: currentEntity ~ ) {
90+ nearestAncestor = currentEntity . ancestor ~
12991 }
13092
13193 return nearestAncestor
13294 }
13395
134- private func subviewsBetween( _ bottomView: PlatformView , and topView: PlatformView ) -> [ PlatformView ] {
96+ @_spi ( Internals)
97+ public func descendantsBetween( _ bottomEntity: Base , and topEntity: Base ) -> [ Base ] {
13598 var entered = false
136- var result : [ PlatformView ] = [ ]
99+ var result : [ Base ] = [ ]
137100
138- for subview in self . allSubviews {
139- if subview === bottomView {
101+ for descendant in self . allDescendants {
102+ if descendant === bottomEntity {
140103 entered = true
141104 continue
142105 }
143- if subview === topView {
106+ if descendant === topEntity {
144107 return result
145108 }
146109 if entered {
147- result. append ( subview )
110+ result. append ( descendant )
148111 }
149112 }
150113
151114 return result
152115 }
153116
154- private var allSubviews : [ PlatformView ] {
155- self . subviews. reduce ( [ self ] ) { $0 + $1. allSubviews }
156- }
157- }
117+ fileprivate func receiver< PlatformSpecificEntity: PlatformEntity > (
118+ ofType type: PlatformSpecificEntity . Type ,
119+ anchorID: IntrospectionAnchorID
120+ ) -> PlatformSpecificEntity ? {
121+ let frontEntity = self
122+ guard
123+ let backEntity = Array ( frontEntity. ancestors) . last? . entityWithTag ( anchorID. hashValue) ,
124+ let commonAncestor = backEntity. nearestCommonAncestor ( with: frontEntity~ )
125+ else {
126+ return nil
127+ }
158128
159- extension PlatformViewController {
160- fileprivate func receiver< PlatformSpecificViewController: PlatformViewController > (
161- ofType type: PlatformSpecificViewController . Type
162- ) -> PlatformSpecificViewController ? {
163- self . hostingView?
164- . allChildren ( ofType: PlatformSpecificViewController . self)
165- . filter { !( $0 is IntrospectionPlatformViewController ) }
129+ return commonAncestor
130+ . descendantsBetween ( backEntity~ , and: frontEntity~ )
131+ . compactMap { $0 as? PlatformSpecificEntity }
166132 . first
167133 }
168134
169- fileprivate func ancestor< PlatformSpecificViewController : PlatformViewController > (
170- ofType type: PlatformSpecificViewController . Type
171- ) -> PlatformSpecificViewController ? {
172- self . parents
135+ fileprivate func ancestor< PlatformSpecificEntity : PlatformEntity > (
136+ ofType type: PlatformSpecificEntity . Type
137+ ) -> PlatformSpecificEntity ? {
138+ self . ancestors
173139 . lazy
174- . filter { !( $0 is IntrospectionPlatformViewController ) }
175- . compactMap { $0 as? PlatformSpecificViewController }
140+ . compactMap { $0 as? PlatformSpecificEntity }
176141 . first
177142 }
178143}
179144
180- extension PlatformViewController {
181- private var parents : some Sequence < PlatformViewController > {
182- sequence ( first: self , next: \. parent) . dropFirst ( )
145+ extension PlatformView : PlatformEntity {
146+ @_spi ( Internals)
147+ public var ancestor : PlatformView ? {
148+ superview
149+ }
150+
151+ @_spi ( Internals)
152+ public var descendants : [ PlatformView ] {
153+ subviews
154+ }
155+
156+ @_spi ( Internals)
157+ public func entityWithTag( _ tag: Int ) -> PlatformView ? {
158+ viewWithTag ( tag)
159+ }
160+ }
161+
162+ extension PlatformViewController : PlatformEntity {
163+ @_spi ( Internals)
164+ public var ancestor : PlatformViewController ? {
165+ parent
183166 }
184167
185- private var hostingView : PlatformViewController ? {
186- self . parents. first ( where: {
187- let type = String ( reflecting: type ( of: $0) )
188- return type. hasPrefix ( " SwiftUI. " ) && type. contains ( " Hosting " )
189- } )
168+ @_spi ( Internals)
169+ public var descendants : [ PlatformViewController ] {
170+ children
190171 }
191172
192- private func allChildren< PlatformSpecificViewController: PlatformViewController > (
193- ofType type: PlatformSpecificViewController . Type
194- ) -> [ PlatformSpecificViewController ] {
195- var result = self . children. compactMap { $0 as? PlatformSpecificViewController }
196- for subview in self . children {
197- result. append ( contentsOf: subview. allChildren ( ofType: type) )
173+ @_spi ( Internals)
174+ public func isDescendant( of other: PlatformViewController ) -> Bool {
175+ self . ancestors. contains ( other)
176+ }
177+
178+ @_spi ( Internals)
179+ public func entityWithTag( _ tag: Int ) -> PlatformViewController ? {
180+ if self . view. tag == tag {
181+ return self
198182 }
199- return result
183+ for child in children {
184+ if let childWithTag = child. entityWithTag ( tag) {
185+ return childWithTag
186+ }
187+ }
188+ return nil
200189 }
201190}
0 commit comments