Skip to content

Commit d8c1e53

Browse files
authored
Swap READMEs (#270)
1 parent 99f747f commit d8c1e53

File tree

3 files changed

+346
-347
lines changed

3 files changed

+346
-347
lines changed

README.md

Lines changed: 153 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,140 @@
1-
> **Note**
2-
>
3-
> [`SwiftUIIntrospect`]([email protected]#L19) is an all-new module based off the original [`Introspect`](Package.swift#L13) module that improves on stability, predictability, and ergonomics.
4-
>
5-
> Both modules currently live together under this repo, but the plan is to ultimately obsolete `Introspect` in favor of `SwiftUIIntrospect` as part of a 1.0 release.
6-
>
7-
> Read the [`SwiftUIIntrospect` documentation](docs/SwiftUIIntrospect.md) to learn more.
1+
SwiftUI Introspect
2+
=================
83

9-
Introspect for SwiftUI
10-
======================
4+
[![CI Status Badge](https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml/badge.svg)](https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml)
5+
[![Platform Compatibility Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsiteline%2FSwiftUI-Introspect%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/siteline/SwiftUI-Introspect)
116

12-
[![GithubCI_Status]][GithubCI_URL] [![Siteline_Badge]](https://siteline.com) [![Quintschaf_Badge]](https://quintschaf.com)
7+
> **Note**
8+
>
9+
> [`SwiftUIIntrospect`](../[email protected]#L19) is an all-new module based off the original [`Introspect`](../Package.swift#L13) module that improves on stability, predictability, and ergonomics.
10+
>
11+
> Both modules currently live together under this repo, but the plan is to ultimately obsolete `Introspect` in favor of `SwiftUIIntrospect` as part of a 1.0 release.
12+
>
13+
> While `Introspect` supports Swift 5.5 or higher, `SwiftUIIntrospect` requires Swift 5.7 or higher due to the use of more recent language features which partially enable the aforementioned improvements over the original.
1314
14-
> Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view.
15+
SwiftUIIntrospect allows you to get the underlying UIKit or AppKit element of a SwiftUI view.
1516

16-
For instance, with Introspect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar.
17+
For instance, with SwiftUIIntrospect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar.
1718

1819
How it works
1920
------------
2021

21-
Introspect works by adding a custom `IntrospectionView` to the view hierarchy, then looking into the UIKit hierarchy to find the relevant view.
22+
SwiftUIIntrospect works by adding an invisible `IntrospectionView` on top of the selected view, and an invisible "anchor" view underneath it, then looking through the UIKit/AppKit view hierarchy between the two to find the relevant view.
23+
24+
For instance, when introspecting a `ScrollView`...
25+
26+
```swift
27+
ScrollView {
28+
Text("Item 1")
29+
}
30+
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
31+
// do something with UIScrollView
32+
}
33+
```
34+
35+
... it will:
2236

23-
![](./docs/diagram.png)
37+
- Add `IntrospectionView` as an overlay of `TextField`
38+
- Add `IntrospectionAnchorView` as the background of `TextField`.
39+
- Traverse through all the subviews between both views until a `UIScrollView` instance (if any) is found.
2440

25-
For instance, when introspecting a `TextField`, it will:
41+
> **Warning**
42+
>
43+
> Although this introspection method itself is very solid and unlikely to break in itself, future OS releases require explicit opt-in for introspection (`.iOS(.vXYZ)`), given differences between major OS versions which might not use the same UIKit/AppKit elements that are being looked for in previous OS versions.
2644
27-
- Add `IntrospectionView` as an overlay of `TextField`
28-
- Get the view host of the introspection view (which is alongside the view host of the `UITextField`)
29-
- Get the previous sibling containing `UITextField`
45+
By default, `.introspect` works directly on its _receiver_. This means calling `.introspect` from inside the view you're trying to introspect won't have any effect. This is different to the original `Introspect` module in which some views would implicitly allow introspection from within. This is because most of the time it's more stable and predictable to introspect views directly, but there are times when it's not possible or simply too inflexible for library developers. You **can** introspect an _ancestor_ with `SwiftUIIntrospect`, but you must opt into this explicitly by overriding the introspection `scope`:
3046

31-
**Please note that this introspection method might break in future SwiftUI releases.** Future implementations might not use the same hierarchy, or might not use UIKit elements that are being looked for. Though the library is unlikely to crash, the `.introspect()` method will not be called in those cases.
47+
```swift
48+
ScrollView {
49+
Text("Item 1")
50+
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in
51+
// do something with UIScrollView
52+
}
53+
}
54+
```
3255

3356
### Usage in production
3457

35-
`Introspect` is meant to be used in production. It does not use any private API. It only inspects the view hierarchy using publicly available methods. The library takes a defensive approach to inspecting the view hierarchy: there is no hard assumption that elements are laid out a certain way, there is no force-cast to UIKit classes, and the `introspect()` methods are simply ignored if UIKit views cannot be found.
36-
58+
`SwiftUIIntrospect` is meant to be used in production. It does not use any private API. It only inspects the view hierarchy using publicly available methods. The library takes a defensive approach to inspecting the view hierarchy: there is no hard assumption that elements are laid out a certain way, there is no force-cast to UIKit/AppKit classes, and the `introspect()` methods are simply ignored if UIKit/AppKit views cannot be found.
3759

3860
Install
3961
-------
4062

41-
### SwiftPM
63+
### Swift Package Manager
4264

43-
```
44-
https://github.com/siteline/SwiftUI-Introspect.git
65+
```swift
66+
let package = Package(
67+
dependencies: [
68+
.package(url: "https://github.com/siteline/swiftui-introspect", from: "0.6.1"),
69+
],
70+
targets: [
71+
.target(name: <#Target Name#>, dependencies: [
72+
.product(name: "SwiftUIIntrospect", package: "swiftui-introspect"),
73+
]),
74+
]
75+
)
4576
```
4677

47-
### Cocoapods
78+
### CocoaPods
4879

49-
```
50-
pod 'Introspect'
80+
```ruby
81+
pod 'SwiftUIIntrospect'
5182
```
5283

5384
Introspection
5485
-------------
5586

5687
### Implemented
5788

58-
SwiftUI | UIKit | AppKit | Introspect
59-
--- | --- | --- | ---
60-
NavigationView (StackNavigationViewStyle) | UINavigationController | _N/A_ | `.introspectNavigationController()`
61-
NavigationView (DoubleColumnNavigationViewStyle) | UISplitViewController | _N/A_ | `.introspectSplitViewController()`
62-
NavigationView (DoubleColumnNavigationViewStyle) | _N/A_ | NSSplitView | `.introspectSplitView()`
63-
_Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()`
64-
ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()`
65-
List (iOS15 and below) | UITableView | NSTableView | `.introspectTableView()`
66-
View in List (iOS15 and below) | UITableViewCell | NSTableCellView | `introspectTableViewCell()`
67-
List (iOS 16) | UICollectionView | _N/A_ | `.introspectCollectionView()`
68-
View in List (iOS 16) | UICollectionViewCell | _N/A_ | `.introspectCollectionViewCell()`
69-
TabView | UITabBarController | NSTabView | `.introspectTabBarController()` (iOS) <br/> `.introspectTabView()` (macOS)
70-
TextField | UITextField | NSTextField | `.introspectTextField()`
71-
Toggle | UISwitch | NSButton | `.introspectSwitch()` (iOS) <br/> `.introspectButton()` (macOS)
72-
Slider | UISlider | NSSlider | `.introspectSlider()`
73-
Stepper | UIStepper | NSStepper | `.introspectStepper()`
74-
DatePicker | UIDatePicker | NSDatePicker | `.introspectDatePicker()`
75-
Picker (SegmentedPickerStyle) | UISegmentedControl | NSSegmentedControl | `.introspectSegmentedControl()`
76-
Button | _N/A_ | NSButton | `.introspectButton()`
77-
ColorPicker | UIColorWell | NSColorWell | `.introspectColorWell()`
78-
TextEditor | UITextView | NSTextView | `.introspectTextView()`
79-
80-
81-
**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own selector](#implement-your-own-selector).
89+
- [`Button`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/buttontype)
90+
- [`ColorPicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/colorpickertype)
91+
- [`DatePicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickertype)
92+
- [`DatePicker` with `.compact` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithcompactstyletype)
93+
- [`DatePicker` with `.field` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithfieldstyletype)
94+
- [`DatePicker` with `.graphical` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithgraphicalstyletype)
95+
- [`DatePicker` with `.stepperField` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithstepperfieldstyletype)
96+
- [`DatePicker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithwheelstyletype)
97+
- [`Form`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formtype)
98+
- [`Form` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formwithgroupedstyletype)
99+
- [`.fullScreenCover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/fullScreenCovertype)
100+
- [`List`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listtype)
101+
- [`List` with `.bordered` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithborderedstyletype)
102+
- [`List` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithgroupedstyletype)
103+
- [`List` with `.insetGrouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetgroupedstyletype)
104+
- [`List` with `.inset` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetstyletype)
105+
- [`List` with `.sidebar` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithsidebarstyletype)
106+
- [`ListCell`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listcelltype)
107+
- [`NavigationSplitView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationsplitviewtype)
108+
- [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationstacktype)
109+
- [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType)
110+
- [`NavigationView` with `.stack` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithStackStyleType)
111+
- [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype)
112+
- [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype)
113+
- [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype)
114+
- [`.popover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/popovertype)
115+
- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletype)
116+
- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype)
117+
- [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype)
118+
- [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype)
119+
- [`.sheet`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/sheettype)
120+
- [`SignInWithAppleButton`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/SignInWithAppleButtonType)
121+
- [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype)
122+
- [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/steppertype)
123+
- [`Table`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabletype)
124+
- [`TabView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabviewtype)
125+
- [`TabView` with `.page` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TabViewWithPageStyleType)
126+
- [`TextEditor`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/texteditortype)
127+
- [`TextField`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/textfieldtype)
128+
- [`TextField` with `.vertical` axis](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TextFieldWithVerticalAxisType)
129+
- [`Toggle`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/toggletype)
130+
- [`Toggle` with `button` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithbuttonstyletype)
131+
- [`Toggle` with `checkbox` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithcheckboxstyletype)
132+
- [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype)
133+
- [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype)
134+
- [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype)
135+
- [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/windowtype)
136+
137+
**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own introspectable view type](#implement-your-own-view-type).
82138

83139
### Cannot implement
84140

@@ -95,26 +151,25 @@ Examples
95151

96152
```swift
97153
List {
98-
Text("Item 1")
99-
Text("Item 2")
154+
Text("Item")
100155
}
101-
.introspectTableView { tableView in
102-
tableView.separatorStyle = .none
156+
.introspect(.list, on: .iOS(.v13, .v14, .v15)) { tableView in
157+
tableView.backgroundView = UIView()
158+
tableView.backgroundColor = .cyan
103159
}
104-
.introspectTableViewCell { cell in
105-
let backgroundView = UIView()
106-
backgroundView.backgroundColor = .clear
107-
cell.selectedBackgroundView = backgroundView
160+
.introspect(.list, on: .iOS(.v16, .v17)) { collectionView in
161+
collectionView.backgroundView = UIView()
162+
collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan
108163
}
109164
```
110165

111166
### ScrollView
112167

113168
```swift
114169
ScrollView {
115-
Text("Item 2")
170+
Text("Item")
116171
}
117-
.introspectScrollView { scrollView in
172+
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
118173
scrollView.refreshControl = UIRefreshControl()
119174
}
120175
```
@@ -123,19 +178,20 @@ ScrollView {
123178

124179
```swift
125180
NavigationView {
126-
Text("Item 2")
127-
.introspectNavigationController { navigationController in
128-
navigationController.navigationBar.backgroundColor = .red
129-
}
181+
Text("Item")
182+
}
183+
.navigationViewStyle(.stack)
184+
.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in
185+
navigationController.navigationBar.backgroundColor = .cyan
130186
}
131187
```
132188

133189
### TextField
134190

135191
```swift
136-
TextField("Text Field", text: $textFieldValue)
137-
.introspectTextField { textField in
138-
textField.layer.backgroundColor = UIColor.red.cgColor
192+
TextField("Text Field", text: <#Binding<String>#>)
193+
.introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { textField in
194+
textField.backgroundColor = .red
139195
}
140196
```
141197

@@ -144,33 +200,40 @@ Implement your own selector
144200

145201
**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues).
146202

147-
In case Introspect doesn't support the SwiftUI element that you're looking for, you can implement your own selector. For example, to look for a `UITextField`:
203+
In case SwiftUIIntrospect doesn't support the SwiftUI element that you're looking for, you can implement your own selector. For example, to introspect a `TextField`:
148204

149205
```swift
150-
extension View {
151-
public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View {
152-
return inject(UIKitIntrospectionView(
153-
selector: { introspectionView in
154-
guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
155-
return nil
156-
}
157-
return Introspect.previousSibling(containing: UITextField.self, from: viewHost)
158-
},
159-
customize: customize
160-
))
161-
}
206+
@_spi(Internals) import SwiftUIIntrospect
207+
208+
public struct TextFieldType: IntrospectableViewType {}
209+
210+
extension IntrospectableViewType where Self == TextFieldType {
211+
public static var textField: Self { .init() }
162212
}
163-
```
164213

165-
You can use any of the following [methods](https://github.com/timbersoftware/SwiftUI-Introspect/blob/master/Introspect/Introspect.swift#L3-L71) to inspect the hierarchy:
214+
#if canImport(UIKit)
215+
extension iOSViewVersion<TextFieldType, UITextField> {
216+
public static let v13 = Self(for: .v13)
217+
public static let v14 = Self(for: .v14)
218+
public static let v15 = Self(for: .v15)
219+
public static let v16 = Self(for: .v16)
220+
}
166221

167-
- `Introspect.findChild(ofType:in:)`
168-
- `Introspect.findChildUsingFrame(ofType:in:from:)`
169-
- `Introspect.previousSibling(containing:from:)`
170-
- `Introspect.nextSibling(containing:from:)`
171-
- `Introspect.findAncestor(ofType:from:)`
172-
- `Introspect.findHostingView(from:)`
173-
- `Introspect.findViewHost(from:)`
222+
extension tvOSViewVersion<TextFieldType, UITextField> {
223+
public static let v13 = Self(for: .v13)
224+
public static let v14 = Self(for: .v14)
225+
public static let v15 = Self(for: .v15)
226+
public static let v16 = Self(for: .v16)
227+
}
228+
#elseif canImport(AppKit)
229+
extension macOSViewVersion<TextFieldType, NSTextField> {
230+
public static let v10_15 = Self(for: .v10_15)
231+
public static let v11 = Self(for: .v11)
232+
public static let v12 = Self(for: .v12)
233+
public static let v13 = Self(for: .v13)
234+
}
235+
#endif
236+
```
174237

175238
Releasing
176239
---------
@@ -183,13 +246,3 @@ Releasing
183246
$ git tag X.Y.Z
184247
$ git push origin --tags
185248
```
186-
187-
<!-- References -->
188-
189-
[GithubCI_Status]: https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml/badge.svg?branch=master
190-
191-
[GithubCI_URL]: https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml
192-
193-
[Siteline_Badge]: https://badgen.net/badge/Built%20by/Siteline/blue?icon=https://uploads-ssl.webflow.com/5f4513afbbfc64c4777fcccf/5f525b122370d681879e170e_siteline-icon.svg
194-
195-
[Quintschaf_Badge]: https://badgen.net/badge/Maintained%20by/Quintschaf/cyan?icon=https://quintschaf.com/assets/logo.svg

0 commit comments

Comments
 (0)