Skip to content

Commit 0adbf16

Browse files
committed
Add basic UIViewRepresentable support
1 parent 3337a0b commit 0adbf16

File tree

4 files changed

+299
-2
lines changed

4 files changed

+299
-2
lines changed

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ case 2024:
4545
.visionOS(.v1),
4646
]
4747
#endif
48-
case 2021:
48+
case 2021: // iOS 15.5
4949
[
5050
.iOS(.v15),
5151
.macOS(.v12),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// PlatformViewRepresentable.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for RELEASE_2021
6+
// Status: WIP
7+
8+
#if os(macOS)
9+
import AppKit
10+
typealias PlatformView = NSView
11+
#elseif os(iOS)
12+
import UIKit
13+
typealias PlatformView = UIView
14+
#else
15+
import Foundation
16+
typealias PlatformView = Void
17+
#endif
18+
19+
protocol PlatformViewRepresentable: View {
20+
associatedtype PlatformViewProvider
21+
associatedtype Coordinator
22+
23+
static var dynamicProperties: DynamicPropertyCache.Fields { get }
24+
func makeViewProvider(context: PlatformViewRepresentableContext<Self>) -> PlatformViewProvider
25+
func updateViewProvider(_ provider: PlatformViewProvider, context: PlatformViewRepresentableContext<Self>)
26+
func resetViewProvider(_ provider: PlatformViewProvider, coordinator: Coordinator, destroy: () -> Void)
27+
static func dismantleViewProvider(_: PlatformViewProvider, coordinator: Coordinator)
28+
static func platformView(for: PlatformViewProvider) -> PlatformView
29+
func makeCoordinator() -> Coordinator
30+
// func _identifiedViewTree(in: PlatformViewProvider) -> _IdentifiedViewTree
31+
func overrideSizeThatFits(_ size: inout CGSize, in: _ProposedSize, platformView: PlatformViewProvider)
32+
// func overrideLayoutTraits(_ traits: inout _LayoutTraits, for provider: PlatformViewProvider)
33+
34+
static func modifyBridgedViewInputs(_ inputs: inout _ViewInputs)
35+
static var isViewController: Bool { get }
36+
// static var safeAreaMode: _PlatformViewRepresentable_SafeAreaMode { get }
37+
}
38+
39+
// MARK: - PlatformViewRepresentableValues
40+
41+
struct PlatformViewRepresentableValues {
42+
var preferenceBridge: PreferenceBridge
43+
var transaction: Transaction
44+
var environment: EnvironmentValues
45+
46+
static var current: PlatformViewRepresentableValues?
47+
48+
func asCurrent<V>(do: () -> V) -> V {
49+
let old = PlatformViewRepresentableValues.current
50+
PlatformViewRepresentableValues.current = self
51+
defer { PlatformViewRepresentableValues.current = old }
52+
return `do`()
53+
}
54+
}
55+
56+
struct PlatformViewRepresentableContext<RepresentableType: PlatformViewRepresentable> {
57+
var values: PlatformViewRepresentableValues
58+
let coordinator: RepresentableType.Coordinator
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//
2+
// UIViewRepresentable.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for RELEASE_2021
6+
// Status: WIP
7+
8+
#if os(iOS)
9+
10+
import UIKit
11+
12+
/// A wrapper for a UIKit view that you use to integrate that view into your
13+
/// OpenSwiftUI view hierarchy.
14+
///
15+
/// Use a ``UIViewRepresentable`` instance to create and manage a
16+
/// [UIView](https://developer.apple.com/documentation/UIKit/UIView) object in your OpenSwiftUI
17+
/// interface. Adopt this protocol in one of your app's custom instances, and
18+
/// use its methods to create, update, and tear down your view. The creation and
19+
/// update processes parallel the behavior of OpenSwiftUI views, and you use them to
20+
/// configure your view with your app's current state information. Use the
21+
/// teardown process to remove your view cleanly from your OpenSwiftUI. For example,
22+
/// you might use the teardown process to notify other objects that the view is
23+
/// disappearing.
24+
///
25+
/// To add your view into your SwiftUI interface, create your
26+
/// ``UIViewRepresentable`` instance and add it to your SwiftUI interface. The
27+
/// system calls the methods of your representable instance at appropriate times
28+
/// to create and update the view. The following example shows the inclusion of
29+
/// a custom `MyRepresentedCustomView` structure in the view hierarchy.
30+
///
31+
/// struct ContentView: View {
32+
/// var body: some View {
33+
/// VStack {
34+
/// Text("Global Sales")
35+
/// MyRepresentedCustomView()
36+
/// }
37+
/// }
38+
/// }
39+
///
40+
/// The system doesn't automatically communicate changes occurring within your
41+
/// view to other parts of your SwiftUI interface. When you want your view to
42+
/// coordinate with other SwiftUI views, you must provide a
43+
/// ``NSViewControllerRepresentable/Coordinator`` instance to facilitate those
44+
/// interactions. For example, you use a coordinator to forward target-action
45+
/// and delegate messages from your view to any SwiftUI views.
46+
@available(macOS, unavailable)
47+
@available(watchOS, unavailable)
48+
public protocol UIViewRepresentable: View where Self.Body == Never {
49+
50+
/// The type of view to present.
51+
associatedtype UIViewType: UIView
52+
53+
/// A type to coordinate with the view.
54+
associatedtype Coordinator = Void
55+
56+
/// Creates the view object and configures its initial state.
57+
///
58+
/// You must implement this method and use it to create your view object.
59+
/// Configure the view using your app's current data and contents of the
60+
/// `context` parameter. The system calls this method only once, when it
61+
/// creates your view for the first time. For all subsequent updates, the
62+
/// system calls the ``UIViewRepresentable/updateUIView(_:context:)``
63+
/// method.
64+
///
65+
/// - Parameter context: A context structure containing information about
66+
/// the current state of the system.
67+
///
68+
/// - Returns: Your UIKit view configured with the provided information.
69+
@MainActor
70+
func makeUIView(context: Self.Context) -> Self.UIViewType
71+
72+
/// Updates the state of the specified view with new information from
73+
/// OpenSwiftUI.
74+
///
75+
/// When the state of your app changes, OpenSwiftUI updates the portions of your
76+
/// interface affected by those changes. OpenSwiftUI calls this method for any
77+
/// changes affecting the corresponding UIKit view. Use this method to
78+
/// update the configuration of your view to match the new state information
79+
/// provided in the `context` parameter.
80+
///
81+
/// - Parameters:
82+
/// - uiView: Your custom view object.
83+
/// - context: A context structure containing information about the current
84+
/// state of the system.
85+
@MainActor
86+
func updateUIView(_ uiView: Self.UIViewType, context: Self.Context)
87+
88+
@MainActor
89+
func _resetUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator, destory: () -> Void)
90+
91+
/// Cleans up the presented UIKit view (and coordinator) in anticipation of
92+
/// their removal.
93+
///
94+
/// Use this method to perform additional clean-up work related to your
95+
/// custom view. For example, you might use this method to remove observers
96+
/// or update other parts of your OpenSwiftUI interface.
97+
///
98+
/// - Parameters:
99+
/// - uiView: Your custom view object.
100+
/// - coordinator: The custom coordinator instance you use to communicate
101+
/// changes back to OpenSwiftUI. If you do not use a custom coordinator, the
102+
/// system provides a default instance.
103+
@MainActor
104+
static func dismantleUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator)
105+
106+
/// Creates the custom instance that you use to communicate changes from
107+
/// your view to other parts of your OpenSwiftUI interface.
108+
///
109+
/// Implement this method if changes to your view might affect other parts
110+
/// of your app. In your implementation, create a custom Swift instance that
111+
/// can communicate with other parts of your interface. For example, you
112+
/// might provide an instance that binds its variables to OpenSwiftUI
113+
/// properties, causing the two to remain synchronized. If your view doesn't
114+
/// interact with other parts of your app, providing a coordinator is
115+
/// unnecessary.
116+
///
117+
/// SwiftUI calls this method before calling the
118+
/// ``UIViewRepresentable/makeUIView(context:)`` method. The system provides
119+
/// your coordinator either directly or as part of a context structure when
120+
/// calling the other methods of your representable instance.
121+
@MainActor
122+
func makeCoordinator() -> Self.Coordinator
123+
124+
// func _identifiedViewTree(in uiView: UIViewType) -> _IdentifiedViewTree
125+
func _overrideSizeThatFits(_ size: inout CGSize, in: _ProposedSize, uiView: UIViewType)
126+
// func _overrideLayoutTraits(_ traits: inout _LayoutTraits, for uiView: UIViewType)
127+
128+
static func _modifyBridgedViewInputs(_ inputs: inout _ViewInputs)
129+
130+
typealias Context = UIViewRepresentableContext<Self>
131+
}
132+
133+
@available(macOS, unavailable)
134+
extension UIViewRepresentable where Self.Coordinator == () {
135+
136+
/// Creates the custom instance that you use to communicate changes from
137+
/// your view to other parts of your SwiftUI interface.
138+
///
139+
/// Implement this method if changes to your view might affect other parts
140+
/// of your app. In your implementation, create a custom Swift instance that
141+
/// can communicate with other parts of your interface. For example, you
142+
/// might provide an instance that binds its variables to SwiftUI
143+
/// properties, causing the two to remain synchronized. If your view doesn't
144+
/// interact with other parts of your app, providing a coordinator is
145+
/// unnecessary.
146+
///
147+
/// SwiftUI calls this method before calling the
148+
/// ``UIViewRepresentable/makeUIView(context:)`` method. The system provides
149+
/// your coordinator either directly or as part of a context structure when
150+
/// calling the other methods of your representable instance.
151+
public func makeCoordinator() -> Self.Coordinator {
152+
return
153+
}
154+
}
155+
156+
@available(macOS, unavailable)
157+
extension UIViewRepresentable {
158+
/// Cleans up the presented UIKit view (and coordinator) in anticipation of
159+
/// their removal.
160+
///
161+
/// Use this method to perform additional clean-up work related to your
162+
/// custom view. For example, you might use this method to remove observers
163+
/// or update other parts of your SwiftUI interface.
164+
///
165+
/// - Parameters:
166+
/// - uiView: Your custom view object.
167+
/// - coordinator: The custom coordinator instance you use to communicate
168+
/// changes back to SwiftUI. If you do not use a custom coordinator, the
169+
/// system provides a default instance.
170+
public static func dismantleUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator) {}
171+
172+
/// Declares the content and behavior of this view.
173+
public var body: Never { bodyError() }
174+
}
175+
176+
extension UIViewRepresentable {
177+
func _resetUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator, destory: () -> Void) {
178+
destory()
179+
}
180+
181+
// func _identifiedViewTree(in uiView: UIViewType) -> _IdentifiedViewTree
182+
func _overrideSizeThatFits(_ size: inout CGSize, in: _ProposedSize, uiView: UIViewType) {}
183+
// func _overrideLayoutTraits(_ traits: inout _LayoutTraits, for uiView: UIViewType)
184+
185+
static func _modifyBridgedViewInputs(_ inputs: inout _ViewInputs) {}
186+
187+
public static func _makeView(view: _GraphValue<Self>, inputs: _ViewInputs) -> _ViewOutputs {
188+
fatalError("TODO")
189+
}
190+
191+
public static func _makeViewList(view: _GraphValue<Self>, inputs: _ViewListInputs) -> _ViewListOutputs {
192+
fatalError("TODO")
193+
}
194+
}
195+
196+
/// Contextual information about the state of the system that you use to create
197+
/// and update your UIKit view.
198+
///
199+
/// A ``UIViewRepresentableContext`` structure contains details about the
200+
/// current state of the system. When creating and updating your view, the
201+
/// system creates one of these structures and passes it to the appropriate
202+
/// method of your custom ``UIViewRepresentable`` instance. Use the information
203+
/// in this structure to configure your view. For example, use the provided
204+
/// environment values to configure the appearance of your view. Don't create
205+
/// this structure yourself.
206+
@available(macOS, unavailable)
207+
@available(watchOS, unavailable)
208+
@MainActor
209+
public struct UIViewRepresentableContext<Representable> where Representable: UIViewRepresentable {
210+
211+
/// The view's associated coordinator.
212+
public let coordinator: Representable.Coordinator
213+
214+
/// The current transaction.
215+
public private(set) var transaction: Transaction
216+
217+
/// The current environment.
218+
///
219+
/// Use the environment values to configure the state of your view when
220+
/// creating or updating it.
221+
public private(set) var environment: EnvironmentValues
222+
223+
var preferenceBridge: PreferenceBridge
224+
225+
init(coordinator: Representable.Coordinator, transaction: Transaction, environment: EnvironmentValues, preferenceBridge: PreferenceBridge) {
226+
self.coordinator = coordinator
227+
self.transaction = transaction
228+
self.environment = environment
229+
self.preferenceBridge = preferenceBridge
230+
}
231+
}
232+
233+
@available(macOS, unavailable)
234+
@available(watchOS, unavailable)
235+
extension UIViewRepresentableContext : Sendable {
236+
}
237+
238+
#endif

Sources/OpenSwiftUI/Layout/LayoutFundamentals/internal/ProposedSize.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import Foundation
99

10-
struct _ProposedSize: Hashable {
10+
public struct _ProposedSize: Hashable {
1111
var width: CGFloat?
1212
var height: CGFloat?
1313

0 commit comments

Comments
 (0)