Skip to content

Instantly share code, notes, and snippets.

@zentrope
Created October 2, 2022 00:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zentrope/29e2e4692c040394291a0fa8806276ed to your computer and use it in GitHub Desktop.
Save zentrope/29e2e4692c040394291a0fa8806276ed to your computer and use it in GitHub Desktop.
NSTextView wrapper for SwiftUI
import Combine
import SwiftUI
struct RichTextEditor: NSViewRepresentable {
@Binding var attributedText: NSAttributedString
@Binding var showInspector: Bool
var activity: PassthroughSubject<Date, Never>
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeNSView(context: Context) -> NSScrollView {
let scrollview = NSTextView.scrollableTextView()
let textview = scrollview.documentView as! NSTextView
textview.delegate = context.coordinator
textview.isEditable = true
textview.isRichText = true
textview.allowsUndo = true
textview.isFieldEditor = false
textview.usesAdaptiveColorMappingForDarkAppearance = true
textview.drawsBackground = false // true
textview.usesRuler = false
textview.usesFindBar = true
textview.isIncrementalSearchingEnabled = true
textview.isAutomaticQuoteSubstitutionEnabled = true
textview.isAutomaticDashSubstitutionEnabled = true
textview.usesInspectorBar = showInspector
textview.usesFontPanel = true
textview.importsGraphics = true
textview.allowsImageEditing = true
textview.isAutomaticSpellingCorrectionEnabled = true
textview.isAutomaticDataDetectionEnabled = true
textview.isAutomaticLinkDetectionEnabled = true
textview.displaysLinkToolTips = true
textview.backgroundColor = NSColor.textBackgroundColor
textview.textContainerInset = NSSize(width: 10, height: 10)
textview.isContinuousSpellCheckingEnabled = true
textview.setSelectedRange(NSMakeRange(0, 0))
return scrollview
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
let textview = nsView.documentView as! NSTextView
if textview.usesInspectorBar != showInspector {
textview.usesInspectorBar = showInspector
}
guard textview.attributedString() != attributedText else {
return
}
guard let storage = textview.textStorage else {
print("WARNING: Text storage is missing.")
return
}
storage.setAttributedString(attributedText)
}
class Coordinator: NSObject, NSTextViewDelegate {
var parent: RichTextEditor
var affectedCharRange: NSRange?
init(_ parent: RichTextEditor) {
self.parent = parent
}
func textDidChange(_ notification: Notification) {
guard let textview = notification.object as? NSTextView else {
return
}
self.parent.activity.send(Date.now)
self.parent.attributedText = textview.attributedString()
}
func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool {
return true
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment