Skip to content

Instantly share code, notes, and snippets.

@tx-TEM
Last active September 22, 2022 07:40
Show Gist options
  • Save tx-TEM/90f413f00862cd9eb6efa8f5016119fc to your computer and use it in GitHub Desktop.
Save tx-TEM/90f413f00862cd9eb6efa8f5016119fc to your computer and use it in GitHub Desktop.
iOS14でCollectionViewCellにSwiftUIで作成したViewを表示する
  • iOS16以降はUIHostingConfigurationを使うと良いです。
  • 再利用の際にUIHostingのセットアップをしなおしているので、処理がやや重そうです。
final class HostingContentViewConfiguration<T: View>: UIContentConfiguration {
    private(set) weak var parentVC: UIViewController?
    private(set) var content: () -> T

    init(parentVC: UIViewController?, @ViewBuilder content: @escaping () -> T) {
        self.parentVC = parentVC
        self.content = content
    }

    func makeContentView() -> UIView & UIContentView {
        return HostingContentView(configuration: self)
    }

    func updated(for _: UIConfigurationState) -> Self {
        return self
    }
}

final class HostingContentView<T: View>: UIView, UIContentView {
    var hostingController: UIHostingController<T>

    var configuration: UIContentConfiguration {
        didSet {
            guard let config = configuration as? HostingContentViewConfiguration else {
                return
            }
            removeHostingControllerFromParent()
            hostingController = UIHostingController(rootView: config.content())
            setup(parentVC: config.parentVC)
        }
    }

    init(configuration: HostingContentViewConfiguration<T>) {
        self.configuration = configuration
        hostingController = UIHostingController(rootView: configuration.content())
        super.init(frame: .zero)
        setup(parentVC: configuration.parentVC)
    }

    @available(*, unavailable)
    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {
        removeHostingControllerFromParent()
    }
}

extension HostingContentView {
    public func setup(parentVC: UIViewController?) {
        guard let parentVC = parentVC else {
            return
        }
        let vc = hostingController
        vc.view.backgroundColor = .clear
        vc.willMove(toParent: parentVC)
        parentVC.addChild(vc)
        vc.didMove(toParent: parentVC)
        vc.view.translatesAutoresizingMaskIntoConstraints = false
        addSubview(vc.view)
        NSLayoutConstraint.activate([
            vc.view.topAnchor.constraint(equalTo: topAnchor),
            vc.view.bottomAnchor.constraint(equalTo: bottomAnchor),
            vc.view.leadingAnchor.constraint(equalTo: leadingAnchor),
            vc.view.trailingAnchor.constraint(equalTo: trailingAnchor),
        ])
    }

    public func removeHostingControllerFromParent() {
        hostingController.willMove(toParent: nil)
        hostingController.view.removeFromSuperview()
        hostingController.removeFromParent()
    }
}


private func cellRegistration() -> UICollectionView.CellRegistration<UICollectionViewCell, Item> {
    return UICollectionView.CellRegistration<UICollectionViewCell, Item> { [weak self] cell, _, _ in
        cell.contentConfiguration = HostingContentViewConfiguration(parentVC: self) {
            ContentView()
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment