NSNotificationCenter addObserver in subclass - objective-c

I register for being notified in superclass (UIViewController) like so:
SuperClass.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notification:)
name:#"Notification"
object:nil];
}
- (void)notification:(NSNotification *)notification {
// Do something for SuperClass with the notification
}
Now In subclass (subclass of SuperClass.m) I also listen for the same notification like so:
SubClass.m
- (void)notification:(NSNotification *)notification {
// Do something specific for SubClass with the notification
}
Is this an acceptable (codewise) way to deal with having a general behaviour when acting on a notification in a superclass and to having a more specific behaviour when acting on a notification in a subclass?

Usually when you want to allow more specific behavior in the subclass, while still maintaining the general behavior in the superclass, you have the subclass call super. For example, the -[UIViewController viewDidAppear:] documentation says:
You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.
So your notification setup is fine (although it's a bit weird to have a NSNotification object as a parameter to a method you expect to be overridden) — but you'll want to call [super notification:notification] to get the superclass's behavior as well.

Related

objective-c should I remove observing NSWindowWillCloseNotification?

I am watching my mac app's preferences window status with the following code in - (void)awakeFromNib
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(windowWillClose:)
name:NSWindowWillCloseNotification
object:self.prefsWindow];
And the method windowWillClose: is the following
- (void)windowWillClose:(NSNotification *)notification {
NSLog(#"Received Notification!");
[[self.customOptField window] makeFirstResponder:nil];
}
I'd like to continue to watch preferences window status while my app is working. i.e. Every time preferences window would be closed, I'd like to call windowWillClose: method.
Currently my code seems to work fine. But I wonder that should I remove observe NSWindowWillCloseNotification? If should be removed, when should I?
Any advice would be appreciated!!
EVERY notification you register, should be removed in the opposite call.
for awakeFromNib which is only called once after init, the opposite is dealloc. So remove it there!
- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(windowWillClose:)
name: NSWindowWillCloseNotification
object: self.prefsWindow];
}
...
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver: self
name: NSWindowWillCloseNotification
object: self.prefsWindow ];
}
You need to remove the observer before you relinquish your last strong reference to the specific window that you're observing, if you ever do, and before the observer object itself is deallocated.
In the code you showed, the observer is self and the window you're observing is self.prefsWindow. Assuming that prefsWindow is a strong property, do you ever clear it to nil or assign a new window to it? If so, you need to remove the observer before doing so. And you would presumably want to start observing the new window, too.
And, of course, you have to remove the observer in self's -dealloc method (as Daij-Djan says), for two reasons: 1) that object is the observer, and 2) the object will be releasing its last strong reference to the observed window.

textDidChange not called ( NSTextFieldDelegate )

Step 1. Add a NSTextField in xib
Step 2. Add NSTextFieldDelegate in .h file,Control-drag NSTextField to File's Owner to set delegate to it
Step 3, In .m file add the method:
- (void)textDidChange:(NSNotification *)notification{
NSLog(#"textDidChange");
}
but the method textDidChange: not called?
Is any mistake?
The file's owner isn't the app delegate -- is the app delegate where you put that method? You should control drag to the blue cube labeled app delegate.
After Edit: The message that the delegate receives is controlTextDidChange: not textDidChange, so implement that one instead.
You need to register an observer to listen for the NSNotification.
// When the NSWindow is displayed, register the observer.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(controlTextDidChange:) name:NSControlTextDidChangeNotification object:nil];
- (void)controlTextDidChange:(NSNotification *)obj {
// You can get the NSTextField, which is calling the method, through the userInfo dictionary.
NSTextField *textField = [[obj userInfo] valueForKey:#"NSFieldEditor"];
}
It seems, the object returned by NSFieldEditor is a NSTextView, instead of the same NSTextField object, which you may expect.
However, according to Apples documentation, if you implement this method and the controls delegate is registered to this object, the notification shall be automatically registered.
The control posts a NSControlTextDidChangeNotification notification, and if the control’s delegate implements this method, it is automatically registered to receive the notification
Source: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSControl_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/controlTextDidChange:

Setting AVAudioPlayer Delegate to multiple classes?

Quick question: Is it possible to set the delegate of an AVAudioPlayer instance to more than one class?
In my program, I want two classes to be notified when a sound has been finished playing, but I am unsure of how to do that.
Any help would be appreciated.
Well it is something not possible. but you can do it by registering and notifying notification. Here is code for registering and notifying classes in objective c.
So you can set one class a delegate and there you can notify other classes
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"delegate called");
[[NSNotificationCenter defaultCenter]
postNotificationName:AUDIO_PLAYER_FINISHED_PLAYING object:nil];
}
No, a delegate is always just one instance.
You could make a new class that is the delegate and make it send an NSNotification to notify the other two classes about the event.

