How to call a uikit viewcontroller method from swiftui view

我只是一个虾纸丫 提交于 2021-01-27 20:39:10

问题


I have looked all over for the answer to this question and can't seem to find it. How can I call a viewController method from swiftUI (e.g. on a button click)?

I have a viewcontroller that looks like this:

import Player

class PlayerViewController: UIViewController {
    var player = Player()
    func play() {
        self.player.play()
    }
}

And I have a wrapper that looks like this:

import SwiftUI
import AVFoundation

struct ProjectEditorPlayerBridge: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> PlayerViewController {
        let player = PlayerViewController()
        return player
    }
    
    func updateUIViewController(_ uiViewController: PlayerViewController, context: Context) {
    }
    
    typealias UIViewControllerType = PlayerViewController
}

I want to be able to use a button action in swiftUI and call the viewController play method once. I have seen answers that suggest setting state/binding on the wrapper and calling the method in updateUIViewController, but when I do this I see it gets called multiple times, not just once.


回答1:


Here is possible protocol/configurator based approach, which allows to use actions directly that looks more appropriate from code simplicity and readability.

protocol Player { // use protocol to hide implementation
    func play()
}

class PlayerViewController: UIViewController, Player {
    var player = Player()
    func play() {
        self.player.play()
    }
}

struct ProjectEditorPlayerBridge: UIViewControllerRepresentable {
    var configurator: ((Player) -> Void)? // callback

    func makeUIViewController(context: Context) -> PlayerViewController {
        let player = PlayerViewController()

        // callback to provide active component to caller
        configurator?(player)

        return player
    }

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

    typealias UIViewControllerType = PlayerViewController
}

struct DemoPlayerView: View {
    @State private var player: Player?     // always have current player

    var body: some View {
        VStack {
            ProjectEditorPlayerBridge { self.player = $0 }  // << here !!

            // use player action directly !!
            Button("Play", action: player?.play ?? {})
        }
    }
}




回答2:


Good question. It seems to me that something is missing from SwiftUI here.

If you only have one of these view controllers in your app, then you could workaround this by having a global PassthroughSubject (or another way to pass an event). Your UIViewController could subscribe to it, and your SwiftUI code could publish clicks to it.

If you don't want to do that, here is another workaround that uses UUID to get rid of those multiple calls you mentioned.

Maybe we'll see new options at WWDC 2020.

struct ContentView: View {
    @State var buttonClickID: UUID? = nil       
    var body: some View {
        VStack {
            Button(action: self.callPlay) { Text("Play") }
            ProjectEditorPlayerBridge(clickID: $buttonClickID)
        }
    }       
    func callPlay() {
        buttonClickID = UUID()
    }
}

struct ProjectEditorPlayerBridge: UIViewControllerRepresentable {

    @Binding var clickID: UUID?        
    
    func makeUIViewController(context: Context) -> PlayerViewController {
        let player = PlayerViewController()
        return player
    }
    
    class Coordinator {
        var previousClickID: UUID? = nil
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
    
    func updateUIViewController(_ uiViewController: PlayerViewController, context: Context) {
        print("Update")
        if clickID != context.coordinator.previousClickID {
            uiViewController.play()
            context.coordinator.previousClickID = clickID
        } else {
            print("Not calling play")
        }
    }
    
    typealias UIViewControllerType = PlayerViewController
}


来源:https://stackoverflow.com/questions/62504435/how-to-call-a-uikit-viewcontroller-method-from-swiftui-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!