web-dev-qa-db-fra.com

Implémenter des délégués dans les vues SwiftUI

J'essaie d'implémenter une fonctionnalité qui nécessite une méthode déléguée (comme NSUserActivity). Par conséquent, j'ai besoin d'un UIViewController qui est conforme à NSUserActivityDelegate (ou à d'autres délégués similaires), gère et détient toutes les informations requises. Mon problème est que j'utilise SwiftUI pour mon interface et donc je n'utilise pas UIViewControllers. Alors, comment puis-je implémenter cette fonctionnalité et toujours utiliser SwiftUI pour l'interface utilisateur. Ce que j'ai essayé: view1 est juste un SwiftUI View normal qui peut présenter (via NavigationLink) view2 qui est la vue où vous souhaitez implémenter cette fonctionnalité. J'ai donc essayé au lieu de lier view1 et view2, de lier view1 à un UIViewControllerRepresentable qui gère ensuite l'implémentation de cette fonctionnalité et ajoute UIHostingController(rootView: view2) comme contrôleur de vue enfant.

struct view1: View {    
    var body: some View {
        NavigationLink(destination: VCRepresentable()) {
            Text("Some Label")
        }
    }
}

struct view2: View {    
    var body: some View {
        Text("Hello World!")
    }
}

struct VCRepresentable: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        return implementationVC()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
}

class implementationVC: UIViewController, SomeDelegate for functionality {
    // does implementation stuff in delegate methods
    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        attachChild(UIHostingController(rootView: view2()))
    }

    private func attachChild(_ viewController: UIViewController) {
        addChild(viewController)

        if let subview = viewController.view {
            subview.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(subview)

            subview.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
            subview.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
            subview.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
            subview.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        }

        viewController.didMove(toParent: self)
    }
}

J'ai du mal à transférer les données entre mon VC et ma vue2. Je me demande donc s'il existe un meilleur moyen de mettre en œuvre une telle fonctionnalité dans une vue SwiftUI.

9
L. Stephan

Vous devez créer une vue conforme à UIViewControllerRepresentable et dotée d'un Coordinator qui gère toutes les fonctionnalités de délégué.

Par exemple, avec votre exemple de contrôleur de vue et de délégués:

struct SomeDelegateObserver: UIViewControllerRepresentable {
    let vc = SomeViewController()
    var foo: (Data) -> Void
    func makeUIViewController(context: Context) -> SomeViewController {
        return vc
    }

    func updateUIViewController(_ uiViewController: SomeViewController, context: Context) { }
    func makeCoordinator() -> Coordinator {
        Coordinator(foo: foo)
    }

    class Coordinator: NSObject, SomeDelegate {
        var foo: (Data) -> Void
        init(vc: SomeViewController, foo: @escaping (Data) -> Void) {
            self.foo = foo
            super.init()
            vc.delegate = self
        }
        func someDelegateFunction(data: Data) {
            foo()
        }
    }
}

Usage:

struct ContentView: View {
    var dataModel: DataModel

    var body: some View {
        NavigationLink(destination: CustomView(numberFromPreviousView: 10)) {
            Text("Go to VCRepresentable")
        }
    }
}

struct CustomView: View {
    @State var instanceData1: String = ""
    @State var instanceData2: Data?
    var numberFromPreviousView: Int // example of data passed from the previous view to this view, the one that can react to the delegate's functions
    var body: some View {
        ZStack {
            SomeDelegateObserver { data in
                print("Some delegate function was executed.")
                self.instanceData1 = "Executed!"
                self.instanceData2 = data
            }
            VStack {
                Text("This is the UI")
                Text("That, in UIKit, you would have in the UIViewController")
                Text("That conforms to whatever delegate")
                Text("SomeDelegateObserver is observing.")
                Spacer()
                Text(instanceData1)
            }
        }
    }
}

Remarque: j'ai renommé VCRepresentable en SomeDelegateObserver pour être plus représentatif de ce qu'il fait: son seul but est d'attendre que les fonctions déléguées s'exécutent puis d'exécuter les fermetures ( c'est-à-dire foo dans cet exemple) vous le fournissez. Vous pouvez utiliser ce modèle pour créer autant de fonctions que nécessaire pour "observer" les fonctions de délégué qui vous intéressent, puis exécuter du code qui peut mettre à jour l'interface utilisateur, votre modèle de données, etc. Dans mon exemple, lorsque SomeDelegate fires someDelegateFunction(data:), la vue affichera "Excuted" et mettra à jour la variable d'instance de données.

7
RPatel99