Make NSFormatter validate NSTextFieldCell continuously - cocoa

In Cocoa, I have an NSOutlineView where the cells are NSTextFieldCell. The cell displays values which are strings that are formatted according to certain rules (such as floats or pairs of floats with a space in between). I have made a custom NSFormatter to validate the text, and this seems to work with no problem.
However, the cell (or the outline view, I'm unsure what is causing this) only seems to use the formatter at the moment my editing would end. If I type some alphabetic characters into the text field (which violates the formatting rules), these characters show up -- the only way I notice the formatter doing its job is that I'm now prevented from moving keyboard focus away from this cell. If I return the contents of the cell to a valid form, then I can move focus away.
I have set both the cell and the outline view to be "continuous".
It would be better if I was unable to enter text into the cell in the first place. Is it possible to make it like that, and if so, how?

Answering my own question because I found the solution. There is an optional method to override on NSFormatter, and this solves the problem. The optional method is:
- (BOOL) isPartialStringValid: (NSString*) partialString
newEditingString: (NSString**) newString
errorDescription: (NSString**) error
Here one can simply return NO if the partialString is invalid. One can return a fixed string by reference in newString if one wants.
There is another method which could also have been used, but it is more complex:
- (BOOL) isPartialStringValid: (NSString**) partialStringPtr
proposedSelectedRange: (NSRangePointer) proposedSelRangePtr
originalString: (NSString*) origString
originalSelectedRange: (NSRange) origSelRange
errorDescription: (NSString**) error

Related

Search for an NSView inside a NIB by string

I'm trying to write an application which should display some data. The data in question comes from a different module in our code (written in C, not ObjC), and for various reasons is identified by a string, not an integer or other form of constant. After the glue code, I have an incoming method on my AppDelegate like so:
-(void)newstringdata:(NSString*)data withLabel:(NSString*)label;
This method should always take the value of data and set it as the text for a particular label in the UI. The problem is, which label.
I could of course create an NSDictionary and fill it at run time with the possible values for the label parameter in the newstringdata:withLabel: method and references to outlets, but this seems somewhat ugly and inefficient; it requires me to maintain the outlets, the nib, and the NSDictionary-initializing code.
Instead, if possible, I would like to set a property in the interface designer somewhere, and then do a lookup in my newstringdata:withLabel: method based on the label which was passed which returns the NSLabel.
Is this possible? If so, how would I do it?
If the value of label will never include a slash (/), backslash (\), or colon (:), you can use the identifier property of NSView.
In the xib, enter each label string as the NSTextViews's identifier (in the Identity tab, 3rd tab from left).
Then in your code, loop through all views and:
if ([aView.identifier isEqualToString:label])
[aView setStringValue:data];

Recognize if user has pressed arrow key while editing NSTextField swift

I have many NSTextFields and I want to know, if the user has pressed one of the arrow keys while editing one of them. The function
override func keyDown(theEvent: NSEvent) {
switch theEvent.character {
case NSRightArrowFunctionKey:
println(1)
moveGor(NSRightArrowFunctionKey)
case NSLeftArrowFunctionKey:
moveGor(NSLeftArrowFunctionKey)
case NSUpArrowFunctionKey:
moveVert(NSUpArrowFunctionKey)
case NSDownArrowFunctionKey:
moveVert(NSDownArrowFunctionKey)
default:
super.keyDown(theEvent)
}
}
doesn't seem to work. Is there any other way to do that in swift?
EDIT:
I have the extension for NSEvent:
extension NSEvent {
var character: Int {
let str = charactersIgnoringModifiers!.utf16
return Int(str[str.startIndex])
}
}
that I used in previous function
When text fields have the focus, they actually don't. Instead, a text view is added to the window on top of the text field and that text view is the first responder and handles all of the input and editing behaviors. The text view is known as the "field editor". The text field does not receive key down events; the text view does.
You could substitute a custom text view as the first responder for the text field and have that text view handle the key down events specially. However, it's probably easier to take advantage of the fact that the text field is the delegate for the text view. Depending on exactly what you're trying to achieve, you might implement -textView:willChangeSelectionFromCharacterRange:toCharacterRange:, but that's not exclusively about arrow keys.
A more promising method might be -textView:doCommandBySelector:. That's also not really about the arrow keys, but in some ways it's better. The arrow keys, and all other standard editing keys, operate by being translated through the key bindings system into command selectors. The command selectors represent the semantic operation being performed, like -moveUp:. They are changed by modifier flags, so that Shift-up-arrow might generate -moveUpAndModifySelection:.
Anyway, in -textView:doCommandBySelector:, you can execute code based on the selector and either tell the text view not to do anything else (by returning YES) or let the text view do its normal thing in addition (by returning NO). (Obviously, return NO for anything that you don't care about.)

How do I post-process a control’s input, and update all attendant controls?

I’m working on a little optical-illusion app. As part of that, I have a model key (a CGFloat) representing an angle.
I have three controls — an NSTextField, an NSStepper, and an NSSlider — each bound to that model key. (The NSTextField was created as a “Text Field with Number Formatter”.)
I want that angle to fall between -45 and 45 degrees at all times. I also want it rounded to the nearest integer.
To that end, I’ve implemented a setAngle method that applies those rules. In addition to rounding its input, it replaces any value falling outside the acceptable range with the closest valid value.
I notice that whenever I use one of the controls to change the angle’s value, the other two controls reflect the post-processing value — but the submitter itself does not.
For instance, if I move the slider, the text field shows the rounded value, not the possibly-fractional value that the slider submitted.
Likewise, if I enter 44.5 in the text field, the slider’s position corresponds to 45.
However, the submitting control still displays the “raw” value it submitted: the text field in the last example continues to read 44.5.
Placing
[self willChangeValueForKey:#"angle"];
and
[self didChangeValueForKey:#"angle"];
around the code in setAngle that actually changes the value had no effect on this.
My principal questions, then, are these:
Is there, generally speaking, a “correct” way to alter a value submitted by a control, such that all controls bound to the key get the updated value? I'm not entirely sure that angleSet is the right place to pull the sort of post-processing shenanigans I am, but I'm even less sure where else I should do so. (The Apple docs regarding validation in key-value coding expressly discourage using validation to this end.)
If there's no general-purpose mechanism for setting a tweaked version of model key, and notifying all associated controls after the fact, is there a way to identify the single control doing the actual setting, and update it with the post-processed value?
Thanks in advance!
use cocoa binding is the most easiest way for this situation.
declare a property
#property CGFloat angle;
and bind it to value of the controls with keypath angle or someobject.angle (it depends on your implementation)

NSTextView / NSScrollView - A few questions to help me understand it's proper usage

I have created a "notes" field designed to hold multiple paragraphs of text which I would like to store in a custom object. Originally, I just used an NSTextField as a temporary solution, but this does not allow me to scroll or have multiple paragraphs of text...
In IB I have placed a NSTextView (which seems to be wrapped inside an NSScrollView.) Upon execution of my program, seems to allow me to enter text in multiple paragraphs, scroll, etc. In short it LOOKS to be exactly what I want would like it to be. So far so good.
Now, I need to retrieve the data from this field and store it in my custom object. This is where I'm getting a bit lost within the developer documentation...
My goals are fairly straight forward:
Allow users to type away in the box.
Store the contents of the box into a variable (array, etc.) in my custom object when the user moves to another field, leaving the notes field.
Display the users stored text in the text box next time the record is viewed.
Second, is there a simple way to retrieve and store the data into a "notes" variable in my custom object (such as an NSString object? I would think having multiple would exclude an NSString object as an option here, but maybe I'm wrong) or am I getting into a more complex area here (such as having to store it in an array of NSString objects, etc.)?
Any help would be appreciated!
You can get the data using -string, defined by NSText (e.g. NSString *savedString = [aTextView string])
Your save code can be put in your NSTextDelegate (read, delegate of the NSTextView, because it's the immediate superclass), in – textDidEndEditing: which will be called, well, when editing is finished (e.g. when the user clicks outside the view) or one of the other methods.
Then to reload the saved string if you emptied the text view or something, use [textView setString:savedString] before editing begins.
NSTextDelegate documentation: here.
I'm not sure what you mena when you say "store the contents of the box into a variable (array, etc.) Are you hoping for an array of custom notes? Text views store a string of data, so the easiest way of storing its value is using one string; if you need an array of notes you'd have to split the string value into different paragraphs, which shouldn't be too hard.

Change cell content in NSTableView before editing

I have an NSTableView that allows inline editing on one of its cells (NSTextFieldCell). This actually works well, but I want to manipulate the content for the editing session.
Imagine having a string with a path (say: "folder/subfolder/myfile.txt") as the content of such a cell. But when the user enters edit mode for this cell (e.g. by double clicking) I want only the string "myfile.txt" to be editable (i.e. to appear in the editor). How can I achieve this?
You could create a custom NSFormatter that does this. Override the method stringForObjectValue: to return the full string and editingStringForObjectValue: to return only the part you want to edit. You also need to write a method getObjectValue:forString:errorDescription: to transform the edited string back to the complete string. How to exactly do this depends on the rest of your program. You somehow need to get back the part of the string you removed for editing.

Resources