Reputation: 4239
I have a keyDown
function in my application that is used to capture input from a NSTextView
named textInput
. Some conversions are done with the input which is appended as a NSAttributedString
back into the NSTextView
.
This works fine currently, but the problem I have is that the value entered into the textbox on keyDown
doesn't get added to the textInput.textStorage?.string
, until another key is pressed.
For example if I enter the text abcde
and nothing more into textInput
, and then inside func keyDown()
I try to access textInput.textStorage?.string
, it will return abcd
.
Here is the function without unnecessary parts:
override func keyDown(with event: NSEvent) {
let bottomBox = textInput.textStorage?.string // This returns one character short of what is actually in the text box
if let bottomBox = bottomBox {
var attribute = NSMutableAttributedString(string: bottomBox)
// Do some stuff here with bottomBox and attribute
// Clear and set attributed string
textInput.textStorage?.mutableString.setString("")
textInput.textStorage?.append(attribute)
}
}
If I were to use keyUp
, this isn't a problem, although the problem with keyUp
is that if the user holds down the key, the attributes on the NSAttributedString
don't get set until the user releases the key.
I though maybe there was a way to programatically release the keyDown event during the keyDown
function, or generate a keyUp event, but can't seem to find anything.
Is there a way to fix this?
Upvotes: 0
Views: 266
Reputation: 17040
What I like to do is to use Cocoa Bindings with a property observer. Set up your properties like so:
class MyViewController: NSViewController {
@objc dynamic var textInput: String {
didSet { /* put your handler here */ }
}
// needed because NSTextView only has an "Attributed String" binding
@objc private static let keyPathsForValuesAffectingAttributedTextInput: Set<String> = [
#keyPath(textInput)
]
@objc private var attributedTextInput: NSAttributedString {
get { return NSAttributedString(string: self.textInput) }
set { self.textInput = newValue.string }
}
}
Now bind your text view to attributedTextInput
with the "Continuously Updates Value" check box checked:
Et voilà, your property will be immediately updated every time you type a character, and your property's didSet
will immediately be called.
Upvotes: 1