I added a NSNotificationCenter to a UIView, when I first go to the page, the NSNotificationCenter work fine.
However, when I left that page and back to that page again, it will give out error
'NSInvalidArgumentException', reason: '-[UITextMagnifierTimeWeightedPoint updateProfile:]: unrecognized selector sent to instance.
Here are the code.
UIView1 :
- (void)changeUIView {
UIView2 *view = [[UIView2 alloc] init];
// show UIView2
}
UIView2 :
- (id)init {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateProfile:) name:#"updateProfile" object:nil];
return self;
}
-(void)updateProfile:(NSNotification *)notification {
// do something
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:#"updateProfile"];
}
- (void)buttonClick {
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateProfile" object:nil userInfo:nil];
}
You need to remove self as the observer not the selector you are using to handle the notification
[[NSNotificationCenter defaultCenter] removeObserver:self];
Or if you want to be specific you can use
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"updateProfile" object:nil];
Always
Add NSNotificationCenter in viewDidAppear
And
Remove NSNotificationCenter in viewDidDisAppear
Related
This is my code.
Here create the observer to Notification called Example into ViewController
- (void)addObserverExample
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(example:)
name:#"Example"
object:nil];
}
- (void)example:(NSNotification *)notification{
NSLog(#"Example!!!");
}
From viewDidLoad register my observer
- (void)viewDidLoad
{
[self addObserverExample];
}
In my second ViewController. When tapped a button excute this code:
[[NSNotificationCenter defaultCenter] postNotificationName:#"Example" object:self.dictKeys userInfo:nil];
The problem I have is that the notification is never executed.
Any idea.
Have created demo for NSNotificationCenter as per your question and it's working fine for me. Here it is the link of that code: NSNotificationCenter Demo
- (void)viewDidLoad {
[super viewDidLoad];
[self addObserverExample];
}
- (void)addObserverExample
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(example:)
name:#"Example"
object:nil];
}
- (void)example:(NSNotification *)notification{
NSLog(#"Example!!!");
NSLog(#"%#",notification.userInfo);
}
- (IBAction)btnFireNotification:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:#"Example" object:nil userInfo:#{#"key" : #"value"}];
}
I believe the problem you're having may be related to the fact that in your second view controller, you're passing self.dictKeys in the object parameter.
If you want to pass data via the NSNotificationCenter, you should use the userInfo parameter instead.
Darshan's example does this the correct way.
In my custom UITableViewCell I added an observer in NSNotificationCenter:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopActivityIndicator) name:#"stopActivityIndicator" object:nil];
I post a notification in a UIViewController:
[[NSNotificationCenter defaultCenter] postNotificationName:#"stopActivityIndicator" object:nil];
This function "stopActivityIndicator" is not being called, any idea what causes this?
EDIT:
ExploreViewController.m
-(void)showCorrectBannerAfterPlusButtonClicked:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"stopActivityIndicator" object:nil];
}
ExploreViewController contains a UITableView with ExploreTableViewCells.
ExploreTableViewCell.m
- (IBAction)plusButtonClicked:(id)sender
{
self.plusButton.hidden = YES;
[self.plusButtonActivityIndicator startAnimating];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(stopActivityIndicator) name:#"stopActivityIndicator" object:nil];
}
-(void)stopActivityIndicator
{
_plusButton.hidden = NO;
[self.plusButtonActivityIndicator stopAnimating];
[[NSNotificationCenter defaultCenter]removeObserver:self name:#"stopActivityIndicator" object:nil];
}
Ok, question, when do you call "showCorrectBannerAfterPlusButtonClicked"?? As this is the method where you post the notification observed by the tableViewCell.
What I see is the notification observer adding and removal, but I don't see how the UIViewController knows when the cell's "plusButtonClicked" is called, so perhaps the method posting the notification is not being called.
Also, be careful with the cell reusage, have in mind if you should remove the observer at that point.
NSNotification observers are added when awakeFromNib is called in my UITableViewCell. Then, I am removing the observers when removeFromSuperView is called.
- (void)awakeFromNib
{
[super awakeFromNib];
[self setNotificationObserver];
_vHolder.layer.cornerRadius = 10.0f;
_vHolder.layer.shadowColor = [UIColor blackColor].CGColor;
_vHolder.layer.shadowRadius = 2.0f;
_vHolder.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
_vHolder.layer.shadowOpacity = 0.5f;
}
- (void)removeFromSuperview
{
[super removeFromSuperview];
[self removeNotificationObserver];
}
- (void)setNotificationObserver
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveESSMQTTMessageNotification:) name:NOTIF_ESSMQTT_MESSAGE_RECEIVED object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveDeviceStatesMessageNotification:) name:NOTIF_DEVICE_STATES_RECEIVED object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didReceiveDeviceOnOffStateNotification:) name:NOTIF_DEVICE_ON_OFF_STATE_RECEIVED object:nil];
}
- (void)removeNotificationObserver
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NOTIF_ESSMQTT_MESSAGE_RECEIVED object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NOTIF_DEVICE_STATES_RECEIVED object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NOTIF_DEVICE_ON_OFF_STATE_RECEIVED object:nil];
}
I am using NSNotification to refresh states of my buttons and images within this UITableViewCell.
The problem I am facing is, every time an NSNotication is received, the awakeFromNib is called. This will cause the states of my buttons and images to refresh back to its initial states. The strange thing is, I never saw removeFromSuperview getting called before that.
So my questions are:
Why is awakeFromNib getting called when NSNotification is received?
I am just wondering, is adding observer under awakeFromNib the correct thing to do when you want your UITableViewCells to observe NSNotifications? (Well, I've been doing this all the time.)
Because your cell was refreshed content but did not be remove from superview
You had to call removeNotificationObserver when content of cell was refreshed successfully.
I suggest you using protocol instead of notification.
Hope help.
My code:
-(void)viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleKeyboard:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
-(void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)handleKeyboard:(NSNotification*)notification {
NSLog(#"triggered");
}
See:
The disappearing handler is triggered once as normal, but 3 times when appears. Is this a iOS bug?
Possibly not helpful for your problem but you need to be calling [super viewWill… for your overrides.
-(void)viewWillAppear:(BOOL)animated
-(void)viewWillDisappear:(BOOL)animated
From the docs.
If you override this method, you must call super at some point in
your implementation.
I'm registering three observers in most of my view controllers. Some have more, some less but I want to include part of the registration and unregistration process in a parent class. Is there any problem with calling the unregistering even if there is no observer? And is one call to unregister enough for all three observers?
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillEnterBackground:)
name:UIApplicationWillResignActiveNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
//Has to be unregistered always, otherwise nav controllers down the line will call this method
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Yes, that will remove all registrations where the observer is self. It's documented in the NSNotificationCenter Class Reference:
The following example illustrates how to unregister someObserver for all notifications for which it had previously registered:
[[NSNotificationCenter defaultCenter] removeObserver:someObserver];
Note that in theory (but not, as far as I know, in practice as of iOS 7.0), UIViewController could have its own registrations that it doesn't want removed in viewWillDisappear:. It's unlikely to register for any of the notifications in the public API using addObserver:selector:name:object:, because that would preclude you registering for them in your UIViewController subclass, but it could certainly register for non-public notifications now or in a future version.
A safe way to deregister is to send removeObserver:name:object: once for each registration:
- (void)deregisterForKeyboardNotifications {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[center removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self deregisterForKeyboardNotifications];
}
- (void)dealloc {
[self deregisterForKeyboardNotifications];
}
Another way is to use addObserverForName:object:queue:usingBlock: to register (instead of addObserver:selector:name:object:). This returns a new observer object reference for each registration. You have to save these away (perhaps in an NSArray instance variable if you don't want to create individual instance variables). Then you pass each one to removeObserver: to deregister its notification. Example:
#implementation MyViewController {
NSMutableArray *observers;
}
- (void)registerForKeyboardNotifications {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
__weak MyViewController *me = self;
observers = [NSMutableArray array];
[observers addObject:[center addObserverForName:UIKeyboardWillShowNotification
object:nil queue:queue usingBlock:^(NSNotification *note) {
[me keyboardWillShow:note];
}]];
[observers addObject:[center addObserverForName:UIKeyboardWillHideNotification
object:nil queue:queue usingBlock:^(NSNotification *note) {
[me keyboardWillHide:note];
}]];
[observers addObject:[center addObserverForName:UIApplicationWillResignActiveNotification
object:nil queue:queue usingBlock:^(NSNotification *note) {
[me applicationWillResignActive:note];
}]];
}
- (void)deregisterForKeyboardNotifications {
for (id observer in observers) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
observers = nil;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self deregisterForKeyboardNotifications];
}
- (void)dealloc {
[self deregisterForKeyboardNotifications];
}
Since every observer returned by addObserverForName:object:queue:usingBlock: is a new object that has only one registration, each call to removeObserver: is guaranteed to only remove that observer's one registration.
Update for iOS 9 / macOS 10.11 and later
As of iOS 9 and macOS 10.11, NSNotificationCenter automatically deregisters an observer if the observer is deallocated. It is no longer necessary to deregister yourself manually in your dealloc method (or deinit in Swift) if your deployment target is iOS 9 or later or macOS 10.11 or later.
For your first Question unregistering even when there is no observer is OK.
But for the way you're removing the observer, [[NSNotificationCenter defaultCenter] removeObserver:someObserver]; will remove even the super class observers which is highly unrecommended (except in dealloc because the object is unloaded) but in viewWillDisappear you should remove the observers one by one by using [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];