Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 22 additions & 28 deletions Sources/ViewTypes/View.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
#if !os(watchOS)
/// An abstract representation of a generic SwiftUI view type.
///
/// Note: prior to iOS 26, primitive views like `Text`, `Image`, `Button`, and layout
/// stacks were drawn inside a subclass of `UIView` called `_UIGraphicsView` which was
/// introspectable via `.introspect(.view)`, however starting iOS 26 this is no longer the
/// case and all SwiftUI primitives seem to somehow be drawn without an underlying
/// `UIView` vessel.
///
/// ### iOS
///
/// ```swift
/// struct ContentView: View {
/// var body: some View {
/// HStack {
/// Image(systemName: "scribble")
/// Text("Some text")
/// }
/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26)) {
/// print(type(of: $0)) // some subclass of UIView
/// }
/// ExampleUIViewRepresentable()
/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26)) {
/// print(type(of: $0)) // some subclass of UIView
/// }
/// }
/// }
/// ```
Expand All @@ -22,13 +25,10 @@
/// ```swift
/// struct ContentView: View {
/// var body: some View {
/// HStack {
/// Image(systemName: "scribble")
/// Text("Some text")
/// }
/// .introspect(.view, on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26)) {
/// print(type(of: $0)) // some subclass of UIView
/// }
/// ExampleUIViewRepresentable()
/// .introspect(.view, on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26)) {
/// print(type(of: $0)) // some subclass of UIView
/// }
/// }
/// }
/// ```
Expand All @@ -38,13 +38,10 @@
/// ```swift
/// struct ContentView: View {
/// var body: some View {
/// HStack {
/// Image(systemName: "scribble")
/// Text("Some text")
/// }
/// .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15, .v26)) {
/// print(type(of: $0)) // some subclass of NSView
/// }
/// ExampleUIViewRepresentable()
/// .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15, .v26)) {
/// print(type(of: $0)) // some subclass of NSView
/// }
/// }
/// }
/// ```
Expand All @@ -54,13 +51,10 @@
/// ```swift
/// struct ContentView: View {
/// var body: some View {
/// HStack {
/// Image(systemName: "scribble")
/// Text("Some text")
/// }
/// .introspect(.view, on: .visionOS(.v1, .v2, .v26)) {
/// print(type(of: $0)) // some subclass of UIView
/// }
/// ExampleUIViewRepresentable()
/// .introspect(.view, on: .visionOS(.v1, .v2, .v26)) {
/// print(type(of: $0)) // some subclass of UIView
/// }
/// }
/// }
/// ```
Expand Down
44 changes: 34 additions & 10 deletions Tests/Tests/ViewTypes/ViewTests.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SwiftUI
import SwiftUIIntrospect
@testable import SwiftUIIntrospect
import XCTest

@MainActor
Expand All @@ -8,33 +8,57 @@ final class ViewTests: XCTestCase {
XCTAssertViewIntrospection(of: PlatformView.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]

VStack(spacing: 10) {
Image(systemName: "scribble").resizable().frame(height: 30)
SUTView().frame(height: 30)
#if os(iOS) || os(tvOS) || os(visionOS)
.introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26), .visionOS(.v1, .v2, .v26), customize: spy0)
#elseif os(macOS)
.introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15, .v26), customize: spy0)
#endif

Text("Text").frame(height: 40)
SUTView().frame(height: 40)
#if os(iOS) || os(tvOS) || os(visionOS)
.introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26), .visionOS(.v1, .v2, .v26), customize: spy1)
#elseif os(macOS)
.introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15, .v26), customize: spy1)
#endif
}
.padding(10)
#if os(iOS) || os(tvOS) || os(visionOS)
.introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18, .v26), .visionOS(.v1, .v2, .v26), customize: spy2)
#elseif os(macOS)
.introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15, .v26), customize: spy2)
#endif
} extraAssertions: {
XCTAssertEqual($0[safe: 0]?.frame.height, 30)
XCTAssertEqual($0[safe: 1]?.frame.height, 40)
XCTAssertEqual($0[safe: 2]?.frame.height, 100)
}
}
}

struct SUTView: PlatformViewControllerRepresentable {
#if canImport(UIKit)
typealias UIViewControllerType = PlatformViewController
#elseif canImport(AppKit)
typealias NSViewControllerType = PlatformViewController
#endif

func makePlatformViewController(context: Context) -> PlatformViewController {
let controller = PlatformViewController(nibName: nil, bundle: nil)
controller.view.translatesAutoresizingMaskIntoConstraints = false

let widthConstraint = controller.view.widthAnchor.constraint(greaterThanOrEqualToConstant: .greatestFiniteMagnitude)
widthConstraint.priority = .defaultLow

let heightConstraint = controller.view.heightAnchor.constraint(greaterThanOrEqualToConstant: .greatestFiniteMagnitude)
heightConstraint.priority = .defaultLow

NSLayoutConstraint.activate([widthConstraint, heightConstraint])

return controller
}

func updatePlatformViewController(_ controller: PlatformViewController, context: Context) {
// NO-OP
}

static func dismantlePlatformViewController(_ controller: PlatformViewController, coordinator: Coordinator) {
// NO-OP
}
}