import SwiftUI /// Provides a convenient 'wrapper' around a UIView public struct UIWrapping: View { let wrapped: AnyView public var body: some View { wrapped } // MARK: Initializers /// Instantiates a wrapper for a `UIView` /// - Parameters: /// - viewProvider: Use this closure to return an instantiated `UIView` /// - updateHandler: Use this closure to configure the view, this is called for all SwiftUI view updates as well public init(_ provider: @escaping RepresentableProvider, update handler: RepresentableUpdater? = nil) where V: UIView { self.wrapped = AnyView( ViewWrapper( provider: provider, updater: handler ) ) } /// Instantiates a wrapper for a `UIView` – uses `.init(frame:)` /// - Parameters: /// - type: The type of `UIView` to wrap /// - updateHandler: Use this closure to configure the view, this is called for all SwiftUI view updates as well public init(_ type: V.Type = V.self, update handler: @escaping (V) -> Void) where V: UIView { self.wrapped = AnyView( ViewWrapper( provider: { V(frame: .zero) }, updater: handler ) ) } /// Instantiates a wrapper for the specified viewProvider /// - Parameters: /// - viewProvider: Use this closure to return an instantiated `UIView` /// - updateHandler: Use this closure to configure the view, this is called for all SwiftUI view updates as well public init(_ provider: @escaping RepresentableProvider, update handler: RepresentableUpdater? = nil) where V: UIViewController { self.wrapped = AnyView( ViewControllerWrapper( provider: provider, updater: handler ) ) } /// Instantiates a wrapper for a `UIViewController` – uses `.init(nibName:bundle:)` /// - Parameters: /// - type: The type of `UIViewController` to wrap /// - handler: Use this closure to configure the view, this is called for all SwiftUI view updates as well public init(_ type: V.Type = V.self, update handler: @escaping (V) -> Void) where V: UIViewController { self.wrapped = AnyView( ViewControllerWrapper( provider: { V(nibName: nil, bundle: nil) }, updater: handler ) ) } } // MARK: - Private ViewWrapper public typealias RepresentableProvider = () -> V public typealias RepresentableUpdater = (V) -> Void private struct ViewWrapper: UIViewRepresentable { let provider: RepresentableProvider let updater: RepresentableUpdater? func makeUIView(context: Context) -> V { provider() } func updateUIView(_ view: V, context: Context) { view.backgroundColor = .clear updater?(view) } } // MARK: - Private ViewControllerWrapper private struct ViewControllerWrapper: UIViewControllerRepresentable { public typealias Provider = () -> V public typealias Updater = (V) -> Void let provider: Provider let updater: Updater? func makeUIViewController(context: Context) -> V { provider() } func updateUIViewController(_ controller: V, context: Context) { controller.view.backgroundColor = .clear updater?(controller) } } // MARK: - Previews struct UIWrapping_Preview: PreviewProvider { static var previews: some View { VStack { UIWrapping { view in view.numberOfLines = 0 view.text = "Hello 😄" view.textAlignment = .center } .fixedSize() UIWrapping { view in view.startAnimating() } // Note: In case of UICollectionView must be initialized with a non-nil layout parameter. UIWrapping { () -> UICollectionView in let flowLayout = UICollectionViewFlowLayout() flowLayout.scrollDirection = .vertical flowLayout.minimumInteritemSpacing = 0 flowLayout.minimumLineSpacing = 0 flowLayout.sectionInset = .zero let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) collectionView.backgroundColor = .black collectionView.showsVerticalScrollIndicator = true return collectionView } } } }