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

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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?
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.