Search code examples
iosswiftswiftuiuikit

How do I call a function written in UIKit/UIController/Storyboard from SwiftUI button


I am a SwiftUI newbie struggling to add SwiftUI functionality to my existing UIKit/Storyboard code. I would like to call a UIKit function from SwiftUI button. Greatly appreciate your help. Here is the relevant code simplified for this discussion. From the code below, I'd like to call the functions startAction() and stopAction() from the If statement in SwiftUI...

                    if (startStop_flag) {

                        //******** call StartAction()

                    } else {

                        //******* call StopAction()

                    }

The entire code below. Some context: when the app is run, the bottom half of the screen will show "UIkit Storyboard View" and show the button "Open Swift Container View". When the user clicks this button, the SwiftUI container view will open up. This view will display "This is a swiftUI view" and display a button "Start/Stop". When the user clicks this button, StartAction() or StopAction() needs to be called. These two functions reside in the UIViewController. I hope I am clear with the problem and the request.

ViewController.swift 

class ViewController: UIViewController {
    @IBOutlet weak var nativeView: UIView!
    @IBOutlet weak var nativeView_Label: UILabel!
    @IBOutlet weak var nativeView_openSwiftViewBtn: UIButton!
    @IBOutlet weak var containerView_forSwiftUI: UIView!



    
    @IBSegueAction func embedSwiftUIView(_ coder: NSCoder) -> UIViewController? {
        return UIHostingController(coder: coder, rootView: SwiftUIView2(text: "Container View"))
    }
    
    var toggleOpenCloseContainerView : Bool = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        
        containerView_forSwiftUI.isHidden = true
    }

    @IBAction func openContainerView_touchInside(_ sender: Any) {
        
        if (toggleOpenCloseContainerView) {
            containerView_forSwiftUI.isHidden = false
            toggleOpenCloseContainerView = false
            nativeView_openSwiftViewBtn.setTitle("Close Swift Container View", for: .normal)
        } else {
            containerView_forSwiftUI.isHidden = true
            toggleOpenCloseContainerView = true
            nativeView_openSwiftViewBtn.setTitle("Open Swift Container View", for: .normal)
        }
        
    }
    
    
    // These two functions need to be called from the SwiftUI's button.
    
    func startAction() {
        
        print("Start Action called from SwiftUI's button")
    }
    
    func stopAction() {
        
        print("Stop Action called from SwiftUI's button")
    }
    
}

The swiftUI functions are in this file


struct SwiftUIView2: View {
    
    var text: String
    @State var startStop_flag: Bool = true
    
    var body: some View {
        VStack {
            Text(text)
            HStack {
                Image(systemName: "smiley")
                Text("This is a SwiftUI View")
                Spacer()
                
                Button("\(startStop_flag ? "Start": "Stop")") {
                    startStop_flag = !startStop_flag
                    
                    if (startStop_flag) {
                        
                        //******** call StartAction()
                        
                    } else {
                        
                        //******* call StopAction()
                        
                    }
                    
                }   .padding()
                    .background(Color.red)
                    .cornerRadius(40)
                    .foregroundColor(.white)
                    .padding(5)
                    .overlay(
                        RoundedRectangle(cornerRadius: 40)
                            .stroke(Color.red, lineWidth: 1)
                    )
            }
        }
        .font(.largeTitle)
        .background(Color.blue)
        
    }
    
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIView2(text: "Sample Text")
    }
}

Solution

  • You can use closures for this. First, define and call them inside your SwiftUI view.

    struct SwiftUIView2: View {
        
        var text: String
        var startAction: (() -> Void) /// define closure 1
        var stopAction: (() -> Void) /// define closure 2
        
        ...
        ...
        
        Button("\(startStop_flag ? "Start": "Stop")") {
            startStop_flag = !startStop_flag
            
            if (startStop_flag) {
                //******** call StartAction()
                startAction()
            } else {
                //******* call StopAction()
                stopAction()
            }
        }
    }
    
    

    Then, just assign the closure's contents inside ViewController.swift.

    @IBSegueAction func embedSwiftUIView(_ coder: NSCoder) -> UIViewController? {
        return UIHostingController(
            coder: coder,
            rootView: SwiftUIView2(
                text: "Container View",
                startAction: { [weak self] in
                    self?.startAction()
                },
                stopAction: { [weak self] in
                    self?.stopAction()
                }
            )
        )
    }