Skip to content

Instantly share code, notes, and snippets.

@simonbs
Created January 27, 2023 08:39
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonbs/8db8b52904f523bc11cf701030dd3d30 to your computer and use it in GitHub Desktop.
Save simonbs/8db8b52904f523bc11cf701030dd3d30 to your computer and use it in GitHub Desktop.
Shows how a multi-platform SwiftUI can bridge to UIKit and AppKit.
// Implementation of the view using AppKit.
#if os(macOS)
import AppKit
import SwiftUI
final class AppKitTextView: NSView {
let textView: NSTextView = {
let this = NSTextView()
this.translatesAutoresizingMaskIntoConstraints = false
return this
}()
init() {
super.init(frame: .zero)
addSubview(textView)
NSLayoutConstraint.activate([
textView.leadingAnchor.constraint(equalTo: leadingAnchor),
textView.trailingAnchor.constraint(equalTo: trailingAnchor),
textView.topAnchor.constraint(equalTo: topAnchor),
textView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
struct AppKitTextViewRepresentable: NSViewRepresentable {
@Binding var text: String
func makeNSView(context: Context) -> some NSView {
let textView = AppKitTextView()
textView.textView.delegate = context.coordinator
return textView
}
func updateNSView(_ nsView: NSViewType, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(text: $text)
}
}
extension AppKitTextViewRepresentable {
final class Coordinator: NSObject, NSTextViewDelegate {
private var text: Binding<String>
init(text: Binding<String>) {
self.text = text
}
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else {
return
}
text.wrappedValue = textView.string
}
}
}
#endif
// This uses our multi-platform view and has no knowledge of the different platforms.
struct ContentView: View {
@State private var text: String = ""
var body: some View {
TextView(text: $text)
.onChange(of: text) { newValue in
print(newValue)
}
}
}
// A wrapper that chooses either the UIKit or AppKit implementation depending on which platform we are compiling for.
struct TextView: View {
@Binding var text: String
var body: some View {
#if os(iOS)
UIKitTextViewRepresentable(text: $text)
#else
AppKitTextViewRepresentable(text: $text)
#endif
}
}
// Implementation of the view using UIKit.
#if os(iOS)
import UIKit
import SwiftUI
final class UIKitTextView: UIView {
let textView: UITextView = {
let this = UITextView()
this.translatesAutoresizingMaskIntoConstraints = false
return this
}()
init() {
super.init(frame: .zero)
addSubview(textView)
NSLayoutConstraint.activate([
textView.leadingAnchor.constraint(equalTo: leadingAnchor),
textView.trailingAnchor.constraint(equalTo: trailingAnchor),
textView.topAnchor.constraint(equalTo: topAnchor),
textView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
struct UIKitTextViewRepresentable: UIViewRepresentable {
@Binding var text: String
func makeUIView(context: Context) -> some UIView {
let textView = UIKitTextView()
textView.textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(text: $text)
}
}
extension UIKitTextViewRepresentable {
final class Coordinator: NSObject, UITextViewDelegate {
private var text: Binding<String>
init(text: Binding<String>) {
self.text = text
}
func textViewDidChange(_ textView: UITextView) {
text.wrappedValue = textView.text
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment