Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
506 views
in Technique[技术] by (71.8m points)

uikit - Calling functions from UIViewController in SwiftUI

I'm looking to call a function inside a UIKit UIViewController from a button managed by Swift UI

In my Swift UI View I have:

struct CameraView: View {
        var body: some View {
            cameraViewController = CameraViewController()
...

which I see creates two instances, one directly created just like calling any class, and the other created by the required makeUIViewController method needed for Swift UI to manage UIKit UIViewControllers.

However when I attached a function to a button in my Swift UI say, cameraViewController.takePhoto() The instance that is referenced is not the one displayed.

How can I obtain the specific instance that is displayed?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

There are probably multiple solutions to this problem, but one way or another, you'll need to find a way to keep a reference to or communicate with the UIViewController. Because SwiftUI views themselves are pretty transient, you can't just store a reference in the view itself, because it could get recreated at any time.

Tools to use:

  • ObservableObject -- this will let you store data in a class instead of a struct and will make it easier to store references, connect data, etc

  • Coordinator -- in a UIViewRepresentable, you can use a Coordinator pattern which will allow you to store references to the UIViewController and communicate with it

  • Combine Publishers -- these are totally optional, but I've chosen to use them here since they're an easy way to move data around without too much boilerplate code.

import SwiftUI
import Combine

struct ContentView: View {
    @ObservedObject var vcLink = VCLink()
    var body: some View {
        VStack {
            VCRepresented(vcLink: vcLink)
            Button("Take photo") {
                vcLink.takePhoto()
            }
        }
    }
}

enum LinkAction {
    case takePhoto
}

class VCLink : ObservableObject {
    @Published var action : LinkAction?
    
    func takePhoto() {
        action = .takePhoto
    }
}

class CustomVC : UIViewController {
    func action(_ action : LinkAction) {
        print("(action)")
    }
}

struct VCRepresented : UIViewControllerRepresentable {
    var vcLink : VCLink
    
    class Coordinator {
        var vcLink : VCLink? {
            didSet {
                cancelable = vcLink?.$action.sink(receiveValue: { (action) in
                    guard let action = action else {
                        return
                    }
                    self.viewController?.action(action)
                })
            }
        }
        var viewController : CustomVC?
        
        private var cancelable : AnyCancellable?
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
    
    func makeUIViewController(context: Context) -> CustomVC {
        return CustomVC()
    }
    
    func updateUIViewController(_ uiViewController: CustomVC, context: Context) {
        context.coordinator.viewController = uiViewController
        context.coordinator.vcLink = vcLink
    }
}

What happens here:

  1. VCLink is an ObservableObject that I'm using as a go-between to communicate between views
  2. The ContentView has a reference to the VCLink -- when the button is pressed, the Publisher on VCLink communicates that to any subscribers
  3. When the VCRepresented is created/updated, I store a reference to the ViewController and the VCLink in its Coordinator
  4. The Coordinator takes the Publisher and in its sink method, performs an action on the stored ViewController. In this demo, I'm just printing the action. In your example, you'd want to trigger the photo itself.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...