NSNotification not returning - ios

I have a modalUIViewController that has a UITableView on it. For whatever cell the user selects, I want to return that text to the previous view controller and dismiss the modal view. I'm using NSNotifications to send the value back. Problem is, my notification is never received.
Here is the code from the 'parent' view:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(choiceReceived:)
name:#"selectionMade"
object:nil];
[self performSegueWithIdentifier: #"locationsDetailsSegue" sender: self];
}
- (void) choiceReceived: (NSNotification *) notification
{
NSLog(#"test");
NSDictionary *dict = [notification userInfo];
NSString *user_choice = [dict objectForKey:#"choice"];
NSLog(#"%#", user_choice);
[[NSNotificationCenter defaultCenter] removeObserver:self
name: #"selectionMade"
object:nil];
}
And in the modal view controller:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *choice = cell.textLabel.text;
// send a notification of this choice back to the 'parent' controller
NSDictionary *dict = [NSDictionary dictionaryWithObject:choice forKey:#"choice"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"selectionMade" object:nil userInfo:dict];
NSLog(#"%#", [dict objectForKey:#"choice"]);
[self dismissViewControllerAnimated:YES completion:nil];
}
I get the correct output from the notifier, but I get no output whatsoever from the receiver. Am I missing something obvious? Thanks!

Well, i don't like use NSNotificationCenter in such scenario (Its just my suggestion). I'm always recommend delegate pattern in such case. Delegation pattern working or communicate one-to-one object notification so it give 100% precise output and removing other conflicts.
Create protocol methods in childviewcontroller and delegate property for confirmation in parentclassviewcontroller.
Consume chileviewcontroller protocol in parentviewcontroller. Implement required delegate methods of protocol in parentviewcontroller class. Also you can send multiple types of arguments through delegates method.
for more info go through this doc.

Related

Wrong object added to UITableView

In my app I get an object by NSNotificationCenter (form another controller) and add the object to UITableView:
-(void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(RosterSave:) name:#"RosterSave" object:nil];
}
-(void)RosterSave:(NSNotification *)notification
{
NewRoster* newRoster = [[NewRoster alloc]init];
newRoster = notification.object;
[myUser.rosterArray addObject:newRoster];
[self.myRoster reloadData];
}
This is the tableView method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return myUser.rosterArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *iden = #"MyTable";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:iden];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:iden];
}
NewRoster* myNewRoster = [myUser.rosterArray objectAtIndex:indexPath.row];
cell.textLabel.text = myNewRoster.nameRoster;
return cell;
}
When the user adds the first object, the tableView get own row. When the user adds the second object, it adds two rows of the second object and on this way.
How can I fix this issue?
You have add observer(notification) in viewWillAppear which get called everytime when view will appear.
add notification in viewDidLoad instead of viewwillAppear.
I always like to put NSNotification subscriptions in init / and unsubscriptions in dealloc. This pattern is easy to read and debug. Also, it guarantees you will never double subscribe or double unsubscribe.
In your case, you are prone to creating multiple subscribtions in viewWillAppear
- (instancetype)init
{
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(RosterSave:) name:#"RosterSave" object:nil];
...
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#Feroz is right about you allocating a new object and replacing it with notification.object. #Lion is right about viewDidLoad vs. viewDidAppear You are generating multiple notifications. You need to only generate one per object. Put a breakpoint in your RosterSave code and count how many times it's called per new object. Also look at the stack trace to see who is generating these calls. It's down to a simple matter of stepping through, understanding your code, and seeing what's happening.

Pass Date Picker date back to View Controller to show Date in label

I've researched a bunch of questions on how to do this, and am coming up just short.
I have ViewControllerA and ViewControllerB.
ViewControllerB is passing the NSDate from the UIDatePicker back to ViewControllerA.
I'm fine until trying to put that NSDate as a label in the TableViewCell it corresponds with.
Can you help? Thanks!
ViewControllerA
- (void)addItemViewController:(EXEDurationPickerViewController *)controller didFinishEnteringItem:(NSString *)item {
NSLog(#"This was returned from ViewControllerB %#", item);
}
item is the Date picked from ViewControllerB. How do I get it to show up as a label in the corresponding TableViewCell?
Use delegate to pass the date or other option is send Notificaition
Add this in ViewControllerA
#interface ViewControllerA : UIViewController{
NSIndexPath *selectedIndexPath;
}
#end
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"dateSend"
object:nil];
}
- (void) receiveNotification:(NSNotification *) notification
{
NSString *item = notification.userInfo[#"date"];
// show for what cell you want to show
//keep selectedIndexPath as instance Variable
YourCell *cell = (YourCell *)[self.tableView cellForRowAtIndexPath:selectedIndexPath];
cell.label.text = item;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedIndexPath = indexPath;
}
//Post the notification fro `ViewControllerB`
- (void)addItemViewController:(EXEDurationPickerViewController *)controller didFinishEnteringItem:(NSString *)item {
NSLog(#"This was returned from ViewControllerB %#", item);
[[NSNotificationCenter defaultCenter] postNotificationName: #"TestNotification" object:nil userInfo:#{#"date":item}];
}
In the didSelectRowAtIndexPath (or in prepareForSegue if you're using that instead) save the indexPath of the selected cell in a property. Then, in your delegate method, add item to your model (whatever you're populating your table view with), and then call reloadRowsAtIndexPath: with that saved indexPath to update the table.

Best practice for KVO observing model changes in UITableView

Let's imagine a basic iPhone app with a table view to show a list of people and a details view to change the name of a person embedded in a navigation controller.
I'm using KVO to get notified in my table view controller that the name of a person was changed down in the details controller.
My question is when/where to add and remove my table view controller as observer for the name of each person object.
My approach:
#implementation PeopleTableViewController
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
Person *person = ...; // person for index path
[person addObserver:self forKeyPath:#"name" options:0 context:(__bridge void *)(PERSON_NAME_CTX)];
}
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
Person *person = ...; // person for index path
[person removeObserver:self forKeyPath:#"name"];
// This is not called when the view is removed from the hierarchy
// Can't use viewDidDisappear: because we are using a navigation controller
// and tableView:willDisplayCell: is not called when we return from the details controller
}
- dealloc {
// See comment in didEndDisplayingCell:
for (UITableViewCell *cell in self.tableView.visibleCells) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Person *person = ...; // person for index path
[person removeObserver:self forKeyPath:#"name"];
}
}
Due to the navigation controller things are a bit tricky, because tableView: didEndDisplayingCell is not called when the view is removed from the view hierarchy. I can't remove the observer in viewWillDisappear:, because when the user returns from the details controller I still need to observe the person objects for changes.
Removing the observer in dealloc seems to work. My question: is this the right way to do it?
Usually you should call addObserver/removeObserver on viewWillAppear/viewWillDisappear methods respectively, because dealloc method is not balanced with this calls (I mean can be called few times than the methods above). Maybe one of the best solutions is use a NSFetchedResultsController in order to track any change to the data source.

UITableView reloadData not working on MainTable in Split View Controller

I have a feature in my app where the user can change the color scheme of the app. The app uses a Split View Controller, with a MainTable and DetailView table. Everything works fine except for the MainTable. What is failing is that the MainTable reloadData method is not causing the cells to be redrawn.
It should be noted that I am changing globalHighContrast and sending the notification from a UIModalPresentationFormSheet viewController, so the tables are kind of visible on the screen while the viewController is active.
I am triggering the screen update from a notification, like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadAllTables)
name:#"contrastModeChanged"
object:nil];
Then, to make sure that I call reloadData on the main thread, I am handling the notification like this:
-(void)reloadAllTables{
[self performSelectorOnMainThread:#selector(doReloadAllTables) withObject:nil waitUntilDone:NO];
}
-(void)doReloadAllTables{
[self showIcon];
if( globalHighContrast ){
theTable.backgroundColor = [Colors lightBkgColor];
self.view.backgroundColor = [Colors lightBkgColor];
} else {
theTable.backgroundColor = [Colors darkBkgColor];
self.view.backgroundColor = [Colors darkBkgColor];
}
[detailViewController configureView:currentMainMenu];
[detailViewController.subTable reloadData];
[theTable reloadData];
// desperate try to force it to work
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentMainMenu inSection:0];
[self tableView:theTable didSelectRowAtIndexPath:indexPath];
}
Both reloadAllTables and doReloadAllTables are being called, but
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
is not being called.
As soon as I tap a cell on the MainTable it does update correctly to the new color scheme.
Also, there is a desperate attempt to workaround this by trying to simulate the MainTable touch, but that doesn't work either.
You can try to put code for updating you scheme in -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath method...

How to display value of selected row to next view controller?

I have an array.This array loading from web service in TableView.
There are all BranchId in tableview.I want display fields of selected branchId when selected row.
e.g Selected "1234" in tableview
Open new view controller(DetailViewController) :
BranchID:1234
BranchName: ABCDEFGH
I have Branchname in web service
TableviewCodes: http://pastie.org/8052416
How can I display selected ID's detail on new view controller ? Thanks
There are different ways, here is one:
From your first viewController:
NSDictionary* dict = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:theIdYouWantToSend]
forKey:#"index"];
[[NSNotificationCenter defaultCenter] postNotificationName: #"getID" object: dict];
Now from the new view controller (detailViewController), in the viewDiDLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getID:) name:#"getID" object:nil];
and create method:
-(void)getID:(NSNotification*)notification{
NSDictionary* dict = (NSDictionary*) notification.object;
}
You can easily get the ID from the dictionary
myId = [dict objectForKey:#"index"];
you should modify didSelectRow method by below way:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *second=[[DetailViewController alloc]
initWithNibName:#"DetailViewController" bundle:nil] ;
second.branchId = [myArray objectAtIndex:indexPath.row];
[self presentModalViewController:second animated:YES];
}

Resources