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
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 this really unexplained problem with NSNotification method.
I have been using NSNotificationCenter for a long time but i can't explain why this is happening.
My problem is this,
I have a UITableViewCell subclass where i send a NSNotificationCenter method to the UIViewController when a user taps a button in the cell.
[[NSNotificationCenter defaultCenter] postNotificationName:MOVE_TO_PROGRAM_VIEW
object:self
userInfo:#{INDEX_ROW : [NSNumber numberWithInteger:self.tag]}];
Where the self.tag is the row (for the data model in the controller).
In the controller i register for the notification in viewWillAppear: like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(userWantsToGoToProgramView:) name:MOVE_TO_PROGRAM_VIEW object:nil];
I also remove myself in the viewWillDisappear:
[[NSNotificationCenter defaultCenter] removeObserver:self name:MOVE_TO_PROGRAM_VIEW object:nil];
Now in the method for the notification i try to get the userInfo and the row but the notificaiton argument is nil for some reason..
- (void)userWantsToGoToProgramView:(NSNotification *)notification
{
// notification is nil here
// get the index of the video in the feed
NSDictionary *userInfo = notification.userInfo;
NSInteger videoIndex = [userInfo[INDEX_ROW] integerValue];
NSDictionary *videoData = self.feed[videoIndex];
}
Any advice of help will be appreciated
Thanks!
How can I pass NSString values from ViewController1 to ViewController2 by using notifications in iOS? I have tried:
ViewController1
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"test" object:nil];
}
- (void) incomingNotification:(NSNotification *)notification{
NSString *theString = [notification object];
NSLog(#"theString value=%#",theString);
}
ViewController2
- (void)viewDidLoad
{
NSString *myString=#"testing";
[[NSNotificationCenter defaultCenter] postNotificationName:#"test" object: myString];
}
It works fine while am going to ViewController2.
But I want to send values from ViewController1 to ViewController2 using these notifications. Is it possible. How?
You can use it by initializing ViewController2 in ViewController1's on button click event..Steps to invoke,
1.Create on method (say like notifRegister) in ViewController2,which initialize notification to receive dictionary value.
2.On button click event init ViewController2 and call method notifRegister
3.Then post notification in ViewController1.
Example:
- (IBAction)navigation:(id)sender {
ViewController1 *vc= [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"ViewController2"];
[vc notifRegister];
[self.navigationController pushViewController:(UIViewController*)vc animated:YES];
NSDictionary *name=[[NSDictionary alloc] initWithObjectsAndKeys:#"Tommy",#"name",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TextChanged" object:name];
}
On ViewController2:
-(void)notifRegister
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadLabel:) name:#"TextChanged"object:nil];
}
-(void)reloadLabel:(NSNotification*)sender
{
NSLog(#"reload label called %#",sender.object);
self.labelName=[sender.object objectForKey:#"name"];
}
Here self.labelName is an NSString object...
I think it is not necessary to call a invisible viewController to send notification. You can pass your values as below:
If you use storyboard, you can get ViewController2 in ViewController1's method prepareForSegue, so you can pass your reference here.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var targetViewController = segue.destinationViewController as UIViewController
}
If your manually create the second ViewController and use presentViewController, just pass your reference when initializing your ViewController2.
If you insist to use notification. Set view2 as a notification sender and view1 as a receiver with another notification.
You get an NSNotification object passed to your function.
Put your strings or other objects in the dictionary
NSDictionary* userInfo = #{#"completionHandler" : completionHandler,
#"myStringIdentifier" : myString};
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"myNotification" object:self userInfo:userInfo];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:) name:#"UIKeyboardWillHideNotification" object:nil];
-(void) receiveNotification:(NSNotification*)notification
{
if ([notification.name isEqualToString:#"MyNotification"])
{
NSDictionary* userInfo = notification.userInfo;
... do dictionary stuff
}
}
This is a question which has been asked before search for userinfo and NSNotification for more examples
As per the doc, yes it's possible. But you should read the doc first (see NSNotificationCenter class reference).
The method you should look at is
- (void)postNotificationName:(NSString *)notificationName
object:(id)notificationSender
userInfo:(NSDictionary *)userInfo
where
notificationName is the notification you want to post. The notificationSender is the object that want to post the notification and userInfo is the info you want to pass with the notification.
So, if you want to pass a notification from ViewController2 to ViewController1 (or viceversa).
// ViewController1
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"myNotificationName" object:nil];
}
- (void) incomingNotification:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
NSString *theString = userInfo[#"stringKey"]; // Here you retrieve the passed string from ViewController2 to ViewController1
NSLog(#"%#",theString);
}
// ViewController2
// Suppose you have an action set up for a specific button
- (IBAction)buttonTapped {
NSDictionary *userInfo = #{#"stringKey": #"stringPassed"};
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotificationName" object:self userInfo:userInfo];
}
Here I would highlight two main things.
Both controllers should be "valid". In other words they should be allocated in memory and presented somewhere.
When you add an observer you should also remove it.
Update 1
In this case you are passing a string from UIViewController2 to UIViewController1. Just switch the code you are using and you will be able to pass the same string from ViewController1 to ViewController2.
My question: what's your goal?
this will work...
in ViewController 2
[[NSNotificationCenter defaultCenter] postNotificationName:#"test" object:self userInfo:#{#"stringValue": myString}];
in ViewController 1
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"test" object:nil];
}
- (void) incomingNotification:(NSNotification *)notification{
NSString *theString = notification.userInfo[#"stringValue"] ;
NSLog(#"theString value=%#",theString);
}
I am very new to iOS development and I'm struggling to make an app which connects to BLE devices. As I have many view controllers I need to keep the peripheral always connected in all of them.
To achieve this, I implemented all the BLE connection methods in a Singleton. This works just great, I call the connect method from View Controller and the Singleton connects to the peripheral.
Now, the problem is I have a UILabel in my view controller which I would like to update with the connection state (scanning, connecting, connected, disconnected) from the Singleton.
So I tried to get instance from the View Controller and change the label directly like:
MainViewController *controller = [[MainViewController alloc] init];
controller.myLabel.text = #"TEST";
I also instantiated the view controller class like:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle: nil];
MainViewController *controller = (MainViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"MainVC"];
Then I tried to create a method in the main View Controller:
- (void) updateLabel:(NSString *) labelText{
NSLog(#"CALLED IN MAIN");
self.myLabel.text = labelText;
}
And call it from Singleton like:
MainViewController *controller = [[MainViewController alloc] init];
[controller updateLabel:#"TEST"]
Which was called properly (NSLog was shown) but the label was not updated.
I don't really know how to update my View Controller label from the Singleton. Don't know neither if the way I'm trying to do it is the right one or not.
Any advice or help would be much appreciated. Thanks.
----- UPDATE: -----
Thanks to Mundi and Nikita, I got a better way to implement what I need through NSNotification. For all those who need it here is how I do it:
In my View Controller in viewDidLoad I call:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateConnectionLabel:) name:#"connectionLabelNotification" object:nil];
Then in the same class I implement the notification observer method like:
- (void)updateConnectionLabel:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"connectionLabelNotification"]) {
self.connectionLabel.text = notification.object; //The object is a NSString
}
}
Then in my Singleton, when I need I call:
[[NSNotificationCenter defaultCenter] postNotificationName:#"connectionLabelNotification" object:[NSString stringWithFormat:#"CONNECTED"]];
When the View Controller receives the notification from the Singleton it updates the label with the text I add on the notification object (in this case #"CONNECTED").
You need to use NSNotification.
Here is sample code:
in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mySelector:)
name:DeviceStateChanged
object:nil];
in dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:DeviceStateChanged
object:nil];
also add a method in ViewController:
- (void) mySelector:(NSNotification *) notification {
// action performed
}
in Sigleton
- (void) foo {
/// some actions
// device connected
[[NSNotificationCenter defaultCenter] postNotificationName:DeviceStateChanged object:self];
///
}
Recommendation: move notification name to your constants and use constant name. For naming convention look at Apple guidelines
The proper way to do this is via NSNotification. This communication device is meant for exactly this kind of situation. It broadcast a message without caring whether the potential receiver is available.
In your view controllers, you call NSNotificationCenter's addObserver / removeObserver when they appear / disappear. You post the notification via postNotification:.
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];