How can I make an NSTextField that has focus redraw itself? - objective-c

I've subclassed and defined my own custom NSFormatter and NSTextField classes in order properly display SMPTE time code values (HH:MM:SS:FF). When the user selects to use drop-frame for the fps value, the format changes to HH:MM:SS;FF (semi-colon at the end). I have this all working well except for one final hurdle.
When changing the format, the NSTextField with the focus does not update it's display until it's focus is lost. Here is a screenshot to illustrate:
I have tried sending the NSTextField a setNeedsDisplay message when the format is changed, as well as sending it a display message. Nothing seems to work so far, and the text field with focus refuses to update/redraw itself until it loses focus.
Any suggestions of what I need to do to make this happen?

I did manage to solve this problem now. It may be a bit of a hack, but basically what I ended up doing was retrieving the NSTextField's field editor with currentEditor and sending it as an object with the posted notification for when the format changes.
With the field editor I then just had it replaceCharactersInRange:withString: to change the last ":" to ";" and vice-versa. Though it may not be optimal, it works perfectly.
In fact, I've posted the solution to github for anyone else who might find this useful.
https://github.com/cfloisand/smpteformatter

Related

How can I get "Select All" working in my subclassed NSTextField/NSTextFieldCell?

Generally, when editing text in a text field/input/area/editor, pressing ⌘A will select all of the current text in said field/input/area/editor. I've subclassed NSTextField and NSTextFieldCell, and no matter what I try, I can't seem to get basic "Select All" functionality working. I've tried implementing delegate protocols, intercepting events, manipulating commands made by selectors, and every other thing I can think of. What gives? (I can already hear the "Ever heard of Google?" refrains because of how simple this probably is, but I haven't found a single answer out there. I guess I can thank iOS for that.)
And before I forget to mention it, I also dragged a standard NSTextField into my nib to see if a non-subclassed NSTextField implements Select All behavior by default, and to my shock, it doesn't. Am I going crazy here, or am I completely overlooking something? Isn't Select All almost a requirement when implementing a text field? Apple's First Responder proxy handles everything under the sun (including two versions of selectAll (selectAll and selectAll:), but the n00b is strong with me, and I can't seem to make sense of any of this.
Any help/ideas would be immensely appreciated. Cheers!
The application menu handles sending the keyboard shortcut actions to the application's current first responder. The missing connection would explain why your regular NSTextField objects are missing this functionality as well.

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 to avoid extra calling of -textFieldDidEndEditing in some cases in objective c?

I have a tableview with customcells with textfields in it. I am facing a peculiar problem now:
When I tap on first row textfield, -beginEditing gets called.
Now I change the value and tap on second row textfield. So, the -didEndEditing of first row gets called. In this didEnd, I have some parsing methods which are called in some other class. But they are not executed now. Right after the didEnd, -beginEditing of second row text is called. After that the parsing happens. Till now, it is fine.
When the parsing is finished, objects from parsing is set in other classes,the flow should stop here, but I don't know from where and why, The -didEndEditing for the second row gets called ! Also, though any resignfirstresponder is not written anywhere, the keyboard gets dismissed !
Any clue why is this happening and how to solve it ?
This is the way Apple designed the system - all developers have to deal with it (right or wrong). The key is that you are given the "textField" property so you know WHICH one of the textFields is sending the delegate messages.
The solution is to use one or more mutable dictionaries (or some data structure) to keep state for each individual textField. You can have a primary dictionary that uses the textField object as the key, then for each textField a dictionary that has the current state, and any other info you want to retain about it.
You can probably hack a less elegant but easier to code solution to. In any case, there is overlap on these messages and there is not way to avoid it.
EDIT: use the tag as the key, or create a non-retained NSValue pointer object, but don't use the text field itself.

How to get highlighted text from my NSTextField?

It must be certainly easy but I haven't found anything in the doc.
The behavior I need is exactly as when you right-click any portion of text and then you can do some action with it.
For the moment I have a my own custom NSTextField class which re-implment 'mouseDown' action. This part works ! I thought I could get the selected portion of text of my nstextfield thanks to 'theEvent' but apparently it is not possible.
I don't actually think you can do this with NSTextField. I think you have to use an NSTextView instead and then make use of the -selectedRanges method.
EDIT: I should have said, you can't do this directly (i.e., there's no NSTextField method for doing this). I think rather, you have to use the field editor (which is itself an NSTextView) associated with the window in order to do this. Here's the apple guide for using the field editor.
NSString *selectedText = ((NSTextFieldCell*)textField.selectedCell).stringValue;

Why does the execution order of touchesBegan, target-action and touchesEnded change with fast touches of UIButton?

UPDATE: With the blush of shame I discovered that the order had nothing to do with the speed of tapping. I was calling the visual code before the super touchesEnded:withEvent call, which was why if you tapped really fast, the display never got a chance to draw the highlighted state before being dismissed again. Because the code that was actually causing the main thread to block just a few milliseconds, the highlighted state would stay visible until the main thread unblocked again, where as if you tapped really fast, it looked like nothing happened at all. Moving the super call up to the top of the overridden method fixed it all. Sorry, if any moderator sees this post it can be deleted. shame
This problem must have been asked a 1000 times at SO, yet I can't find the explanation to match my specific issue.
I have a UIButton subclass with a custom design. Of course the design is custom enough that I can't just use the regular setSomething:forControlState: methods. I need a different backgroundcolor on touch, for one, and some icons that need to flash.
To implement these view changes, I (counter-intuitively) put the display code in (A) touchesBegan:withEvent and (Z) touchesEnded:withEvent:, before calling their respective super methods. Feels weird, but it works as intended, or so it seemed at first.
After implementing addTarget:action:forControlEvents was used to bind the UIControlEventTouchUpInside to the method (X) itemTapped:, I would expect these methods to always fire in the order (A)(X)(Z). However, if you tap the screen real fast (or the mouse in simulator), they fire in the order (A)(Z)(X). Where (A) and (Z) follow each other in such rapid succession, that the whole visual feedback for tapping is invisible. This is unwanted behavior. This also can't be the way to go, for so many apps need similar behavior, right?
So my question to you is: What am I doing wrong? One thing I'm guessing is that the visual appearance of the buttons shouldn't be manipulated in the touchesBegan:withEvent and touchesEnded:withEvent, but then where? Or am I missing some other well known fact?
Thanks for the nudge,
Eric-Paul.
I don't know why the order is different, but here's 2 suggestions to help deal with it.
What visual changes are you making to the button? If it's things like changing title/image/background image, you can do all this by modifying the highlighted state of the button. You can set a few properties like title and background image per-state. When the user's finger is down on the button, the highlighted state is turned on, so any changes you make to this state will be visible at this time. Do note that if you're making use of the selected state on the button, then you'll need to also set up the visual appearance for UIControlStateHighlighted|UIControlStateSelected, otherwise it will default back to inheriting from Normal when both highlighted & selected are on.
The other suggestion is to ditch touchesBegan:withEvent: and touchesEnded:withEvent: and switch over to using the methods inherited from UIControl, namely beginTrackingWithTouch:withEvent: and endTrackingWithTouch:withEvent:. You may also want to implement continueTrackingWithTouch:withEvent: and use the touchInside property to turn off your visual tweaks if the touch leaves the control.