I just spent ages trying to work out how to keep the focus in an NSTableView column after deleting a row, rather than just keeping a selection. I did it like this:
[[myTableView window]makeFirstResponder:myTableView];
Why does the code above work, but the code below doesn't?
[myTableView becomeFirstResponder];
-makeFirstResponder: is a request to the window that it make the specified responder its first responder. -becomeFirstResponder is a notification to a responder that it is about to become the first responder. It doesn't inherently cause a state change; it gives the receiver a chance to react to a state change that was caused by -makeFirstResponder:.
You should not call -becomeFirstResponder (except, possibly, to call through to super in an override). The framework calls it as necessary.
Related
I just started to suspect that calling resignFirstResponder directly is not actually allowed. Unlike NSResponder, it's allowed to call becomeFirstResponder directly in UIKit. So far I was making assumption that calling resignFirstResponder would be okay too. But in fact, the manual says resignFirstResponder method is there to get notified, and mentions nothing about direct calling.
Notifies this object that it has been asked to relinquish its status
as first responder in its window.
If it's designed in same way of how NSResponder works, direct calling to resignFirstResponder wouldn't be allowed though there's no obvious way to figure out validity of the call in the manual...
If it's been designed not to be called directly, directly calling to the method would be harmful or make code harder to maintain.
Is it okay to call UIResponder.resignFirstResponder method directly?
It seems to be okay because on another part of the manual, it says
...
To dismiss the keyboard, you call the resignFirstResponder method of
the text-based view that is currently the first responder. When a text
view resigns its first responder status, it ends its current editing
session, notifies its delegate of that fact, and dismisses the
keyboard. In other words, if you have a variable called myTextField
that points to the UITextField object that is currently the first
responder, dismissing the keyboard is as simple as doing the
following:
[myTextField resignFirstResponder];
Everything from that point on is handled for you automatically by the text object.
Function NSBeginAlertSheet(...) has all the events I need to have especially the didDismiss: callback, but I really need to be able to do the same sheet action with any window I want, so I discovered this notification:
NSWindowDidOrderOffScreenAndFinishAnimatingNotification
Which is posted whenever a sheet is closed AND done with animations
now, my question is can I use that? Or is there a better way?
I use ARC and I load the windows from .xib using NSWindowController.
Overall what I need is to show a window as sheet and catch all events.
What's wrong with
- (void)beginSheet:(NSWindow *)sheet modalForWindow:(NSWindow *)docWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo
This calls the optional didEndSelector which should look like this:
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
This is all in the NSApplication documentation. There are two methods for ending the sheet:
- (void)endSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode
- (void)endSheet:(NSWindow *)sheet
So you could just do whatever you wanted to right before calling endSheet: or you could in the sheetDidEnd: method.
Edit:
Here is an example project showing that after calling [window orderOut:self] then animation is finished and you can do what you'd like.
NSWindowDidEndSheetNotification It is posted whenever a sheet is finished animating out.
Starting with 10.9, the correct way is to call beginSheet:completionHandler: on a NSWindow object.
This method has the advantage, that the completion handler is a block, so it can keep all the objects alive that are required as long as the sheet is still displayed and once the sheet is done and the block has been executed, the block itself is released and thus all objects it was keeping alive are as well.
To make sure a block keeps objects alive, use the objects within that block or if there is no way to use them in a meaningful fashion, put all of them into a NSMutableArray and within the block call removeAllObjects on that array; this requires the block to keep the array alive and the array keeps the rest alive -> memory management made easy.
I manually bind the value of a NSTextView to a property of an object. When I call unbind, the window becomes first responder ... Why?
I don't want that!
Well there isn't a lot of information here, but you might want to check the responder chain to see if the window is next in line and being passed the focus.
So I'm building a program that features the use of the IKImageBrowserView component as a subview in an NSWindow. As a side note, I have a controller object called ImageBrowserController which subclasses NSWindow and is set as the delegate of the NSWindow object of my app.
I have sent IKImageBrowserView the message setCanControlQuickLookPanel:YES to enable it to automatically use the QuickLook functionality to preview image files when the IKImageBrowserView is a first responder to receive key events. Then it took me a while to figure out how to make the IKImageBrowserView a first responder which I finally got working by overriding acceptsFirstResponder inside my ImageBrowserController.
Now I understand that as the delegate to the NSWindow, ImageBrowserController has a place in the responder chain after the event gets triggered on NSWindow. And I understand that as a subview of NSWindow, IKImageBrowserView is in line to be passed events for event handling. What I don't get is where the connection is between the ImageBrowserController being a first responder and the event somehow making it to the IKImageBrowserView. I didn't set NSWindow or IKImageBrowserView as first responders explicitly. So why isn't it necessary for me to implement event handling inside my ImageBrowserController?
EDIT: So after reading the accepted answer and going back to my code I tried removing the acceptsFirstResponder override in my ImageBrowserController and the QuickLook functionality still triggered just like the accepted answer said it would. Commenting out the setCanControlQuickLookPanel:YES made the app beep at me when I tried to invoke QuickLook functionality via the spacebar. I'm getting the feeling that my troubles were caused by user error of XCode in hitting the RUN button instead of the BUILD button after making changes to my code (sigh).
Some of what you are saying regarding the interactions between your objects does not make sense, and it is hard to address your stated question without some background.
As you say, your window delegate has a place at the end of the responder chain, after the window itself. The key point I think you are missing is that GUI elements, such as your IKImageBrowserView, will be at the beginning of the chain, and any one of them in a given window could be the current firstResponder.
When your application gets an event, it passes it off to the key window (which is just the window which currently accepts "key" (i.e., "keystroke") events). That window begins by asking its firstResponder to handle the event. If that object refuses, it passes the event to its own nextResponder, usually its superview, which either handles it or passes it on, until the event has either been handled or passed all the way up to the window object itself. Only then will the window (if it does not handle the event itself) ask its delegate to handle the event.
This means that the connection between the window delegate and the IKImageBrowserView is only through the Responder Chain, and its nature is simply that if the view declines to handle any given event, the delegate may eventually be asked to handle it, if no other object in between them handles it first.
Your window delegate does not need to be a firstResponder. Nor does overriding acceptsFirstResponder on the window delegate have any effect on one of the window's subviews.*
Your window delegate also does not need to (and, indeed should not) be a subclass of NSWindow. All it needs is to be a subclass of NSObject which implements whatever methods from the NSWindowDelegate Protocol you are interested in, and methods to handle any events you might want to catch if they are not handled by other objects.
So, the answer to your explicit question at the end is (and I do not mean this sarcastically): you only need to implement event handling in your window delegate if you want it to handle events itself.
*: IKImageBrowserView already responds YES to acceptsFirstResponder. If there are no other subviews in your window, it will automatically be the firstResponder when your application starts. You can set this initialFirstResponder explicitly in Interface Builder by connecting that outlet on the window to whatever object you want.
I'm implementing a -(void)delete: method so I can handle the delete key in my Cocoa app. I want it to do different things depending on what's selected: for text-fields, I want the default behaviour (remove char to the left), but for NSBrowser items, I want it to delete the item.
I thought I would ask the Window for it's first responder, and then see if that first responder is equal to the pointer for my NSBrowser, but it never matched. When I debug it, I find that the firstResponder is pointing to an instance of NSBrowserTableView, but I can't find that in the documentation.
What is it?
And how else could I test to see if my firstResponder is a particular tableView? (I Thought of subclassing NSBrowser but I tend to avoid subclassing, and my second thought was to add a tag, but I like my first method best, if only the firstResponder would point to my NSBrowser instance when one of the items in the browser is selected. )
Thoughts?
Actually, #trudyscousin is only partially correct. This class is definitely not a subclass of NSBrowser.
NSBrowserTableView is a private subclass of NSTableView used by NSBrowser to display each column. The table view is used so there is a separate place to draw the branch image (the little arrow drawn next to folders) while leaving the rest of the row to be drawn by either the default or user-defined cell.
If you think about it, it actually makes sense that the table view (rather than the browser) be the first responder, because then the table for the active column gets first crack at responding to keystrokes, and NSBrowser can let NSTableView do what it already knows how to. (For example, jumping to the first row that matches a letter typed by the user.)
Fortunately, NSBrowserTableView has a pointer back to the browser it works for. You can access this via its -(NSBrowser*)browser method. I recommend you don't subclass NSBrowser for this particular case, since you'd have to have a deep knowledge of its private implementation to do anything useful.
You can't find that in the documentation because it's private. My guess is that, when you instantiate a NSBrowser or a NSTableView, you're actually instantiating a subclass of this private class, which itself is a subclass of NSControl (which is pointed out in the documentation as being the superclass of both NSBrowser and NSTableView). Another example is NSString represented as 'NSCFString,' which I take as an allusion to the fact that CFString and NSString are "toll-free bridged."
Take this with as many grains of salt as you wish, but the way I'd go about gaining insight into the first responder is inserting a NSLog statement in my code and breaking just beyond it, seeing what was printed in the log. You could set the view's tag and display that in the statement. Or you could ask for your first repsponder's class
NSStringFromClass([myFirstResponder class])
and display that.
Hope this helped.