Allow .introspectNavigationController() on NavigationView directly

This commit is contained in:
Lois Di Qual 2019-12-02 10:15:26 -08:00
parent 357d497a9c
commit 71e34e11d1
4 changed files with 80 additions and 2 deletions

View File

@ -1,6 +1,10 @@
Changelog
=========
## master
- Allow `.introspectNavigationController()` on NavigationView directly.
## [0.0.4]
- Fix a bug in 0.0.4 that would not allow customization of root elements.

View File

@ -20,6 +20,23 @@ public enum Introspect {
return nil
}
/// Finds a child view controller of the specified type.
/// This method will recursively look for this child.
/// Returns nil if it can't find a view of the specified type.
public static func findChild<AnyViewControllerType: UIViewController>(
ofType type: AnyViewControllerType.Type,
in root: UIViewController
) -> AnyViewControllerType? {
for child in root.children {
if let typed = child as? AnyViewControllerType {
return typed
} else if let typed = findChild(ofType: type, in: child) {
return typed
}
}
return nil
}
/// Finds a previous sibling that contains a view of the specified type.
/// This method inspects siblings recursively.
/// Returns nil if no sibling contains the specified type.
@ -44,6 +61,30 @@ public enum Introspect {
return nil
}
/// Finds a previous sibling that contains a view controller of the specified type.
/// This method inspects siblings recursively.
/// Returns nil if no sibling contains the specified type.
public static func previousSibling<AnyViewControllerType: UIViewController>(
containing type: AnyViewControllerType.Type,
from entry: UIViewController
) -> AnyViewControllerType? {
guard let parent = entry.parent,
let entryIndex = parent.children.firstIndex(of: entry),
entryIndex > 0
else {
return nil
}
for child in parent.children[0..<entryIndex].reversed() {
if let typed = findChild(ofType: type, in: child) {
return typed
}
}
return nil
}
/// Finds a next sibling that contains a view of the specified type.
/// This method inspects siblings recursively.
/// Returns nil if no sibling contains the specified type.
@ -279,7 +320,16 @@ extension View {
/// Finds a `UINavigationController` from any view embedded in a `SwiftUI.NavigationView`.
public func introspectNavigationController(customize: @escaping (UINavigationController) -> ()) -> some View {
return inject(IntrospectionViewController(
selector: { $0.navigationController },
selector: { introspectionViewController in
// Search in ancestors
if let navigationController = introspectionViewController.navigationController {
return navigationController
}
// Search in siblings
return Introspect.previousSibling(containing: UINavigationController.self, from: introspectionViewController)
},
customize: customize
))
}

View File

@ -17,6 +17,20 @@ private struct NavigationTestView: View {
}
}
private struct NavigationRootTestView: View {
let spy: () -> Void
var body: some View {
NavigationView {
VStack {
EmptyView()
}
}
.introspectNavigationController { navigationController in
self.spy()
}
}
}
private struct ListTestView: View {
let spy1: () -> Void
@ -128,6 +142,16 @@ class IntrospectTests: XCTestCase {
wait(for: [expectation], timeout: 1)
}
func testRootNavigation() {
let expectation = XCTestExpectation()
let view = NavigationRootTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testList() {
let expectation1 = XCTestExpectation()

View File

@ -51,7 +51,7 @@ SwiftUI | UIKit | Introspect | Target
--- | --- | --- | ---
List | UITableView | `.introspectTableView()` | List, or List child
ScrollView | UIScrollView | `.introspectScrollView()` | ScrollView, or ScrollView child
NavigationView | UINavigationController | `.introspectNavigationController()` | **NavigationView child**
NavigationView | UINavigationController | `.introspectNavigationController()` | NavigationView, or NavigationView child
TabbedView | UITabBarController | `.introspectTabBarController()` | **TabbedView child**
TextField | UITextField | `.introspectTextField()` | TextField
Toggle | UISwitch | `.introspectSwitch()` | Toggle