Skip to content

Instantly share code, notes, and snippets.

@YanSte
Last active August 18, 2022 08:01
Show Gist options
  • Select an option

  • Save YanSte/544d73b4f077c00bfb8cde0a218f4c8d to your computer and use it in GitHub Desktop.

Select an option

Save YanSte/544d73b4f077c00bfb8cde0a218f4c8d to your computer and use it in GitHub Desktop.

Revisions

  1. YanSte revised this gist Aug 18, 2022. 1 changed file with 0 additions and 6 deletions.
    6 changes: 0 additions & 6 deletions UIWrapping.swift
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,3 @@
    //
    // UIWrapping.swift
    //
    // Created by Yannick Stephan on 29/06/2022.
    //

    import SwiftUI

    /// Provides a convenient 'wrapper' around a UIView
  2. YanSte created this gist Aug 10, 2022.
    138 changes: 138 additions & 0 deletions UIWrapping.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,138 @@
    //
    // UIWrapping.swift
    //
    // Created by Yannick Stephan on 29/06/2022.
    //

    import SwiftUI

    /// Provides a convenient 'wrapper' around a UIView
    public struct UIWrapping<V>: 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<V>, update handler: RepresentableUpdater<V>? = 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<V>, update handler: RepresentableUpdater<V>? = 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> = () -> V
    public typealias RepresentableUpdater<V> = (V) -> Void

    private struct ViewWrapper<V: UIView>: UIViewRepresentable {
    let provider: RepresentableProvider<V>
    let updater: RepresentableUpdater<V>?

    func makeUIView(context: Context) -> V {
    provider()
    }

    func updateUIView(_ view: V, context: Context) {
    view.backgroundColor = .clear
    updater?(view)
    }
    }

    // MARK: - Private ViewControllerWrapper

    private struct ViewControllerWrapper<V: UIViewController>: 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<UILabel> { view in
    view.numberOfLines = 0
    view.text = "Hello 😄"
    view.textAlignment = .center
    }
    .fixedSize()

    UIWrapping<UIActivityIndicatorView> { 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
    }
    }
    }
    }