Reputation: 21
I'm attempting to add a Find bar to an NSTextView within a SwiftUI view, but despite setting the necessary properties (usesFindBar, usesFindPanel, isIncrementalSearchingEnabled), the Find bar doesn't appear. Here's the code I'm using:
import AppKit
struct TextView: NSViewRepresentable {
@Binding var selectedRange: NSRange?
@Binding var text: String
var onAddingNewLine: (() -> Void)?
@Binding var textToReplace: (text: String, range: NSRange)?
@Binding var shouldInsertText: Bool?
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NSTextView.scrollableTextView()
guard let textView = scrollView.documentView as? NSTextView else {
return scrollView
}
textView.allowsUndo = true
textView.usesFindBar = true
textView.usesFindPanel = false
textView.isIncrementalSearchingEnabled = true
textView.window?.makeFirstResponder(textView)
textView.isContinuousSpellCheckingEnabled = true
textView.font = NSFont.monospacedSystemFont(ofSize: 15, weight: .regular)
textView.textColor = NSColor(Color.onSurface)
DispatchQueue.main.async {
textView.string = text
textView.delegate = context.coordinator
}
return scrollView
}
func updateNSView(_ scrollView: NSScrollView, context: Context) {
guard let textView = scrollView.documentView as? NSTextView else {
return
}
if let textToReplace {
DispatchQueue.main.async {
let textToInsert = textToReplace
self.textToReplace = nil
if shouldInsertText ?? false {
textView.insertText(textToInsert.text, replacementRange: textToInsert.range)
}
shouldInsertText = false
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, NSTextViewDelegate {
var parent: TextView
init(_ parent: TextView) {
self.parent = parent
}
func textDidEndEditing(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else { return }
parent.text = textView.string
textView.undoManager?.removeAllActions()
}
func textDidChange(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else { return }
let string = textView.string
let length = string.count
let insertionPoint = textView.selectedRange.location
let low = max(0, insertionPoint - 1)
if let range = Range(NSRange(location: low, length: 1), in: string) {
let newChar = string[range]
if length > 0 && length > parent.text.count, newChar == "\n" {
parent.text = textView.string
parent.onAddingNewLine?()
} else {
parent.text = textView.string
}
} else {
parent.text = textView.string
}
}
func textViewDidChangeSelection(_ notification: Notification) {
guard let textView = notification.object as? NSTextView else { return }
parent.selectedRange = textView.selectedRange
}
}
}
This properties(usesFindBar, usesFindPanel, isIncrementalSearchingEnabled) works when using a NSTextView created through Storyboard, but not when using it within SwiftUI.
Also tried out Findbar using this MacEditorTextView
Any insights into why the Find bar isn't showing up would be greatly appreciated!
Upvotes: 0
Views: 91
Reputation: 11
I was able to get this working by creating this extension
extension NSTextView {
func showFindBar() {
let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "")
menuItem.tag = NSTextFinder.Action.showFindInterface.rawValue
self.performTextFinderAction(menuItem)
}
}
I also moved textView.window?.makeFirstResponder(textView)
into DispatchQueue.main.async
Then I just call that showFindBar function from a custom menu bar item tied to the keyboard shortcut F via .commands { CommandGroup(after: .undoRedo) }
Upvotes: 1