Custom NSCursor for ToolbarItem - macos

After reading several questions on the subject here, I have successfully used "custom" cursors for textfields (labels), window contentViews, and buttons using a couple of approaches but I can't make either work for a toolbarItem. Here's the current code:
CustomToolbarItem.h:
#import <Cocoa/Cocoa.h>
#interface CustomToolbarItem : NSToolbarItem
#end
CustomToolbarItem.m:
#import "CustomToolbarItem.h"
#implementation CustomToolbarItem
- (void) awakeFromNib {
// setup trackingarea for cursor control (mouseEntered/mouseExited)
NSTrackingArea *trackingArea= [[NSTrackingArea alloc]
initWithRect:self.view.bounds
options: (NSTrackingInVisibleRect |
NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways)
owner:self
userInfo:nil];
[self.view addTrackingArea:trackingArea];
}
- (void)mouseEntered:(NSEvent *)theEvent {
[[NSCursor pointingHandCursor] push];
}
- (void)mouseExited:(NSEvent *)theEvent {
[[NSCursor pointingHandCursor] pop];
}
#end
I've also tried setting owner: to self.view, but no joy.
The alternate approach that works for textfields, buttons, etc. that support the drawRect method:
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
}
- (void)resetCursorRects {
[self addCursorRect:self.bounds
cursor:NSCursor.pointingHandCursor];
}
I can't directly modify/address the ToolbarItem's view in XCode's interface builder so I'm stumbling in the dark here.
Any help would be greatly appreciated.
Thanks

Related

NSContainerView with multiple child view controllers in Xcode 6

