Best way to capture key events in NSTextView? - objective-c

I'm slowly learning Objective-C and Cocoa, and the only way I see so far to capture key events in Text Views is to use delegation, but I'm having trouble finding useful documentation and examples on how to implement such a solution. Can anyone point me in the right direction or supply some first-hand help?

Generally, the way you implement it is simply to add the required function to your view's controller, and set its delegate. For example, if you want code to run when the view loads, you just delegate your view to the controller, and implement the awakeFromNib function.
So, to detect a key press in a text view, make sure your controller is the text view's delegate, and then implement this:
- (void)keyUp:(NSEvent *)theEvent
Note that this is an inherited NSResponder method, not a NSTextView method.

Just a tip for syntax highlighting:
Don't highlight the whole text view at once - it's very slow. Also don't highlight the last edited text using -editedRange - it's very slow too if the user pastes a large body of text into the text view.
Instead you need to highlight the visible text which is done like this:
NSRect visibleRect = [[[textView enclosingScrollView] contentView] documentVisibleRect];
NSRange visibleRange = [[textView layoutManager] glyphRangeForBoundingRect:visibleRect inTextContainer:[textView textContainer]];
Then you feed visibleRange to your highlighting code.

It's important to tell us what you're really trying to accomplish — the higher-level goal that you think capturing key events in an NSTextView will address.
For example, when someone asks me how to capture key events in an NSTextField what they really want to know is how to validate input in the field. That's done by setting the field's formatter to an instance of NSFormatter (whether one of the formatters included in Cocoa or a custom one), not by processing keystrokes directly.
So given that example, what are you really trying to accomplish?

I've done some hard digging, and I did find an answer to my own question. I'll get at it below, but thanks to the two fellas who replied. I think that Stack Overflow is a fantastic site already--I hope more Mac developers find their way in once the beta is over--this could be a great resource for other developers looking to transition to the platform.
So, I did, as suggested by Danny, find my answer in delegation. What I didn't understand from Danny's post was that there are a set of delegate-enabled methods in the delegating object, and that the delegate must implement said events. And so for a TextView, I was able to find the method textDidChange, which accomplished what I wanted in an even better way than simply capturing key presses would have done. So if I implement this in my controller:
- (void)textDidChange:(NSNotification *)aNotification;
I can respond to the text being edited. There are, of course, other methods available, and I'm excited to play with them, because I know I'll learn a whole lot as I do. Thanks again, guys.

Related

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.

Speed up Animation for UIPickerView Scrolling

Currently, when selecting components or swiping the UIPickerView, the default is a lengthy animation time waiting for the selection, with a "gravity" effect near values. Is there a simple way to speed up this animation? I've looked at the delegate protocols as well as UIPickerView's methods and properties. Will I have to subclass and overload the animation method? Any help will be useful.
There is no way to do this. If you'd like for there to be a way to do this, please file a bug asking for it.
Also, relying on implementation details and a particular interval view hierarchy, as Fabian suggests, is a really excellent way to introduce a ton of fragility into your application and open the possibility of your app breaking in the future, should UIKit ever change anything.
I don't know of a way to achieve that using public API, but UIPickerView uses a UIPickerTableView as a subview somewhere in its view hierarchy. That is a subclass of UITableView which is a subclass of UIScrollView which has a decelerationRate property.
You shouldn't use private API, though. If you really need this and it's not for an App Store app this might be okay, but you should be careful and code defensively.
I don't have 50 rep, so can't comment on this (which is where this should really go). This question shouldn't have been downvoted since the question is legitimate. The valid answer is "no, you can't do that without private API hacks"), but the question is still valid.

I need to know when a textfield is done being edited

I have a class that contains several textfields. I need to know when one is done being edited and send the new information to one of my other objects.
This question seemed similar but in Objective C? The buttons should be visible when the user is done entering the value in the textfield
Objective-j doesn't have the same functions.
Browsing the documentation I found several functions that seemed like they might be able to help such as, textDidEndEditing but didn't understand how to use it. There are several more that sound related and I don't know what to use.
Summary: when the textfield is done being edited I need to perform another function. Also there are multiple textfields so I need to know which one is edited.
controlTextDidEndEditing is the delegate method you should implement. Or you can just set the target/action of the textfield

-makeFirstResponder: usage

