I am sending a notification from a class inherits from NSObject using NSNotificationCenter.
The notification should be sent to 2 viewController but it's being sent just to one of them.
My Code:
fetchFromParse:
-(void)sendAllStores
{
[[NSNotificationCenter defaultCenter]postNotificationName:#"getStoresArrays" object:nil userInfo:self.storesDict];
}
firstVC.m (working):
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(getStoresArrays:) name:#"getStoresArrays" object:nil];
}
-(void)getStoresArrays:(NSNotification*)notification
{
NSLog(#“Working”); //Working
}
secondVC.m (not working):
-(void)prepareArrays
{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(getStoresArrays:) name:#"getStoresArrays" object:nil];
}
-(void)getStoresArrays:(NSNotification*)notification
{
NSLog(#“Not Working”); //Not working
}
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
secondVC *secVC=[[secondVC alloc] init];
[secVC prepareArrays];
fetchFromParse *fetchFromParseObj=[[fetchFromParse alloc] init];
[fetchFromParseObj getStoresFromParse];
Return YES;
}
Note: Xcode shows me an error message that "firstVC is not registered as an observer".
The firstVC is never used so it cannot add an observer then.
It adds the observer in the viewDidLoad, but if the view controller is never used it cannot load a view and therefore not add an observer.
The logic for your code is not correct. You are posting a notification and have made two classes as observer, so this will lead to unpredictable results. You should have one class as observer for a notification.
From what i see only one notification listener should be called here and that should be your SecondVC because your first view controller is not loaded yet so no observer is registered for first view controller.
Related
I am facing an issue with NSNotificationCenter.
I am not able to send message and receive message using NSNotificationCenter in latest ios 8.4 (XCode 6.4)
Please check the following code:
1) I want to send data using first view controller to another view.
so i have written the following code in first viewcontroller:
When user btn clicked method as following :
- (IBAction)btnClicked:(id)sender
{
[self postNotification];
[self performSegueWithIdentifier:#"asGo" sender:self];
}
-(void)postNotification{
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyNotification" object:self];
}
2) In Second view controller i have added observer in ViewWillApper as following :
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(eventListenerDidReceiveNotification:)
name:#"MyNotification"
object:nil];
}
-(void)eventListenerDidReceiveNotification:(NSNotification*)txt
{
NSLog(#"i got notfication:");
}
so eventListenerDidReceiveNotification is not called while come on view.
But i am not getting above log while i come on second vc with navigation
As others have noted, NSNotificationCenter doesn't work like a post office. It only delivers notifications if someone actually listens to them at the moment they arrived. This is the reason your eventListenerDidReceiveNotification method is not being called: you add an observer in viewWillAppear, which is called after the segue (I assume that you're using segues because of the performSegueWithIdentifier method in your code) is finished, so it's definitely called after postNotification has been called.
So, in order to pass data via NSNotificationCenter you have to add an observer before you post a notification.
The following code is completely useless and unnecessarily overcomplicated, you shouldn't do anything like that, but since you keep insisting on using a scheme like this, here you go:
//Didn't test this code. Didn't even compile it, to be honest, but it should be enough to get the idea.
NSString * const SOUselessNotificationName = #"MyUselessNotification";
#pragma mark - FIRST VC
#interface SOFirstVC : UIViewController
#end
#implementation SOFirstVC
NSString * const SOasGoSegueIdentifer = #"asGo";
- (IBAction)btnClicked:(id)sender {
[self performSegueWithIdentifier:SOasGoSegueIdentifer sender:self];
}
-(void)postNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:SOUselessNotificationName object:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifer isEqualToString:SOasGoSegueIdentifer]) {
SOSecondVC *destinationVC = (SOSecondVC *)segue.destinationViewController;
[destinationVC registerToReceiveNotificationsFromObject:self];
[self postNotification];
}
}
#end
#pragma mark - SECOND VC
#interface SOSecondVC : UIViewController
-(void)registerToReceiveNotificationsFromObject:(id)object;
#end
#implementation SOSecondVC
-(void)registerToReceiveNotificationsFromObject:(id)object {
[[NSNotificationCenter defaultCenter] addObserver:self selector:(eventListenerDidReceiveUselessNotification:) name:SOUselessNotificationName object:object];
}
-(void)eventListenerDidReceiveUselessNotification:(NSNotification*)uselessNotification {
NSLog(#"I got a useless notfication! Yay!");
}
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
NSNotificationCenter basically has 3 steps
Adding Observer like [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(open:) name:#"OpenDetail" object:nil];
Posting Notification [[NSNotificationCenter defaultCenter] postNotificationName:#"OpenDetail" object:self];
Removing Observer [[NSNotificationCenter defaultCenter] removeObserver:self name:#"OpenDetail" object:nil];
I think you are posting your notification and then later adding observer while it's vie versa. You have to add observer first then post notification.
HTH
First you have to setup the data you want to send
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:myObject forKey:#"aKey"];
Then you post it with the data like so:
[[NSNotificationCenter defaultCenter] postNotificationName: #"MyNotification" object:nil userInfo:userInfo];
And finally you read the data off the notification:
-(void)eventListenerDidReceiveNotification:(NSNotification*)notification
{
NSLog(#"i got notification:");
NSDictionary *userInfo = notification.userInfo;
NSString *myObject = [userInfo objectForKey:#"aKey"];
}
I have in my app a UITableview Controller, a View Controller and I'm trying to pass NSDictionary from UITableview Controller to my ViewController, using NSNotificationCenter. So, I push a notification at my UITableview Controller and then I add an observer ,using a selector at my ViewController.The selector is called,but I have an NSLog and get memory results ,like :
ViewController: 0x8a0bcc0
I have tried to pass NSString instead of NSDictionary , but I get again memory results , and not the value of the string.
My code :
UITableView Controller
NSString *string=#"This is a test string";
[[NSNotificationCenter defaultCenter] postNotificationName: #"Update" object: string];
ViewController
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"Update" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self];
And here is the incomingNotification selector method:
-(void) incomingNotification : (NSNotification *)notification{
NSLog(#"Trying to print : %#",[notification object]);
}
All Notifications take place at ViewDidLoad method.Thank you!
UPDATE
Finally , I quit using NSNotificationCenter and used properties to pass data ,changing a bit the inheretence from my TableViewController. No idea why Notifications did not work ,as they were supposed to. Thank you all ,very much for your suggestions and ideas :)
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self]
Object means the object that generates a notification. To post parameters use another method
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self userInfo:string]
If I understand correctly, UIViewController is shown after you tap a button on UITableViewController. And you if you are adding a ViewController as observer in its -viewDidLoad:, then it will be able to receive notifications only when it is loaded.
What do you need:
1) override -init or -initWithNibName: method of ViewController like this:
-(id) init
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"Update" object:nil];
}
return self;
}
so you can be sure ViewController is observing for notifications from the beginning (well, this might be unnecessary step for your case)
2) when you push ViewController you need to send a notification after it was created, like this:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ViewController *nextController = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:nextController animated:YES];
NSString *string=#"This is a test string";
[[NSNotificationCenter defaultCenter] postNotificationName: #"Update" object: string];
}
However, if you're trying just to send some parameters from one view controller to another, this is the wrong way. Just create a property in ViewController and in method -tableView:didSelectRowAtIndex: of UITableViewController set this property
I am using push notifications in my code and whenever a notification comes, I want to update the value of a label in another ViewController.
My code in AppDelegate is:
- (void)addMessageFromRemoteNotification:(NSDictionary*)userInfo updateUI:(BOOL)updateUI
{
NSLog(#"Notification arrived");
//[mydeals setCode1_id:mydeals.code1_id withString:#"123456"];
mydeals=[[MyDealsViewController alloc]init];
NSDictionary* codeDetails=[[NSDictionary alloc] initWithObjectsAndKeys:#"123456",#"Code_id", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CodeArrived" object:self userInfo:codeDetails];
}
then in my other view controller I have this code:
#implementation MyDealsViewController
-(id) init
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveCode:)
name:#"CodeArrived"
object:nil];
return self;
}
-(void) receiveCode:(NSNotification*)notification
{
NSLog(#"received Code: %#",notification.userInfo);
self.code1_id.text=[NSString stringWithFormat:#"%#",[[notification userInfo] valueForKey:#"Code_id"]];
}
the log is printed correctly but when I manually go into that screen I see the default value, like the label is not updated at all. What should I do?
You have to make sure that when you "manually go" to MyDealsViewController, whatever how you do it, it got to be the same instance of MyDealsViewController wich has been called receiveCode. Otherwise it's going to init with it's default values.
You might also try calling [self.code1_id setNeedsLayout];
I have a View Controller in which my value is 0 (label) and when I open that View Controller from another ViewController I have set viewDidAppear to set value 20 on label. It works fine but when I close my app and than again I open my app but the value doesn't change because viewDidLoad, viewDidAppear and viewWillAppear nothing get called. How can I call when I open my app. Do I have to do anything from applicationDidBecomeActive?
Curious about the exact sequence of events, I instrumented an app as follows: (#Zohaib, you can use the NSNotificationCenter code below to answer your question).
// AppDelegate.m
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"app will enter foreground");
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"app did become active");
}
// ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"view did load");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
}
- (void)appDidBecomeActive:(NSNotification *)notification {
NSLog(#"did become active notification");
}
- (void)appWillEnterForeground:(NSNotification *)notification {
NSLog(#"will enter foreground notification");
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"view will appear");
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"view did appear");
}
At launch, the output looks like this:
2013-04-07 09:31:06.505 myapp[15459:11303] view did load
2013-04-07 09:31:06.507 myapp[15459:11303] view will appear
2013-04-07 09:31:06.511 myapp[15459:11303] app did become active
2013-04-07 09:31:06.512 myapp[15459:11303] did become active notification
2013-04-07 09:31:06.517 myapp[15459:11303] view did appear
Enter the background then reenter the foreground:
2013-04-07 09:32:05.923 myapp[15459:11303] app will enter foreground
2013-04-07 09:32:05.924 myapp[15459:11303] will enter foreground notification
2013-04-07 09:32:05.925 myapp[15459:11303] app did become active
2013-04-07 09:32:05.926 myapp[15459:11303] did become active notification
Using Objective-C
You should register a UIApplicationWillEnterForegroundNotification in your ViewController's viewDidLoad method and whenever app comes back from background you can do whatever you want to do in the method registered for notification. ViewController's viewWillAppear or viewDidAppear won't be called when app comes back from background to foreground.
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doYourStuff)
name:UIApplicationWillEnterForegroundNotification object:nil];
}
-(void)doYourStuff{
// do whatever you want to do when app comes back from background.
}
Don't forget to unregister the notification you are registered for.
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Note if you register your viewController for UIApplicationDidBecomeActiveNotification then your method would be called every time your app becomes active, It is not recommended to register viewController for this notification .
Using Swift
For adding observer you can use the following code
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: "doYourStuff", name: UIApplication.willEnterForegroundNotification, object: nil)
}
func doYourStuff(){
// your code
}
To remove observer you can use deinit function of swift.
deinit {
NotificationCenter.default.removeObserver(self)
}
Swift 3.0 ++ version
In your viewDidLoad, register at notification center to listen to this opened from background action
NotificationCenter.default.addObserver(self, selector:#selector(doSomething), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
Then add this function and perform needed action
func doSomething(){
//...
}
Finally add this function to clean up the notification observer when your view controller is destroyed.
deinit {
NotificationCenter.default.removeObserver(self)
}
Swift 4.2. version
Register with the NotificationCenter in viewDidLoad to be notified when the app returns from background
NotificationCenter.default.addObserver(self, selector: #selector(doSomething), name: UIApplication.willEnterForegroundNotification, object: nil)
Implement the method that should be called.
#objc private func doSomething() {
// Do whatever you want, for example update your view.
}
You can remove the observer once the ViewController is destroyed. This is only required below iOS9 and macOS 10.11
deinit {
NotificationCenter.default.removeObserver(self)
}
Just have your view controller register for the UIApplicationWillEnterForegroundNotification notification and react accordingly.
I think registering for the UIApplicationWillEnterForegroundNotification is risky as you may end up with more than one controller reacting to that notification. Nothing garanties that these controllers are still visible when the notification is received.
Here is what I do: I force call viewDidAppear on the active controller directly from the App's delegate didBecomeActive method:
Add the code below to - (void)applicationDidBecomeActive:(UIApplication *)application
UIViewController *activeController = window.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
activeController = [(UINavigationController*)window.rootViewController topViewController];
}
[activeController viewDidAppear:NO];
try adding this in AppDelegate applicationWillEnterForeground.
func applicationWillEnterForeground(_ application: UIApplication) {
// makes viewWillAppear run
self.window?.rootViewController?.beginAppearanceTransition(true, animated: false)
self.window?.rootViewController?.endAppearanceTransition()
}
As per Apple's documentation:
(void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated;
Description:
Tells a child controller its appearance is about to change.
If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: directly.
(void)endAppearanceTransition;
Description:
Tells a child controller its appearance has changed.
If you are implementing a custom container controller, use this method to tell the child that the view transition is complete.
Sample code:
(void)applicationDidEnterBackground:(UIApplication *)application
{
[self.window.rootViewController beginAppearanceTransition: NO animated: NO]; // I commented this line
[self.window.rootViewController endAppearanceTransition]; // I commented this line
}
Question: How I fixed?
Ans: I found this piece of lines in application. This lines made my app not recieving any ViewWillAppear notification's. When I commented these lines it's working fine.
I want to show a full page image Ad every time a UIViewController is shown.
I think I have to call the method inside a viewDidAppear or ViewWillAppear, but they are being called once.
- (void) viewDidAppear:(BOOL)animated{
[self showAds];
}
- (void) showAds{
//Do Something
}
What should I do to call a method every time a uiviewcontroller is shown( even if its already created)?
ViewWillAppear will be called every time a UIViewController is shown,but won't be called when the app is back to foreground.
you can use Notification to achieve your goal by following code,
This scenario is specially when your app is in background and user press HOME button to active it.
Register for Notifcation when your application enterForground in viewDidLoad only.
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(handleEnteredBackground)
name: UIApplicationDidBecomeActiveNotification
object: nil];
write a method to invoke when application enterForground
-(void)handleEnteredBackground
{
NSLog(#"%s",__FUNCTION__);
// Your stuff here
}
Dont forget to Remove Observer in viewDidUnload method
[[NSNotificationCenter defaultCenter] removeObserver:self];
Post New Notification everytime your application enterForground
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification object:nil];
}
ViewWillAppear should be called every time. Use:
- (void) viewWillAppear:(BOOL)animated{
[self showAds];
}