NSTextField doubleValue localization confusion - objective-c

I am from germany. So I have a little problem with localization and the property doubleValue of the class NSTextField. Comma and points are different from the U.S.
double a = self.heightTextField.doubleValue;
If I have a "11,11" in my textfield I only get the integer part unless I write "11.11" with a point. So I guess I have to convert this value according to my current location. No big deal. NSNumberFormatter will do that.
But if I assign a value to the property it all works fine automatically. "11.11" gets converted into "11,11" on the NSTextField without me doing anything.
self.heightTextField.doubleValue = 11.11;
Why is that so?
Why is the the value from the controller converted automatically when send to the view but when I read a value from the view I have to convert it myself? Can I get automatic conversion both ways?

The number handling in NSTextField is from the old, old days, and is being gently deprecated in favor of NSFormatters, so it’s not surprising it’s kind of spotty.
As #Michael says, if you want to handle different localizations of you should add an NSNumberFormatter to your field. You can just drag one off of the Interface Builder’s classes palette and onto the field in question.

Related

Appending *simple* ASCII strings to an NSScrollView

Is there a simple way to append a string to an NSScrollView's NSTextView? I don't want attributes. I simply have error messages coming in as NSString's, and I wish to append each to the window. Nothing fancy. No formatting beyond CR, LF and perhaps TAB if I have too many beers and decide to get over the top fancy.
Every path I follow through the Class docs seems to lead down into a self referential blackhole... Like NSMutableAttributedStrings... which aren't really NSStrings, and don't even have a cString method.
I have been considering just keeping my own NSString and complete rewriting the contents of the scroll view after appending the errorstring the easy way. But that seems... inefficient... when the numbers of reports could get quite large.
Every string needs some attributes to be drawn to the screen — a font/size/colour at the very least is mandatory.
It's not entirely obvious from the docs, but the "proper" way to manipulate an NSTextView is by manipulating the NSTextStorage directly. Also, NSTextStorage is a subclass of NSMutableAttributedString.
You can add characters to the string without dealing with attributes, it will simply copy the attributes from the string around where you add the text to:
[textView.textStorage replaceCharactersInRange:NSMakeRange(textView.textStorage.length, 0) withString:#"\nhello world"];
You will probably also want to scroll down:
[textView scrollRangeToVisible:NSMakeRange(textView.storage.length, 0)];
Performance will be good, even up to gigabytes of data. NSTextView is very efficient, especially when only small changes to the content are made.

NSTextField accessibility - How to provide alternate text for voiceover

It will probably be obvious, but I have never done any work with NSAccessibility before so I'm assuming what I'm asking is something simple I've overlooking.
I have an NSTextField displaying a duration like this, 15:39. This text field is a subview of an NSTableCellView in a view-based NSTableView.
When VoiceOver is enabled, it currently reads off, "one five, three nine", which seems completely useless. Instead, I want it to say "Duration is 15 minutes, 39 seconds."
I can produce the desired string, but I cannot figure out which accessibility attributes I have to set to make this happen.
I've tried setting the accessibility description in IB, which has no effect (whether setting it on the NSTextField or NSTextFieldCell).
I've also tried overriding accessibilityValue: and accessibilityAttributeValue:forParameter: in order to provide custom attribute values for:
NSAccessibilityNumberOfCharactersAttribute
NSAccessibilityStringForRangeParameterizedAttribute
NSAccessibilityAttributedStringForRangeParameterizedAttribute
NSAccessibilityStringForRangeParameterizedAttribute.
This seemed to be the right track since that does allow me to replace what is read aloud by voice over, however, providing any NSRange for NSAccessibilityVisibleCharacterRangeAttribute that doesn't match the length of the "15:39" string causes voice over to completely skip this field when reading off the NSTableCellView's contents. So, the best I've been able to do is get Voice over to say "Durat" instead of reading off "15:39" :(
Everything I've tried, I've tried on NSTextField and NSTextFieldCell.
Ideally, I'd prefer to do what I'd do in iOS and just set the accessibilityLabel of the NSTableCellView, but I see no reasonable way of doing this in AppKit. Hopefully I'm just missing something.
I was able to achieve this simply by setting accessibilityValueDescription on the NSTextField. This method is part of the new Accessibility API on OS X 10.10 and higher. With the older API, you may be able to use kAXValueDescriptionAttribute to achieve the same thing.
The solution for overriding the text read by Voice Over was much simpler than I thought. All I had to do was override the value returned for NSAccessibilityAttributedStringForRangeParameterizedAttribute:
// The displayed text for this text field is "45m".
// The voice over for this field incorrectly reads "forty five meters" instead of "forty five minutes".
// The following forces it to read "Duration forty five minutes"
-(id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
return ([attribute isEqualToString:NSAccessibilityAttributedStringForRangeParameterizedAttribute])
? [[NSAttributedString alloc] initWithString:#"Duration: 45min"];
: [super accessibilityAttributeValue:attribute forParameter:parameter];
}
I believe I've answered my own question, at least enough to control what's read off when clicking a table cell view.
By overriding accessibilityIsIgnored to return "NO" in my NSTableCellView sub-class I was able to specify exactly what I wanted to be read off for the table cell by overriding the NSAccessibilityTitleAttribute for the cell. I had not tried this before b/c I had misunderstood the purpose of the accessibilityIsIgnored selector.
From the documentation for accessibilityIsIgnored:
When asking for an object’s children, ignored children should not be included; instead, the ignored children should be replaced by their own unignored children.
I'd like to be able to control exactly what is read off for the individual NSTextFields in the future, but controlling what is read off for an entire NSTableCellView is actually ideal for my particular situation.

How do I share NSNumberFormatter over multiple NIB files?

I'm working on an NSPersistentDocument based app that has a per-document currency setting.
Preferably, I would like to use a single NSNumberFormatter currencyFormatter for all my NIB files, but there doesn't seem to be any easy way to bind, say, NSTextField formatter to document.currencyFormatter or something similar.
I'm pretty sure it is possible to do this using code, adding bindings manually, but it is quite a bit of work since I have many fields using the currency formatter.
What is the best option to share the currency formatter across multiple NIB files?
Here I suggest two ways:
Subclass the NSNumberFormatter and make it be a singleton (the init method return the same object every time is called).Then drag an object in every xib file and set it's class to the name of the NSNumberFormatter subclass;
Just use the bindings inspector to bind each formatter to document.currencyFormatter.
PS: I'm not sure if I completely understand what you're asking, tell me if one of these would be a valid solution to you.

How do I access the properties of UITextField's selectedTextRange UITextPosition objects?

I'm trying to establish the start and end positions of a text selection of a UITextField instance, using its selectedTextRange property (as gained from the UITextInput protocol). However, I have no idea how to access the properties of the UITextPosition objects that make up the start and end properties of selectedTextRange.
Apple's docs on UITextPosition are woeful at this time, providing no methods or properties, though I know there are such properties in the object, because NSLogging one gives this:
<UITextPositionImpl: 0x6aaeb60>
<<WebVisiblePosition: 0x6aa40e0>(offset=5, context=([s|a], [u+0073|u+0061])>
In this example, the 'offset' is correct, and the context shows the characters either side of the selection point ('s' and 'a'), but I don't know how to access this nebulous WebVisiblePosition class. So, in short, is there a way of retrieving the details I want using UITextPosition objects from UITextField?
Of course, just after asking my question I found the answer, in this SO question: UITextPosition in UITextField.
It seems that when used as part of UITextField, the UITextPosition objects are not meant to be tinkered with directly, but used to feed other methods. In this case, the method offsetFromPosition:toPosition:, along with the text field property beginningOfDocument, can be used to return an NSInteger of a selection index.

Interface builder bindings only working in one direction?

I've got an XIB window that I'm working with in Interface Builder. It has an NSScroller and 4 popups. The controller class has a float and 4 ints.
I bound the scroller to the float and the pupups to the ints, binding the value of the scroller and the selected index of the popups.
When I move the scroller, or change the popup selection, the ints and float change exactly as expected.
However, when I change the ints and float in the code (To initialize them for example) the UI elements don't change.
All of the ints and float are properties, properly synthesized.
Am I missing something really dumb?
There could be one of several things wrong.
First, by "binding" do you mean that you are using Cocoa bindings? If so, do you have an Object Controller in the NIB file that arbitrates between your UI elements and your data holding code? That is generally required.
When you say "when I change the ints and floats in code", how are you changing them? If you are setting the instance variables directly, you are bypassing any change monitoring mechanisms.
Post some code.