I am fairly new to cocoa programming and I would like to ask if anyone can explain me how to
-(BOOL)makeFirstResponder:(NSResponder *)responder; method works. I was planning on using it for NSEvent but can anyone show me how to implement it?
I am trying to use the NSResponder class to get me a working -keyDown method.
NSResponder is one of the fundamental classes in Cocoa. Any class that can respond to events like key presses or menu commands should be a subclass of NSResponder. Each responder keeps track of it's "next responder", and each window keeps track of the object that's currently the "first responder". When an event happens in a window, a message is sent to the first responder. If that object handles the message, great. If not, it passes it along to its next responder. This is known as the "responder chain."
Normally, you don't mess much with the responder chain in Cocoa. The first responder is mostly determined by user actions, such as clicking on a control.
It doesn't make sense to 'use it for NSEvent'. NSEvent isn't a responder, but something that enables responders to do their job.
If you describe more clearly what you're trying to accomplish, I'm sure we can point you in the right direction.
You don't usually implement -makeFirstReponder:, you call it to set the input focus to a view. What is it that you really want to achieve?
I am trying to use the NSResponder class to get me a working keyDown method.
That doesn't make sense. “Use” a class?
If you want to respond to key events, you normally should do that in a view that should be capable of becoming the first responder (see the NSView docs).
See also the Event-Handling Guide, the View Programming Guide, and the video for session 145 (“Key Event Handling in Cocoa Applications”) from the WWDC 2010 session videos (which you should be able to access through your developer account even if you didn't go to WWDC last year).

Subclassing a NSTextField

I want to expand on the functionality of NSTextField. AMong the things I want to achieve is:
Changing the look and feel of the caret.
Detecting when the text reaches a certain number of characters and then coloring the text after that limit differently. *
To my great frustration after spending quite some time googling I find hundreds of hits that simply state "Sublass NSTextField and use this code.", and to my humiliation I have found myself unable to grok exactly how to do this.
I would be extremely grateful if someone could give me a working example of a subclass that achieves one of the two things I list above, and instructions* on how to implement the code so I can try and figure out how it works by looking at some actual live code.
I am extremely apologetic for my late response!
Apologies to all of you. I have a colic infant at home, and as you (or at least those of you that have children) can imagine this takes up quite a lot of your available time. Thank you all for your responses.
I see that one of my main problems is that I don't have a sufficient understanding of delegates and outlets. I have purchased the book recommended here (and many other places. Some sort of "Bible" I gather) and I'm looking into it as we speak in the few silent hours I have these days. :)
But although I can see it's going to be an indispensable tool for me I still gain the most understanding from studying examples rather than reading the theory* and so I would be extremely grateful if someone would create a project with a proper subclass of the relevant class since I understand that I should probably not be extending the NSTextfield class?
I would instantly mark Mark Thalmans post as the answer as I'm sure it's a proper "for dummies" response, but I'll hold out for a few days since I'd really love a file to peruse. But I am not ungrateful!
Oh, and; Please believe me guys when I say I'm not quite as useless in languages I actually know. It's just that these concepts with the Interface Builder and GUIs connection to the code is very unknown to me. I usually just write the code and keep it at that.
*Yes, my first little training project is indeed a Twitter Utility.
*Like to a child
*Not that reading the theory hasn't got tremendous value for me as well. I wouldn't be where I am without Colin Moock definitive guide to AS3
setInsertionPointColor: will take care of setting the caret color, and using delegate methods would be the best way to color the text after the number of characters change. In general, a lot of classes in Cocoa are like this; you can subclass them, but most of the functionality you need to change are in delegate methods.
NSTextField is special, because it doesn’t actually implement text editing. That’s done by a shared (per-window) NSTextView, known as the field editor. You can provide a special field editor for a given NSTextField. This is canonically done by subclassing NSWindow (!) and overriding -fieldEditor:forObject:. When I was looking this up, though, I found NSTextFieldCell’s -setUpFieldEditorAttributes: method, which looks as though it could return a different field editor than the one it’s handed.
Recommended reading: Control and Cell Programming Topics for Cocoa, Text System Overview.
Martin,
I started with the "New File" dialog and chose "Cocoa" on the left and then Objective-C class.
That will generate the following code, without the comments. Then all you need to do is change the NSObject in the"#interface" line of the header to "NSTextView" and you have a working subclass. If you are using XCode 3.0 you can go to Interface Builder and change class of your NSTextField to "MyTextView".
You should also pick up Aaron Hillegass' book "Cocoa Programming for Mac OS X, Third Edition" It has bee updated for Leopard, if you haven't already.
Good Luck.
#import <Cocoa/Cocoa.h>
#interface MyTextView : NSTextView {
// Outlets & Members go here
}
// Actions & messages go here
#end
#import "MyTextView.h"
#implementation MyTextView
#end
If you really have to subclass it, you have to subclass the NSTextFieldCell. Informations about NSCells are available online.
Don't subclass the cell if not absolutely necessary. Use the delegate methods.
At least the color can be changed using NSTextField's bindings, use those.
You may also be able to get some of the functionality required with a formatter.