I'm trying to create a SwiftUI alert dialog that contains a picker view inside of it, similar to the example here https://github.com/zhiyao92/PickerView-on-AlertController.
This is the code I've built so far in PickerAlert.swift
import SwiftUIpublic struct PickerAlert { public var title: String = "" // Title of the dialog public var message: String = "" // Dialog message public var pickerData: [String] = ["red", "green"] // Data for the picker public var action: (String?) -> Void // Triggers when either of the two buttons closes the dialog}extension UIAlertController { convenience init(alert: PickerAlert) { self.init(title: alert.title, message: alert.message, preferredStyle: .alert) let pickerFrame = UIPickerView(frame: CGRect(x: 5, y: 20, width: 250, height: 140)) pickerFrame.reloadAllComponents() // Set the picker view as the accessory view of the alert controller self.view.addSubview(pickerFrame) self.view.translatesAutoresizingMaskIntoConstraints = false self.view.addConstraints([ pickerFrame.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 60), pickerFrame.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), pickerFrame.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20), pickerFrame.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -50) ]) addAction(UIAlertAction(title: "Cancel", style: .destructive) { _ in alert.action(nil) }) addAction(UIAlertAction(title: "OK", style: .default) { _ in alert.action("dummy data") }) }}struct PickerAlertWrapper<Content: View>: UIViewControllerRepresentable { @Binding var isPresented: Bool let alert: PickerAlert let content: Content func makeUIViewController(context: UIViewControllerRepresentableContext<PickerAlertWrapper>) -> UIHostingController<Content> { UIHostingController(rootView: content) } final class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate { var alertController: UIAlertController? let alert: PickerAlert init(alert: PickerAlert, controller: UIAlertController? = nil) { self.alert = alert self.alertController = controller super.init() // Create the picker view instance and add it as a subview to the alert controller's view if let pickerFrame = alertController?.view.subviews.first as? UIPickerView { pickerFrame.dataSource = self pickerFrame.delegate = self pickerFrame.reloadAllComponents() } } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return alert.pickerData.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return alert.pickerData[row] } } func makeCoordinator() -> Coordinator { let coordinator = Coordinator(alert: alert) return coordinator } func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: UIViewControllerRepresentableContext<PickerAlertWrapper>) { uiViewController.rootView = content if isPresented && uiViewController.presentedViewController == nil { var alert = self.alert alert.action = { self.isPresented = false self.alert.action($0) } context.coordinator.alertController = UIAlertController(alert: alert) uiViewController.present(context.coordinator.alertController!, animated: true) } if !isPresented && uiViewController.presentedViewController == context.coordinator.alertController { uiViewController.dismiss(animated: true) } }}extension View { public func alert(isPresented: Binding<Bool>, _ alert: PickerAlert) -> some View { PickerAlertWrapper(isPresented: isPresented, alert: alert, content: self) }}
This is the code of a SwiftUI view that shows the alert with the picker.
struct MyView: View { @StateObject var viewModel = MyViewModel() var body: some View { ZStack { Text("") .hidden() .alert(isPresented: $viewModel.showSelectHomeAlert, PickerAlert( title: "asdf" ) { selection in if let selection = selection { print("selection: \(selection)") } } ) ... } }}
This is a screenshot of the output I'm seeing. It doesn't look like the UIPickerView is getting populated with the default values of "red" and "green" that I put. How can I get this to work?