I'm trying to link two different view controllers in an Xcode 6 storyboard with a NSContainerView so that they can be switched conditionally. Unfortunately this tutorial here isn't of any help since things seem to have changed since in Xcode.
So I have two different view controllers and one of them is loaded into the container view by default but I want to be able to load the second view controller into the container view programmatically. Xcode 6 only allows to create embed Segues when I drag from one to the other so that's not much of a help.
Can somebody tell me how this is achieved with Xcode 6?
First, here's a sample GitHub project of the solution: click. I wasn't sure whether you wanted to swap the views or simply push the 2nd view onto a proverbial stack, so I went with a push/pop scheme. If you want to swap the views instead, you should be able to do that fairly easily by just skipping the stack storage.
Essentially, we have our "host" NSViewController that holds a Container View (CV) inside of it. This host doesn't actually manually manage the view controller that the CV is showing at the moment. The way this is done is through, well, a sort of nested view controller that then manages all the other view controllers that you're going to show/hide/push/pop/swap/etc. (Note: you might be able to remove the layering a bit, but in iOS terms, I'm treating the 'Sub View Controller Manager' in the storyboard screenshot sort of like a UINavigationController).
We also take advantage of some custom segues/segue animators in order to be able to do more work in the storyboard.
You just have to tell the content view manager view controller to manipulate its subviews in such a way that the old views that you want to pop "back" to are retained (in this case, using an NSMutableArray) and such that the new views have the right frame or have their constraints set up properly.
Here is a screenshot of the storyboard: Each segue you see on the storyboard of a custom type (looks like this -> { } ) is of type SegueBetweenEmbedded in the sample project. Buttons that push perform a segue, and buttons labeled 'Pop' perform dismissController: on the NSViewController (so that was done in the storyboard).
Here's some code (and there's a lot of it, so I suggest looking at the sample project instead):
ViewController.h
#import <Cocoa/Cocoa.h>
#import "ContentManagerViewController.h"
#class ContentManagerViewController;
#protocol ContentManagerViewControllerHolder <NSObject>
-(ContentManagerViewController*)retreiveContentManagerController;
#end
#interface ViewController : NSViewController <ContentManagerViewControllerHolder>
#end
ViewController.m
#import "ViewController.h"
#import "ContentManagerViewController.h"
#import "BackForwardViewController.h"
#interface ViewController ()
#property ContentManagerViewController *vcController;
-(IBAction)pushViewController:(id)sender;
-(IBAction)popViewController:(id)sender;
-(IBAction)popToRootViewController:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
if ([[segue destinationController] class] == [ContentManagerViewController class]) {
self.vcController = segue.destinationController;
}
}
-(ContentManagerViewController*)retreiveContentManagerController {
return self.vcController;
}
-(IBAction)pushViewController:(id)sender {
// note: this works, but then pop is broken via dismissController: since it wasn't done with a segue.
// Better way is to rig up a manual segue and execute the segue.
//BackForwardViewController *viewController = [[NSStoryboard storyboardWithName:#"Main" bundle:nil] instantiateControllerWithIdentifier:#"BackForwardStoryboardID"];
//[self.vcController push:viewController];
[self performSegueWithIdentifier:#"CustomSegueToBackForward" sender:self];
}
-(IBAction)popViewController:(id)sender {
[self.vcController pop];
}
-(IBAction)popToRootViewController:(id)sender {
[self.vcController popToRoot];
}
#end
SegueBetweenEmbedded.h
#import <Cocoa/Cocoa.h>
#interface SegueBetweenEmbedded : NSStoryboardSegue
#end
SegueBetweenEmbedded.m (sorry not sorry for the nested class)
#import "SegueBetweenEmbedded.h"
#import "ContentManagerViewController.h"
#import "ViewController.h"
#interface SegueAnimator : NSObject <NSViewControllerPresentationAnimator>
- (void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController;
- (void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController;
#end
#implementation SegueAnimator
- (void)animatePresentationOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController *parent = [fromViewController parentViewController];
if (parent && [parent class] == [ContentManagerViewController class]) {
ContentManagerViewController *manager = (ContentManagerViewController*)parent;
[manager push:viewController];
}
else if ([fromViewController conformsToProtocol:#protocol(ContentManagerViewControllerHolder)]) {
id<ContentManagerViewControllerHolder> holder = (id<ContentManagerViewControllerHolder>)fromViewController;
[[holder retreiveContentManagerController] push:viewController];
}
}
- (void)animateDismissalOfViewController:(NSViewController *)viewController fromViewController:(NSViewController *)fromViewController {
NSViewController *parent = [viewController parentViewController];
if ([parent class] == [ContentManagerViewController class]) {
ContentManagerViewController *manager = (ContentManagerViewController*)parent;
[manager pop];
}
}
#end
#implementation SegueBetweenEmbedded
- (void)perform {
SegueAnimator *animator = [[SegueAnimator alloc] init];
[self.sourceController presentViewController:self.destinationController
animator:(id<NSViewControllerPresentationAnimator>)animator];
}
#end
ContentManagerViewController.h
#import <Cocoa/Cocoa.h>
#interface ContentManagerViewController : NSViewController
-(void)push:(NSViewController*)viewController;
-(void)pop;
-(void)popToRoot;
#end
ContentManagerViewController.m
#import "ContentManagerViewController.h"
#import "BackForwardViewController.h"
#interface ContentManagerViewController ()
#property (weak) IBOutlet NSView *subViewControllerManager;
#property NSViewController *currentViewController;
#property NSMutableArray<NSViewController*> *viewControllerStack;
#end
#implementation ContentManagerViewController
-(instancetype)init {
self = [super init];
self.viewControllerStack = [NSMutableArray array];
return self;
}
-(instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
self.viewControllerStack = [NSMutableArray array];
return self;
}
-(instancetype)initWithNibName:(NSNibName)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
self.viewControllerStack = [NSMutableArray array];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)showViewController:(NSViewController*)viewController {
[self addChildViewController:viewController];
viewController.view.frame = self.currentViewController.view.frame;
[self.view addSubview:viewController.view];
self.currentViewController = viewController;
}
-(void)removeCurrentViewControllerFromView {
[self.currentViewController.view removeFromSuperview];
[self.currentViewController removeFromParentViewController];
}
-(void)push:(NSViewController*)viewController {
[self removeCurrentViewControllerFromView];
[self.viewControllerStack addObject:viewController];
[self showViewController:viewController];
}
-(void)pop {
if (self.viewControllerStack.count > 1) {
[self removeCurrentViewControllerFromView];
[self.viewControllerStack removeLastObject];
NSViewController *viewController = [self.viewControllerStack lastObject];
[self showViewController:viewController];
}
}
-(void)popToRoot {
while (self.viewControllerStack.count > 1) {
[self pop];
}
}
-(void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender {
// this will be called on the initial embed to set up the first view controller
self.currentViewController = segue.destinationController;
[self.viewControllerStack addObject:segue.destinationController];
}
#end
BackForwardViewController.h
#import <Cocoa/Cocoa.h>
#interface BackForwardViewController : NSViewController
#end
BackForwardViewController.m
#import "BackForwardViewController.h"
#interface BackForwardViewController ()
#end
#implementation BackForwardViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
#end

xcode 6 Beta - strange warning for ViewPort delegates

One of the example projects in XCode 5 was named "Utility Application". It had a main view and an alternate view and set up an Info button to flip the main view to the alternate view. It used a viewcontroller delegate and a protocol definition to switch back from the flip view to the main view. In XCode 6 it is no longer there.
When I run this simple XCode 5 example app in XCode 6, I get a warning in the method below:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAlternate"]) {
[[segue destinationViewController] setDelegate:self]; //<<<< Here is where the warning appears
}
}
The warning is: Sending 'MainViewController *const_strong' to parameter of incompatible type 'id'
What is this? A bug? The app runs fine on both simulator and iOS device. Below is the entire code. Any help will be much appreciated.
**// MainViewController.h
#import "FlipsideViewController.h"
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate>
#end
// MainViewController.m
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Flipside View
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showAlternate"]) {
[[segue destinationViewController] setDelegate:self]; //<<<< Here is where the warning appears
}
}
#end
// FlipsideViewController.h
#import <UIKit/UIKit.h>
#class FlipsideViewController;
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#interface FlipsideViewController : UIViewController
#property (weak, nonatomic) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
// FlipsideViewController.m
#import "FlipsideViewController.h"
#interface FlipsideViewController ()
#end
#implementation FlipsideViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Actions
- (IBAction)done:(id)sender
{
[self.delegate flipsideViewControllerDidFinish:self];
}
#end**
You may have figured this out already...just wanted to post a link for for those stumbling upon this: getting a warning setting up delegate for a custom protocol

