UIKit UITableViewCell holds space for items removed from embedded SwiftUI LazyVGrid

I'm having an issue where I have a grid with a number of items that I'm retrieving from an API. It's a SwiftUI LazyVGrid embedded in a UIKit storyboard TableView as a UITableViewCell.

The user has the ability to dismiss the items on a different screen, and then the main screen refreshes to reflect the change. Everything looks fine until the user dismisses enough items to reduce the number of rows. The items themselves still go away, but it looks like the hosting controller isn't responding to the changing height of the grid, so it leaves a weird-looking empty space.

Looking through the view hierarchy, I see that every time an item is removed, the updated grid view is stacked on top of the old view instead of replacing it, so I thought that could be causing the issue, if the constraints are still taking the old view into account.

Is there any way to stop this extra space from showing?

Here's the table cell that's hosting the SwiftUI view:

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: .default, reuseIdentifier: "opportunityGridCell")
  }
   
  func host(_ view: Content, parent: UIViewController) {
    let hostingController = UIHostingController(rootView: view)
    hostingController.view.backgroundColor = .lightGray
     
    layoutIfNeeded()
     
    parent.addChild(hostingController)
    contentView.addSubview(hostingController.view)
     
    hostingController.view.translatesAutoresizingMaskIntoConstraints = false
    hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
    hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    hostingController.view.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
    hostingController.view.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true

    hostingController.didMove(toParent: parent)
    hostingController.view.layoutIfNeeded()
  }
}

And the SwiftUI view:


struct OpportunityGridView: View {
  @ObservedObject var opportunityViewModel: OpportunityViewModel
  let goToDetailsPage: (PolicyOpportunity, OpportunityContent) -> Void

   
  let columns = [
    GridItem(.flexible(), alignment: .top),
    GridItem(.flexible(), alignment: .top),
    GridItem(.flexible(), alignment: .top)
  ]
   
  var body: some View {
    VStack(alignment: .leading) {
      Spacer()
   
      ScrollView {
        LazyVGrid(columns: columns, spacing: 20) {
          ForEach(opportunityViewModel.opportunities, id: \.self) { opportunity in
            if let opportunityContent = opportunityViewModel.getOpportunityContent(opportunity: opportunity) {
              Button(action: {
                goToDetailsPage(opportunity, opportunityContent)
              }, label: {
                OpportunityView(opportunityImage: Image(opportunityContent.opportunityIconName), opportunityLabel: opportunityContent.cellTitle)
              })
            }
          }
        }
      }
    }
  }
}
Answered by KBartlett in 718796022

I found a fix for this issue - I needed to remove the view from the parent when coming back to the main screen, like so:

    hostingController.willMove(toParent: nil)
    hostingController.view.removeFromSuperview()
    hostingController.removeFromParent()
Accepted Answer

I found a fix for this issue - I needed to remove the view from the parent when coming back to the main screen, like so:

    hostingController.willMove(toParent: nil)
    hostingController.view.removeFromSuperview()
    hostingController.removeFromParent()
UIKit UITableViewCell holds space for items removed from embedded SwiftUI LazyVGrid
 
 
Q