forwarding a notification to another class - iphone

I am using this method
- (void)keyboardWillShow:(NSNotification *)notification
that is triggered when the keyboard shows.
When this method is triggered, it receives a notification that contains several parameters about the keyboard, as the animation duration, animation curve and frame. I need to forward this notification and all its parameters to another class. So, I've tried to do this inside keyboardWillShow:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"doSomething" object:notification userInfo:nil];
the doSomething notification runs doSomething method on another class, and it has this form:
- (void) doSomething:(NSNotification *)notification {
but when I try to read the notification values on this other class, using, for example,
myRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
I obtain zero for all values. The notification is losing its parameters and not being forwarded. How can I do that?
thanks.

It is losing all the values because you passed nil when you created the new notification! You must pass the keyboard notification's userInfo dictionary if you want this to work. So:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"doSomething" object:self userInfo:userInfo];
In this case, the object creating this notification is this object. If you want this to appear as to come from the keyboard object, then send [notification object] for object, but I don't recommend this. Also, why not simply have the class that you want to respond to these notifications ALSO listen for keyboard notifications? Creating and sending a new notification from within the keyboard notification call-back seems kind of round-about to me. The other option is to create a delegate-style thing where the class (I am assuming a view controller or something?) actually listening for keyboard notifications then calls back a delegate object (that other class that you want to forward this info to) and passes the userInfo dictionary to it directly. So:
// Assume a proper delegate is set and it responds to - (void)doSomething:(NSDictionary*)userInfo
[delegate doSomething:userInfo]; // from within the keyboard notification
Still, I think I would just have this other class listen for keyboard notifications itself, though.

Related

Sending data to a method registered for a notification

I need to pass some data to a method which I am registering to execute once I receive a notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:withCell:) name:UIKeyboardWillShowNotification object:nil];
Here I want to send some data for withCell part of my method.
How can I achieve this?
Short answer: you can't. You're registering for a notification and you can't control what is sent with that notification.
What is it you would like to do?
When you're posting notification and want to pass some data use:
- (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo
but this is system notification in your case, so the best choice for you would be registering callback in your view controller that would be called when keyboard is shown (it receives only one parameter - NSNotification). You'll have to use some ivar (e.g. selectedCell) and process it in that callback.

Call a function (IBAction) in a class with a button in another class (and view)

I have an application where I used a UITabBarController with 3 buttons. So I also have 3 classes. What I want to do is to call an - (IBAction) doSomething: (id) sender {} in class 1 (view 1) with a button in class 2 (view 2).
Take whatever it is that your doSomething method (not function) does and use it to create method in a new class. Both controllers can import the class, instantiate it, and use the method.
Alternately you can send a notification to whichever controller has doSomething, but if the code in the method really does apply to both controllers, provide it to both controllers.
You can have one controller send a notification to another. When you want to notify class 1 to perform the button-pressed code you'll send out a notification like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"ABCPerformButtonAction"
object:nil];
You don't have to call it ABCPerformButtonAction, you just need a string that you'll recognize and something -- I used ABC because I don't know your initials or the name of the app or whatever -- to help ensure you don't accidentally send a notification that has the same name as a notification something you're unaware of is listening for (including 3rd party libraries you're using, etc.).
When that notification goes out, any object that has registered with the defaultCenter to listen for #"ABCPerformButtonAction" will perform any actions you choose. Here's how controller 1 registers (this should be located in some place like ViewDidLoad or the initialization method of the object):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(performDoSomething:)
name:#"ABCPerformButtonAction"
object:nil];
The selector there, performDoSomething:, is just the name of a method you want to run when the notification goes out. That method has to have a specific format, so you can't call your doSomething method directly. It would look like this:
- (void)performDoSomething:(NSNotification *)notif {
[self doSomething];
}
As you can see, all it does is call the method. Obviously it could do much more, and you can even send information along with the notification (see below).
Lastly, it's important that you also remove your object as an observer before it's deallocated. In your Dealloc method of each object that registered to receive the notification you add this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Hopefully that makes sense. The Apple documentation for NSNotificationCenter explains more and they provide several sample apps that use notifications.

NSNotification issue: it's posted but just cached by one instance of the same class, why?

