diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift index ff3946a67..b9277ebc1 100644 --- a/Sources/ViewTypes/View.swift +++ b/Sources/ViewTypes/View.swift @@ -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 +/// } /// } /// } /// ``` @@ -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 +/// } /// } /// } /// ``` @@ -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 +/// } /// } /// } /// ``` @@ -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 +/// } /// } /// } /// ``` diff --git a/Tests/Tests/ViewTypes/ViewTests.swift b/Tests/Tests/ViewTypes/ViewTests.swift index 884d444ef..b5a475d13 100644 --- a/Tests/Tests/ViewTypes/ViewTests.swift +++ b/Tests/Tests/ViewTypes/ViewTests.swift @@ -1,5 +1,5 @@ import SwiftUI -import SwiftUIIntrospect +@testable import SwiftUIIntrospect import XCTest @MainActor @@ -8,17 +8,16 @@ 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) @@ -26,15 +25,40 @@ final class ViewTests: XCTestCase { #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 + } +}