- 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()
}
}
}