NSViewController mouseDown: not called

I have a NSWindowController where I'm adding my corresponding ViewControllers. I would like to handle touch events in one of the ViewController. My class is
Window.h
#interface WindowController : NSWindowController<NSToolbarDelegate>
#property (nonatomic, strong) NSViewController *currentViewController;
#property (assign) IBOutlet NSView *targetView;
#end
Window.m
-(void)addViewController
{
NSViewController *currentController = [[currentControllerClass alloc]initWithNibName:controllerIdentifier bundle:nil];
self.currentViewController = currentController;
[self.targetView addSubview:self.currentViewController.view];
[self.currentViewController.view setFrame: [self.targetView bounds]];
}
-(void) awakeFromNib{
[super awakeFromNib];
[self.window setIgnoresMouseEvents:NO];
[self setToolbarToPracticeView];
}
-(void)mouseDown:(NSEvent *)theEvent
{
NSLog(#"Window > Mouse down");
}
I'm able to print the mousedown event here. Now I have a viewController, which is
ViewController.h
#interface ViewController : NSViewController
#end
ViewController.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
-(void)awakeFromNib
{
[self.view setAcceptsTouchEvents:YES];
}
-(BOOL)acceptsFirstResponder
{
return YES;
}
-(void)mouseDown:(NSEvent *)theEvent
{
NSLog(#"ViewController > mouse down");
}
I would like to get the control of the mouseDown in the ViewController. Am I missing something here?
Thanks.
Create a Custom NSView and then delegate a mouseDown method in it's protocol. Import and use the customView in the ViewController and delegate its methods. When you click on the view a mouseDown event triggers.
Not sure of my answer, but if you don't call [super mouseDown:theEvent] in the Window.m (which should be called WindowController.m by the way), you are breaking the responder chain. Hence your view controller can't see it. Am I right?

How to change NSButton's title when cursor is on it

I'm beginner of Cocoa Programming.
How can I change NSButton's title when cursor is on the button? (without clicking).
If you look at the class hierarchy for NSButton you'll see that it derives from NSResponder which is the class that handles mouse events.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nsbutton_Class/Reference/Reference.html
Create a subclass of NSButton and override the following messages to set the title to what you want:
- (void)mouseEntered:(NSEvent *)theEvent
- (void)mouseExited:(NSEvent *)theEvent
Add this to your initializer (Either awakeFromNib or your init message, depending on your usage):
[self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:YES];
Note that even when the mouse has not actually entered the tracking area the first time the title will display the exited message. You may want to add some state to your class to if you want a third title set before it enters the tracking area the first time.
EDIT: Maybe this will help.
Here is the header file "MyButton.h":
#import <Cocoa/Cocoa.h>
#interface MTButton : NSButton {
NSTrackingRectTag myTrackingRectTag;
}
#end
Pretty standard stuff.
Here is my source file.
#import "myButton.h"
#implementation MTButton
- (void) awakeFromNib
{
[self setTitle:#"Initial"];
myTrackingRectTag = [self addTrackingRect:[self bounds]
owner:self
userData:NULL
assumeInside:YES];
}
- (void) dealloc
{
[super dealloc];
[self removeTrackingRect:myTrackingRectTag];
}
- (void)mouseEntered:(NSEvent *)theEvent
{
[super mouseEntered:theEvent];
[self setTitle:#"Entered"];
}
- (void)mouseExited:(NSEvent *)theEvent
{
[super mouseExited:theEvent];
[self setTitle:#"Exited"];
}
#end

NSTextField not noticing lost focus when pressing Tab?

I can't seem to find a way to get notified when an NSTextField loses focus by pressing the Tab key. I get a nice textDidEndEditing when clicking another control or when pressing Enter, but not if I change the focus by pressing the Tab key.
Also tried to yank KeyDown and doCommandBySelector for this purpose but I got nowhere.
Any ideas?
Thanks in advance
Edit:
Forgot to mention, but I tried resignFirstResponder too. This is the code I tried:
- (BOOL)resignFirstResponder
{
NSRunAlertPanel(#"", #"Lost Focus",#"OK", nil, nil);
return [super resignFirstResponder];
}
- (BOOL)becomeFirstResponder
{
NSRunAlertPanel(#"", #"Got focus",#"OK", nil, nil);
return [super becomeFirstResponder];
}
Strangely, what happens here is that when getting focus, both becomeFirstResponder and resignFirstResponder are called one after the other. But when changing focus away from the control, neither are.
"I get a nice textDidEndEditing when
clicking another control or when
pressing Enter, but not if I change
the focus by pressing the Tab key."
As of April 2011, with OS X 10.6 libs, I'm using:
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
...to listen for NSTextField losing focus, and it's working correctly. Is this possible in your situation? Is it something that used to be broken, but is now fixed by Apple?
If so, it's much less code :).
Ok, I've found a way to do it: use a window delegate to make the window return a custom field editor. This field editor keeps track of the last TextField that's been activated and calls its textDidEndEditting method when losing firstResponder itself. Here's an example of how to do it:
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#interface MyTextField : NSTextField
- (BOOL)resignFirstResponder;
- (void)textDidEndEditing:(NSNotification *)notification;
#end
#interface MyFieldEditor : NSTextView
{
MyTextField * lastBox;
}
-(void) setLastEditBox:(MyTextField*) box;
#end
#interface MyWindowDelegate : NSWindowController
{
MyFieldEditor *fieldEditor;
}
#end
#implementation MyFieldEditor
-(void) setLastEditBox:(MyTextField*) box{ lastBox = box; }
-(id)init
{
if (self = [super init])
[self setFieldEditor:YES];
return self;
}
- (BOOL)resignFirstResponder
{
// Activate the last active editbox editting-end event
if(lastBox != nil)
{
[lastBox textShouldEndEditing:self];
lastBox = nil;
}
return [super resignFirstResponder];
}
#end
#implementation MyWindowDelegate
-(id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
{
if(fieldEditor == nil) // Return our special field editor
fieldEditor = [[[MyFieldEditor alloc] autorelease] init];
return fieldEditor;
}
#end
#implementation MyTextField
- (BOOL)resignFirstResponder
{
// We're losing first responder, inform the field editor that this was the last edit box activated
MyFieldEditor* myTf = (MyFieldEditor*) [[self window] fieldEditor:YES forObject:self];
[myTf setLastEditBox:self];
return [super resignFirstResponder];
}
- (void)textDidEndEditing:(NSNotification *)notification;
{
[super textDidEndEditing:notification];
[self setStringValue:#"RECEIVED ENDEDITING"];
}
#end
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSApplication *app = [NSApplication sharedApplication];
NSRect frame = NSMakeRect(100, 100, 200, 150);
// Create the window
NSWindow* window = [[[NSWindow alloc] autorelease ] initWithContentRect:frame styleMask:NSClosableWindowMask|NSResizableWindowMask
backing:NSBackingStoreBuffered defer:NO];
[window setDelegate:[[MyWindowDelegate alloc] autorelease]];
MyTextField * tf = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 100.0, 150.0, 22.0 ) ];
[ [ window contentView ] addSubview: tf ];
MyTextField * tf2 = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 40.0, 150.0, 22.0 ) ];
[ [ window contentView ] addSubview: tf2 ];
[window makeKeyAndOrderFront: window];
[app run];
[pool release];
return 0;
}
You have to do only this
For key Tab
self.textfield.delegate = self;
and then implement this method
- (void)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
{
NSLog(#"Selector method is (%#)", NSStringFromSelector( commandSelector ) );
if (commandSelector == #selector(insertTab:)) {
//Do something against TAB key
//Or Call a Method
}
}
or see my answer at
Execute an Action when the Enter-Key is pressed in a NSTextField?
With the understanding that I mentioned in my other post, I figured out an answer. It's a little convoluted but it works. You have to subclass both the NSTextField and the NSWindow because you need information from both to set this up. Here's the subclasses:
HMTextField.h
#import <Foundation/Foundation.h>
#interface HMTextField : NSTextField {
}
#end
HMTextField.m
#import "HMTextField.h"
#import "HMWindow.h"
#implementation HMTextField
- (BOOL)becomeFirstResponder {
[(HMWindow*)[self window] setTfBecameFirstResponder:YES];
return [super becomeFirstResponder];
}
#end
HMWindow.h
#import <Foundation/Foundation.h>
#interface HMWindow : NSWindow {
BOOL tfIsFirstResponder, tfBecameFirstResponder;
}
#property (nonatomic, readwrite, assign) BOOL tfBecameFirstResponder;
#end
HMWindow.m
#import "HMWindow.h"
#implementation HMWindow
#synthesize tfBecameFirstResponder;
-(id)init {
if (self = [super init]) {
tfIsFirstResponder = NO;
}
return self;
}
- (NSResponder *)firstResponder {
id fr = [super firstResponder];
if ([fr isEqualTo:[self fieldEditor:NO forObject:nil]]) {
tfIsFirstResponder = YES;
} else {
if (tfIsFirstResponder && tfBecameFirstResponder) {
NSLog(#"the text field stopped being first responder");
tfBecameFirstResponder = NO;
}
tfIsFirstResponder = NO;
}
return fr;
}
#end
Make the classes and make your objects their class. You'll be notified of the first responder change from your text field where the NSLog message is in the HMWindow.m file. If you need help understanding how it works let me know.
Here's an example of how to indicate the appropriate time a custom NSTextFieldCell (NSCell) should draw its own bezel & focus ring (in the method [NSTextFieldCell drawWithFrame: inView]), by 'borrowing' the cell's highlight field, setting it when the text field gains focus, and clearing it when the text field loses focus (editing completes).
This technique overcomes some problems:
The cell can't easily determine if it has focus.
The cell can't easily determine which higher level component (e.g. text field or button) it belongs to to track via its parent
NSTextField can instantaneously resign first responder after gaining it, which could make it seem like it lost user focus when it didn't.
Since we're re-purposing the cell's "highlighted" state field, in order to communicate the focus state to the cell, be sure to return nil from the custom NSTextFieldCell's [highlightColorWithFrame: inView:] method.
#import "CustomTextField.h"
#implementation CustomTextField
-(BOOL)becomeFirstResponder {
((NSTextFieldCell *)self.cell).highlighted = true;
return [super becomeFirstResponder];
}
-(void)textDidEndEditing:(NSNotification *)notification {
((NSTextFieldCell *)self.cell).highlighted = false;
[super textDidEndEditing:notification];
}
#end
Complex answers. There is a simpler way to do it.
Don't forget to subclass your NSTextField to NotificableTextField and set its delegate to your view controller.
NotificableTextField.h:
#import <Cocoa/Cocoa.h>
#protocol NotificableTextFieldDelegate <NSObject>
#optional
- (void)textFieldStartedEditing:(NSTextField *)textField;
- (void)textFieldEndedEditing:(NSTextField *)textField;
#end
#interface NotificableTextField : NSTextField
#end
NotificableTextField.m:
#import "NotificableTextField.h"
#implementation NotificableTextField
- (void)awakeFromNib
{
[super awakeFromNib];
self.target = self;
self.action = #selector(inputEnd);
}
- (BOOL)becomeFirstResponder
{
BOOL status = [super becomeFirstResponder];
if (status && [self.delegate respondsToSelector:#selector(textFieldStartedEditing:)])
[(id<NotificableTextFieldDelegate>)self.delegate textFieldStartedEditing:self];
return status;
}
- (void)inputEnd
{
if ([self.delegate respondsToSelector:#selector(textFieldEndedEditing:)])
[(id<NotificableTextFieldDelegate>)self.delegate textFieldEndedEditing:self];
}
#end
NSTextField is a subclass of NSResponder. NSResponder has a method - (BOOL)resignFirstResponder. That will notify you when the NSTextField is no longer first responder... ie. loses focus. So subclass your NSTextField and do your stuff in there.

Resources