here i am again:
what i want to do is:
if i press a button, then post a notification. This notification should be cached by 2 instances of the same class.
the problem:
the notification is posted, but it is cached just by one instance.
some code and explanation
i have 1 tab bar controller
i have 3 tabs ( 3 different views -xib files-)
2 views references the same (view controller) class (so, there are 2 instances of the same class, let's say class A)
the other tab/view references another class (class B)
if i press a button of one view, a method of class B is fired and, at some point it does this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"update" object:nil ];
in the viewDidLoad method of class A I have this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateAll:) name:#"update" object:nil];
also, i have defined the updateAll function as:
- (void) updateAll: (NSNotification *) notification {
NSLog(#"called");
}
As i said before, just one time the updateAll method is fired.
questions
why?
how to fix it?
thanks for reading!
It is possible that your view is not loaded yet, because you are using tab bar controller. The view that is not yet visible is not loaded, so it is likely that your viewDidLoad will get called only for one instance. I recommend you debug it and make sure your addObserver call is really get executed twice, not once.
This won't work at all. You're posting a notification with a name #"updated" but you've attached observers for name #"update". You should be getting no notifications at all.
The way of posting notification is synchronous. I think another object doesn't register as an observer yet, so it cannot receive the notification posted.
And, if the notification is posted on another thread, it will be obtained by the observer on the same thread.

Standard idiom for property change notifications on iPhone

Lets say I have a simple object that represents the state of a puzzle. The puzzle can be either solved or unsolved. I have two controllers with references to this puzzle object and they are visually representing the state in two different ways - say an on/off switch and a red/green light.
When the 'solved' property of the puzzle changes, the controllers need to update their view to represent the current state. Is there a standard idiom for communication the state change from the puzzle object to the controller?
My initial intent was to declare a custom protocol and track observers in the puzzle object. When the solved property changed, iterate over all the observers and invoke a specific method on the protocol. This seems like a common enough pattern that there might be some built in support but I wasn't able to find exactly what I was looking for in the docs.
While both answers so far have concentrated on the use of NSNotification, and that's totally valid, there's another way to do what you want that's built-in to Cocoa objects: Key-Value Observing, or KVO. It's slightly lighter-weight and a little less "action at a distance". I prefer using it whenever possible for observing changes in my data model classes. YMMV.
If I'm understanding correctly you can use NSNotification to do what you want. In your puzzle class you can use postNotificationName to tell any class that's observing when the puzzle changes state. To register a class as an observer to the puzzle, use the addObserver and removeObserver methods. Here are the definitions for the three methods:
-(void) postNotificationName:(NSString *) aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
-(void) addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
-(void) removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;
Here is some example code to use these for your program:
In your puzzle class, in the function that changes state:
[[NSNotificationCenter defaultCenter] postNotificationName:#"puzzleChangedState" object:self userInfo:NULL]
// if you want to send out moreInfo, like other variables, use userInfo with a dictionary object
In your controllers, or views, etc... wherever you want to get the puzzle changed state message:
//In your constructor or initialization method, register this class with the puzzle class
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handlePuzzleChangedState:) name:#"puzzleChangedState" object:nil];
This will add your controller to the NotificationCenter and when the puzzle class posts a notification of "puzzleChangedState", your controller's handlePuzzleChangedState: method will be invoked.
Here is the handlePuzzleChangedState: function:
-(void) handlePuzzleChangedState:(NSNotification *) notification
{
//handle your puzzle state change here
}
If you want more help here's the docs:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html#//apple_ref/doc/uid/10000043i
Hope it works out!
Instead of a custom protocol, you can use notifications as follows.
In your controller, in viewDidLoad register yourself as an observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(puzzleStateDidChange:)
name:#"PuzzleStateDidChange" object:nil];
then implement
- (void)puzzleStateDidChange:(NSNotification *)notification
{
// set your switch and light according to the state notified
puzzleState = notification.object;
...
}
and finally add
[[NSNotificationCenter defaultCenter] removeObserver:self];
to the dealloc method to unregister yourself as an observer.
Now you are ready to receive notifications and react accordingly. What is missing is the code to be added to your puzzle object. Each time the state changes in the puzzle object,
use something like
[[NSNotificationCenter defaultCenter] postNotificationName:#"PuzzleStateDidChange" object:yourPuzzleState];
to notify your controllers about the state change.

NSNotification center may not respond to -object?

I'm trying to make simple use of the NSNotification center inside my iPhone application, but I seem to be doing something wrong in this case. I was under the impression that it was possible to retrieve an object associated with a particular message, or at least a reference to the object, but using the following example code I'm getting a warning,
"NSNotification center may not respond to -object"
- (void)addNewBookmark:(NSNotificationCenter *)notification {
Bookmark *newBookMark = (Bookmark *)[notification object];
//Do some stuff with the bookmark object
}
Indeed, when I compile and run the code, basically nothing I try to do with the contents of the object actually gets carried out - it's simply ignored.
The post code is as follows,
- (IBAction)save:(id) sender{
//Sending the message with the related object
[[NSNotificationCenter defaultCenter]
postNotificationName:#"addNewBookmark"
object:bookmark];
}
and the bookmark object itself is just a dictionary. I also tried using the "userInfo" argument and passing the bookmark object through that, but the result was the same.
How should I be doing this? What am I doing wrong?
Your addNewBookmark: method should accept an NSNotification, not an NSNotificationCenter.
NSNotification should respond to -object as expected.
A notification center is the object in charge of keeping track of who is listening and sending notifications (not centers) to them.