What is NSViewControllerRepresentable
In SwiftUI, there are no View Controllers, only Views. So what should we do if want to use something related to View Controllers? Here comes NSViewControllerRepresentable/UIViewControllerRepresentable. Basically, we can use instance of View Controller Representable as View, contains a view controller.
Since view controllers have their own view, ViewControllerRepresentable has not view body.
protocol NSViewControllerRepresentable : View where Self.Body == Never
A Simple Sample of NSViewControllerRepresentable
- Create a new SwiftUI app.
- Add a new file, choose Cocoa Class, name it as TextViewController.swift.
Choose create xib file as well. - Add a close button and label.
- TextViewController.swift
//
// TextViewController.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import Cocoa
class TextViewController: NSViewController {
@IBOutlet weak var label: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func closeButtonClicked(_ sender: Any) {
self.dismiss(sender)
}
}
- TextView
//
// TextView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import AppKit
import SwiftUI
struct TextView:NSViewControllerRepresentable {
func makeNSViewController(context: Context) -> TextViewController {
let controller = TextViewController()
return controller
}
func updateNSViewController(_ nsViewController: TextViewController, context: Context) {
}
}
- ContentView.swift
//
// ContentView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import SwiftUI
struct ContentView: View {
@State var title = ""
@State var showTextView = false
var body: some View {
HStack {
TextField("Title", text: $title)
Button("Show Text View") {
showTextView = true
}
.sheet(isPresented: $showTextView) {
TextView()
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The app ran as below, but the close button didn't work.
Coordinator
View Controllers are class, and View Representables are structs. We use Coordinator to pass changes to SwiftUI.
- TextView
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import AppKit
import SwiftUI
struct TextView:NSViewControllerRepresentable {
@Environment(\.presentationMode) var presentationMode
@Binding var title:String
class Coordinator: NSObject, TextViewControllerDelegate {
var parent: TextView
init(_ parent: TextView) {
self.parent = parent
}
func textView(_ tvc: TextViewController) {
self.parent.presentationMode.wrappedValue.dismiss()
}
}
func makeNSViewController(context: Context) -> TextViewController {
let controller = TextViewController()
controller.delegate = context.coordinator
return controller
}
func updateNSViewController(_ nsViewController: TextViewController, context: Context) {
nsViewController.label.stringValue = title
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
- TextViewController
//
// TextViewController.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import Cocoa
class TextViewController: NSViewController {
@IBOutlet weak var label: NSTextField!
weak var delegate:TextViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func closeButtonClicked(_ sender: Any) {
delegate?.textView(self)
}
}
protocol TextViewControllerDelegate:AnyObject {
func textView(_ textView:TextViewController)
}
- ContentView
//
// ContentView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import SwiftUI
struct ContentView: View {
@State var title = ""
@State var showTextView = false
var body: some View {
HStack {
TextField("Title", text: $title)
Button("Show Text View") {
showTextView = true
}
.sheet(isPresented: $showTextView) {
TextView(title: $title)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}//
// ContentView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import SwiftUI
struct ContentView: View {
@State var title = ""
@State var showTextView = false
var body: some View {
HStack {
TextField("Title", text: $title)
Button("Show Text View") {
showTextView = true
}
.sheet(isPresented: $showTextView) {
TextView(title: $title)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
App ran like this and the close button worked.
Update Variables of NSViewController
You may aware that there was a func
func updateNSViewController(_ nsViewController: TextViewController, context: Context) {
nsViewController.label.stringValue = title
}
We couldn't call nsViewController.label.stringValue = title
in makeNSViewController
, as in the make function the View Controller was created, but the xib was not loaded. We can think this update as viewDidLoad function in View Controller.