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