In my app in which I'm using storyboards I have mainViewController with MapView inside. At the top of the mainViewController I'm displaying UIContainerView from the bottom of the screen with UITableViewController inside. When user click on row I'm sending notification to the mainViewController in purpose of changing map region with animation. The similar notifications are sending when user is selecting specific tableViewCell and also when user tapping on the DONE button located in navigationViewController. Everything works fine for me except method called in DONE button method.
MAIN VIEW CONTROLLER -(void)viewDidLoad method...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideContainerView:)
name:HideContainerView object:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeRegionForUser:)
name:ChangeRegionForUser
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideContainerView:)
name:ChangeRegionToInitial
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeRegionToFitWearerAnnotations:)
name:ChangeRegionToFitWearerAnnotations
object:nil];
MAIN VIEW CONTROLLER OBSERVERS METHODS:
#pragma mark NSNotificationCenter methods
-(void)changeRegionForUser:(NSNotification*)notification
{
NSLog(#"%#",notification.description);
[self zoomToFitUserLocationWithLatitude:-33.861858 longitude:151.210546 andUserInfo:nil];
}
-(void)hideContainerView:(NSNotification*)notification
{
NSLog(#"%#",notification.description);
[self zoomToFitMapAnnotations:_mainMapView];
[UIView beginAnimations:#"HideContainerAnimation" context:nil];
[UIView setAnimationDuration:0.5];
[_containerView setFrame:CGRectMake(0, 600, 320, 284)];
[UIView commitAnimations];
}
-(void)changeRegionToFitWearerAnnotations:(NSNotification*)notification
{
NSLog(#"%#",notification.description);
[self zoomAnnotationsOnMapView:_mainMapView toFrame:CGRectMake(0, 0, 320, 200) animated:YES];
}
TABLE VIEW CONTROLLER (DID-SELECT-ROW-AT-INDEX-PATH METHOD)//It work properly
[[NSNotificationCenter defaultCenter] postNotificationName:#"ChangeRegionForUser" object:self];
TABLE VIEW CONTROLLER (DONE BUTTON METHOD) //IT DOESN'T WORK
-(void)doneButtonAction:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"HideContainerView" object:self];
}
MY STORYBOARD
IN MAIN VIEW CONTROLLER.H
#interface MainScreenViewController : UIViewController<MKMapViewDelegate>
extern NSString * const HideContainerView;
extern NSString * const ChangeRegionForUser;
extern NSString * const ChangeRegionToInitial;
extern NSString * const ChangeRegionToFitWearerAnnotations;
IN MAIN VIEW CONTROLLER.M
#import "MainScreenViewController.h"
NSString* const HideContainerView = #"HideContainerView";
NSString* const ChangeRegionForUser = #"ChangeRegionForUser";
NSString* const ChangeRegionToInitial = #"ChangeRegionToInitial";
NSString* const ChangeRegionToFitWearerAnnotations =#"ChangeRegionToFitWearerAnnotations";
You are using a string constant HideContainerView for the notification name when you add yourself as observer. But when you post the notification you are using a literal string #"HideContainerView". Maybe you are using two different strings there?
Also, I think that you are using self as the object when you add yourself as an observer. It seems this precludes receiving the notification. From the documentation:
notificationSender
The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer.
If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.
Use nil instead.
That being said, would it not be better to couple these two controllers more tightly. I think that would be a much more appropriate design. You should send notifications when the sender does not know if the receiver exists or not. But in this case, you know that the receiver exists.
Thus, you should implement a simple delegate protocol, retain a reference to the map view controller and tell it directly what to do.
Related
I am trying to pass NSString from one class to another. Let's say I have ViewController A and ViewController B. I want to pass NSString from A to B.
In ViewController A, I have following code :
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationMessageEvent" object:userType];
//Here user type is a string I get using delegate and I need to pass this userType to ViewController B
In ViewController B, I have following code :
In viewDidLoad , I have following code :
[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(notificationAction:) name:#"NotificationMessageEvent" object:nil];
//This NSNotificationCenter method is called
I have registered the following selector method.
-(void) notificationAction:(NSNotification *) notification
{
if ([notification.object isKindOfClass:[NSString class]])
{
NSString *message = [notification object];
// do stuff here with your message data
NSLog(#"%# is message",message);
}
else
{
NSLog(#"Error, object not recognised.");
}
}
//The above selector method is never called.
I have read other similar stackoverflow answers but I have not been able to find any solutions regarding this.
Your code and syntax is clearly correct. I'm guessing that it's a matter of object lifecycle. I would hypothesize that either of the following are true:
ViewController B doesn't actually exist as an object when the notification is posted
or
ViewController A is posting the notification before ViewController B has had a chance to register for it.
One way you can verify either of these is to add two breakpoints, one where the notification is posted, and one where the notification listener is registered. The breakpoint where the listener is registered should be hit before the notification is posted. If that happens, then verify that ViewController B is in fact an object in existence when the notification is posted (like it's not popped off the navigation stack or something).
This is what you are looking for
NSNotification not being sent when postNotificationName: called
You must addObserver before postNotificationName
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(notificationAction:) name:#"NotificationMessageEvent" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationMessageEvent" object:nil];
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 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 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];