SwiftUI as a cell inside a TableView Controller

I have a DisclosureGroup(isExpanded: ..) inside a SwiftUI view that shows checkbox options with a button that when clicked shows an Alert for the user to accept/store their selection. The SwiftUI view is inside a tableView cell using cell.contentConfiguration = UIHostingConfiguration {..}

  • The Alert action from the SwiftUI view has to call a function to store the user’s choice that is located in the Tableview controller
  • it will also have to delete the current row updating the tableview UI immediately.

The data on the SwiftUI View are sent by TableViewController using a class ShowContent: ObservableObject {.. through @Published properties.

  • Question:

How can I make a trigger action to update the UIKit UI coming from the SwiftUI View Alert to make the tableView be updated asap?

Accepted Answer

The talk Use SwiftUI with UIKit SwiftUI with UIKit] and sample Using SwiftUI with UIKit cover this topic.


Traditionally, when using UIKit, you would need to manually tell the diffable data source about these changes by reconfiguring or reloading items in the snapshot. But when using SwiftUI in cells, this isn't necessary anymore. By storing the ObservableObject model in an ObservedObject property in our SwiftUI view, changes to published properties of the model will automatically trigger SwiftUI to refresh the view. This establishes a direct connection between the model and the SwiftUI view inside of the cell. When a change is made, the SwiftUI views in the cell are updated directly, without going through the diffable data source or the UICollectionView. When a cell's data changes, it may cause the cell to need to grow or shrink to fit the new content. But if the SwiftUI cell content is being updated directly without going through UIKit, how does the collection view know to resize the cell? UIHostingConfiguration takes advantage of a brand-new feature in UIKit to make this work.


Hopefully this helps. RIco

Thanks for the reply Rico. I watched the talk Use SwiftUI with UIKit several times and tried the sample too. Maybe with an image I can make myself clear:

After a selection the user can click the button which means Accept Bid then an Alert is displayed

If Aceito! is chosen it means that the equipment is no longer for sale and the row must be removed from the LancesTableView.

                CheckBoxUIView(showContent:  ShowContent(false, buttons: buttons[indexPath.row], oferta: offers[indexPath.row] )) // accepted
                    
                    .swipeActions(edge: .trailing, allowsFullSwipe: false) {
                        Button(role: .destructive) { [weak self] in
                            guard let indexPath = self?.tableView.indexPath(for: cell) else {
                                         return}
                            self!.buttons.remove(at: indexPath.row)
                            self?.offers.remove(at: indexPath.row)
                            self!.tableView.deleteRows(at: [indexPath], with: .automatic)
                            self!.showAlert(title: "Deleted", message: "***** ", row: indexPath.row)
                        } label: {
                            Label("Delete", systemImage: "trash")
                        }

If swipeAction is used like the code above shows I can get the UI effect I need but the Alert is inside the cell.

  • How can I make a trigger action coming from the SwiftUI cell's Alert to call a function from the tableView controller so that the tableview's row can be updated/deleted?

Okay now when the data being presented by the ObservableObject model was deleted by the Accept Alert action using the ObservedObject property, the cell was updated and I could leave a message to the user:

About the cell to need to grow or shrink it was already working great using in UITableView, heightForRowAt indexPath

if indexPath.section == 2  {
     return UITableView.automaticDimension }

Hi Amilnyleve, I am the SwiftUI engineer who talked to you in the lab session. As a follow up I want to share how to trigger your delete row action within SwiftUI alerts - you would need to declare a closure for your SwiftUI view which will be executed in one of the Alert actions, and the closure could be the same piece of logic that deletes a row from UITableView. It will go something like this:

struct CheckBoxUIView: View {
     var deleteRowAction: () -> Void // this is the closure you would declare, the actual logic will be provided by yourself when you construct this view

    var body: some View {
        // ..
        .alert(
            /* ... */,
            isPresented: /* ... */,
            presenting: /* ... */
        ) { details in
            Button(role: .destructive) {
                // Here you will execute the closure
                deleteRowAction()
            } label: {
                Text("Delete")
            }
        } message: { details in
            /* ... */
        }
    }
}

And then when you are constructing that CheckBoxUIView, add the closure implementation like this:

CheckBoxUIView(deleteRowAction: {
    guard let indexPath = self?.tableView.indexPath(for: cell) else { return }
    self!.buttons.remove(at: indexPath.row)
    self?.offers.remove(at: indexPath.row)
    self!.tableView.deleteRows(at: [indexPath], with: .automatic)
    self!.showAlert(title: "Deleted", message: "***** ", row: indexPath.row)
})

That way, you can share the same logic you used on the swipe actions to properly remove the row. If you have any further questions, please continue to post in the forum, thank you!

Okay thanks and emphasising how the cell is being configured:

cell.contentConfiguration = UIHostingConfiguration {
             CheckBoxUIView(showContent: ShowContent(false, buttons: buttons[indexPath.row], oferta: offers[indexPath.row] )) 
     .swipeActions(edge: .trailing, allowsFullSwipe: false) { ...}
...
}

I can't ask for another lab session now that I've got my security settings for screen sharing fixed they are closed now but I will test your suggestion asap and let you guys know how it goes.

SwiftUI as a cell inside a TableView Controller
 
 
Q