Execute something on application startup?

I have a class in my application which handles all the controls and all the functions and variables are stored in it. How can I add a function which handles the application startup to it?
So basically I need to handle 'applicationDidFinishLaunching' in my class as well as in the application delegate.
How do I do that?
NSApplication sends the NSApplicationDidFinishLaunchingNotification notification, so you should just be able to register for that in your class:
- (void)awakeFromNib
{
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(appDidLaunch:)
name:NSApplicationDidFinishLaunchingNotification
object:nil];
}
- (void)appDidLaunch:(NSNotification*)notification
{
NSLog(#"Blast off!");
}
There's a general pattern here, in that Cocoa classes that have delegate methods with a method signature that passes a single notification parameter, such as the ‑(void)applicationDidFinishLaunching:(NSNotification*)notification delegate method of NSApplication, will also post a notification when the delegate method is called.
For example, NSWindow has a lot of delegate methods with this kind of signature, such as:
- (void)windowDidResize:(NSNotification *)notification
If you look at the docs for this method, you'll see that the notification that is passed to this delegate method is a NSWindowDidResizeNotification. You can then find more detail about this notification in the notifications section of the NSWindow docs.
This type of delegate method is often used when there is a likelihood that more than one object will be interested in the delegate information.

NSTextFieldCell Delegate?

I have a text field cell in a table view, from which I need to be made aware when it ends editing. I thought I would set my Controller class as the text field cell's delegate, and then use NSTextField's delegate method textDidEndEditing:, but realized that the text field cell doesn't seem to have delegate methods? Why is this, and what can I do (other than subclassing) to be informed when editing is finished?
Thanks
NSTextFieldCell inherits from NSCell (well, technically from NSActionCell which inherits from NSCell). The NSCell class is used to (from the docs):
The NSCell class provides a mechanism for displaying text or images in an NSView object without the overhead of a full NSView subclass.
Notably, The cell class is used for "displaying text or images", and not dealing with interaction with the user. Similarly, with the NSTextField class:
The NSTextField class uses the NSTextFieldCell class to implement its user interface.
The NSTextField deals with the actual user input, whilst using the text field cell to simply implement its user interface, and similarly, the delegate methods to provide notification when the editing of text has ended is provided through the NSTextField class and not through the NSTextFieldCell class.
If you want to be notified of when editing ends in an NSTableView, then you need to register yourself as an observer of the NSTextDidEndEditingNotification (you might want to read the NSNotificationCenter class reference if you are unfamiliar with notifications). To do this, place the following in your controller class; the awakeFromNib function is a good place to include it to ensure that it is called upon your application's startup:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(textDidEndEditing:)
name:NSTextDidEndEditingNotification
object:tableView];
Where tableView is the pointer to your NSTableView object. Then, simply implement the method as follows:
- (void)textDidEndEditing:(NSNotification *)aNotification
{
// Do what you want here
}
Don't forget to remove yourself as an observer upon deallocation:
- (void)dealloc
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];
}
The reason that you set the object that you are observing to be the NSTableView instance (and not the cell itself) is that under the hood, when you edit a cell in the table, the cell that you are dealing with isn't being edited directly; it is the window's (or a custom) field editor. When editing ends, the field editor then passes the new value for that cell on to the table view. However the table view will post a notification to say that a cell has finished being edited.
Implement the tableView:setObjectValue:forTableColumn:row: method in the NSTableViewDataSource protocol. Put it next to the tableView:objectValueForTableColumn:row: method that you've already implemented.
- (void)tableView:(NSTableView *)aTableView
setObjectValue:(id)anObject
forTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex
{
[mutableArrayWithStrings replaceObjectAtIndex:rowIndex withObject:anObject];
}