2

I have to replace an existing SwiftUI List of views with a UICollectionView(because the app designs were updated and the new designs are pretty complex for SwiftUI so it that had to be implemented as a custom UICollectionViewFlowLayout)

So the views(which now will be (or a part of)UICollectionViewCell) are already implemented in SwiftUI, and i didn't want to re-write them in Swift. The view are complex in terms of writing down the Layout Code, which apparently was pretty easy using SwiftUI.

I could find some help wrapping up a collection view like this one but little on how to host an existing swift ui view inside a UICollectionViewCell

Any help/suggestions/links would be appreciated.

Thanks

Pulkit Vaid
  • 103
  • 1
  • 8

1 Answers1

3

I did get that working. But not sure if that's the correct solution. If anyone has a better solution please add your answer. So i created a UICollectionViewCell sub-class.

final class HostingCollectionViewCell: UICollectionViewCell {
    func host<Content: View>(_ hostingController: UIHostingController<Content>) {
        backgroundColor = .clear
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        hostingController.view.backgroundColor = .clear
        addSubview(hostingController.view)

        let constraints = [
            hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
            hostingController.view.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0),
            hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
            hostingController.view.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0),
        ]
        NSLayoutConstraint.activate(constraints)
    }
}

And used it like..

func makeUIView(context: Context) -> UICollectionView {
     let collectionView = UICollectionView(
         frame: .zero,
         collectionViewLayout: MyCustomCollectionViewLayout()
     )
     collectionView.register(HostingCollectionViewCell.self, forCellWithReuseIdentifier: "HostingCell")

     let dataSource = UICollectionViewDiffableDataSource<Section, Member>(collectionView: collectionView) { collectionView, indexPath, member in
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HostingCell", for: indexPath) as? HostingCollectionViewCell
         for subview in cell?.contentView.subviews ?? [] {
             subview.removeFromSuperview()
         }

        let swiftUIView = SomeSwiftUIView()
            .onTapGesture {
                // Equivalent of didTapItemAt...
            }

        cell?.host(UIHostingController(rootView: firefighterView))
        return cell
    }

    context.coordinator.dataSource = dataSource
    collectionView.clipsToBounds = false
    collectionView.backgroundColor = .clear
    collectionView.showsVerticalScrollIndicator = false

    // Populated Datasource 

    return collectionView
}

This makeUIView is part of the struct SwiftUICollectionView: UIViewRepresentable view.

Pulkit Vaid
  • 103
  • 1
  • 8
  • 1
    I tried this, I have a button in my SwiftUI view and it seems that when I tap on the button the action is not performed. Any idea why? Could it be because we are extracting the view from the hosting controller? – Hunter Meyer Nov 16 '20 at 05:43
  • Nevermind, I got it working by constraining the hostingController.view to the cell itself, rather than the Content View. This allows me to interact with the Buttons in my SwiftUI view – Hunter Meyer Nov 19 '20 at 01:29