Reputation: 105
I am working on a macOS application. I want to write a code that shows the cursor on the click location in NSTextView
. So I searched in StackOverFlow and found the following code for iOS:
@objc func didTapTextView(recognizer: UITapGestureRecognizer) {
if recognizer.state == .ended {
textView.isEditable = true
textView.becomeFirstResponder()
let location = recognizer.location(in: textView)
if let position = textView.closestPosition(to: location) {
let uiTextRange = textView.textRange(from: position, to: position)
if let start = uiTextRange?.start, let end = uiTextRange?.end {
let loc = textView.offset(from: textView.beginningOfDocument, to: position)
let length = textView.offset(from: start, to: end)
textView.selectedRange = NSMakeRange(loc, length)
}
}
}
}
So, to convert the code from iOS to macOS, I can:
UITapGestureRecognizer
to NSClickGestureRecognizer
textView.becomeFirstResponder()
to textView.window?.makeFirstResponder(textView)
The problem is:
I can't convert the methods .closestPosition
, .textRange
and .offset
to macOS, because they are only available in UIKit, and they return UIKit values like UITextPosition
and UITextRange
.
Minimal Reproducible Example
To reproduce the problem, just create a document-based Cocoa app (macOS), and add a Scrollable TextView, then the code will be:
import Cocoa
class ViewController: NSViewController, NSGestureRecognizerDelegate {
@IBOutlet var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
// Set tap gesture
let singleClickGesture = NSClickGestureRecognizer(target: self, action: #selector(singleClickGesture(_:)))
singleClickGesture.numberOfClicksRequired = 1 // single
singleClickGesture.delegate = self
textView.addGestureRecognizer(singleClickGesture)
// create attributed string
let myAttrString = NSMutableAttributedString(string: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.")
// Write it to the Text View
textView.textStorage?.append(myAttrString)
}
@objc func singleClickGesture(_ recognizer: NSClickGestureRecognizer) {
// Show cursor and set it to position on tapping
if recognizer.state == .ended {
textView.isEditable = true
textView.window?.makeFirstResponder(textView)
// Comment the next 9 lines to see the problem.
// TextView is not responding correctly to mouse clicks.
// The next lines fixes the problem for UIKit (iOS),
// but I couldn't make equivalent code for Cocoa (macOS).
let location = recognizer.location(in: textView)
if let position = textView.closestPosition(to: location) {
let uiTextRange = textView.textRange(from: position, to: position)
if let start = uiTextRange?.start, let end = uiTextRange?.end {
let loc = textView.offset(from: textView.beginningOfDocument, to: position)
let length = textView.offset(from: start, to: end)
textView.selectedRange = NSMakeRange(loc, length)
}
}
}
}
}
Solutions?
Upvotes: 2
Views: 670
Reputation: 15633
Here you go:
@objc func singleClickGesture(_ recognizer: NSClickGestureRecognizer) {
// Show cursor and set it to position on tapping
print("click \(String(describing: NSApp.currentEvent))")
if recognizer.state == .ended {
textView.isEditable = true
if let window = textView.window, window.makeFirstResponder(textView) {
let location = recognizer.location(in: textView)
let position = textView.characterIndexForInsertion(at: location)
textView.selectedRange = NSMakeRange(position, 0)
}
}
}
Upvotes: 1