Accessing NSDictionary of another ViewController shows null - ios

I am using NSDictionary to plot various markers on a Map.After the pins have been plotted I click the call out of the marker to show the user details.Now when I press the call out I am passing details like marker.title and marker.snippet to another View Controller.With refrence to these values I am trying to fetch other details from the dictionary.This is what I do for accessing the dictionary from another view controller.
-(void)fetchMarkerData:(NSString *)typeOfMarker nameOfUser:(NSString *)username
{
NSString *type = typeOfMarker;
NSString *name = username;
NSLog(#"User Type - %# & Name - %# ", type, name); // shows name and type
NSLog(#"members dict from map view - %#" , self.membersDetailsDict); // shows null & self.membersDetailsDict is allocated and initialised in viewDidLoad.
if ([type isEqualToString:#"Member"])
{
for (NSDictionary *instance in self.membersDetailsDict)
{
NSString *name= [instance objectForKey:#"pseudo"];
if ([name isEqualToString:name])
{
self.nameLabel.text = name;
self.displayPicture.image =[instance objectForKey:#"avatar"];
self.cityLabel.text = [instance objectForKey:#"pays"];
}
}
}
}
Passing data this way :-
- (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker
{
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
UserProfileViewController *userProfileViewController = (UserProfileViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"UserProfileViewController"];
[userProfileViewController fetchMarkerData:marker.snippet nameOfUser:marker.title];
userProfileViewController.membersDetailsDict = self.membersDict; //passing dictionary also
UINavigationController *nv = [[UINavigationController alloc]initWithRootViewController:userProfileViewController];
[self.revealSideViewController popViewControllerWithNewCenterController:nv animated:YES];

It seems as though you are trying to access the membersDict variable from your second view controller which you actually instantiated again there when instead you should be accessing the actual membersDict variable from where it was originally created - in what i assume was in your ViewController1. Now from what I understand this is your setup.
Viewcontroller1 contains your maps data points stored in a variable called membersDict.
user clicks on a point and it will push a viewController2, and that's where you're trying to access the membersDict variable from viewController1.
One way you can do this is by feeding your ViewController2 with the variable from ViewController1 when youre about to push it rather than trying to access the membersDict from your `ViewController2 after its been pushed.
So you can do something like this, this is just a crude quick example:
// in .h viewController file
NSMutabledictionary *membersDict; //whatever the data object was that you stored the data in
//in .m viewController1 file
//viewDidLoad method
membersDict = [[NSMutableDictionary alloc] init];
//add whatever data you like to your membersDict
//In section of code where youre about to push your second ViewController 2: Pass the members dict data too along with pushing the second viewController.
ViewController2 = [[ViewController2 alloc] initWithWhatever];
viewController2.membersDict = self.membersDict; //
or
ViewController2 = [[ViewController2 alloc] initWithMembersDict:self.membersDict];
//Then push the viewController2.
This will ensure your membersDict data successfully passes on.

Related

Some data from object either not saving or not loading

I am working on a simple app to track my daughter's hockey games. My problem is that when I stop and restart the app some of the data doesn't get loaded back in. (I'm checking with a log statement that just shows zero regardless of what was there before.)I'm not sure if the problem is in the loading or the saving.
(Sorry for the long post, but I'm not sure where to look.)
It uses a Games class that looks like this:
#import <Foundation/Foundation.h>
#interface Game : NSObject <NSCoding>//conforms to this protocol so data can be prepared for read/write to file
//used primarily in GameFactsViewController
#property (nonatomic, strong) NSString *opponent;
#property (nonatomic, strong) NSDate *dateOfGame;
//used primarily in PlayerActionsViewController
#property (nonatomic) NSInteger shotsOnGoal;
#property (nonatomic) NSInteger shotsNotOnGoal;
#property (nonatomic) NSInteger passesCompleted;
#property (nonatomic) NSInteger passesNotCompleted;
#property (nonatomic) NSInteger takeaways;
#property (nonatomic) NSInteger giveaways;
#property (nonatomic) NSInteger faceoffsWon;
#property (nonatomic) NSInteger faceoffsLost;
#property (nonatomic) NSInteger shifts;
#property (nonatomic) NSInteger blockedShots;
#end
My problem is that the opponent and dateOfGame properties and getting saved and loaded when I start the app again, but none of the other properties are.
Tha main controller is a tableview controler with each game as a row.The opponent and dateOfGame properties are set in a tableview controller and the others in a view controller inside a tab bar controllers. The seques to these controllers are made from a disclosure indicator for the first and by clicking on the row for the second. These work fine, using this code:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//check for proper seque
if ([segue.identifier isEqualToString:#"AddGame"]) {
//identify the top level view controller holding the GameFactsVC (see storyboard)
UINavigationController *navigationController = segue.destinationViewController;
//go down one level to get the GFVC
GameFactsViewController *controller = (GameFactsViewController *) navigationController.topViewController;
//set the current controller (the HSVC) as the delegate for the GFVC
controller.delegate = self;
} else if ([segue.identifier isEqualToString:#"EditGame"]) {
//as above to get to the right place
UINavigationController *navigationController = segue.destinationViewController;
GameFactsViewController *controller = (GameFactsViewController *) navigationController.topViewController;
controller.delegate = self;
//"sender" here is what was clicked, the detail disclosure icon
//this identifies what game data to load to edit
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
controller.gameToEdit = _games[indexPath.row];
} else if ([segue.identifier isEqualToString:#"GameDetails"]) {
UITabBarController *tabBarController = segue.destinationViewController;
PlayerActionsViewController *controller = (PlayerActionsViewController *)[[tabBarController viewControllers] objectAtIndex:0];
controller.delegate = self;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
controller.gameToEditPerformance = _games[indexPath.row];
NSLog(#"Data passed %ld", (long)controller.gameToEditPerformance.shotsOnGoal);
}
}
I am returning to the main controller with this method for the opponent and dateOfGame and I can log the data coming back.
-(IBAction) done
{
//Use delegate to capture entered data and pass to HSVC
//methods here are delegate methods listed above and defined in the HSVC
//see if the data was empty when view loaded
if (self.gameToEdit == nil) {
Game *game = [[Game alloc] init];
game.opponent = self.opponentField.text;
game.dateOfGame = [self convertToDate:self.dateLabel.text withFormat:#"MMM d, yyyy"];
[self.delegate gameFactsViewController:self didFinishAddingGame:game];
} else {
self.gameToEdit.opponent = self.opponentField.text;//updates data model; will update display in delegate method
self.gameToEdit.dateOfGame = [self convertToDate:self.dateLabel.text withFormat:#"MMM d, yyyy"];
[self.delegate gameFactsViewController: self didFinishEditingGame:self.gameToEdit];
}
}
Similarly, I am passing the rest of the data back from the other controller with this line at the end of a long method that sets the data values and I can log the correct data coming back here, too.
[self.delegate playerActionsViewController:self didEditGameData:self.gameToEditPerformance];
I'm calling the Save method with these two similar methods. (The key difference as I see it as I have updated the display in the other table already, while I still need to update the main view.)
- (void) gameFactsViewController:(GameFactsViewController *)controller didFinishEditingGame:(Game *)game
{
//edit already made in data model in GFVC
//need to update display
NSInteger index = [_games indexOfObject: game];//locate the item being edited in the games array
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];//get the right cell
[self configureDataForCell:cell withGame:game];//puts the game data into the labels in the cell
[self saveGames];//write the current data to the file
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void) playerActionsViewController: (PlayerActionsViewController *) controller didEditGameData: (Game *) game
{
NSLog(#"Paased back shots %ld", (long) game.shotsOnGoal);
[self saveGames];
}
Now for data saving and loading. I'm using this code:
- (void) loadGames
{
NSString *path = [self dataFilePath];//for convenience below
//if there is already a data file, unarchive/decode and load games array
//else create an empty arry to hold games
if ([[NSFileManager defaultManager] fileExistsAtPath: path]) {
NSData *data = [[NSData alloc] initWithContentsOfFile: path];//data structure created and loaded with file data
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];//archiver created and connected to data
_games = [unarchiver decodeObjectForKey:#"Games"];
[unarchiver finishDecoding];//data now in games array
} else {
_games = [[NSMutableArray alloc] initWithCapacity:50];
}
}
- (void) saveGames
{
NSMutableData *data = [[NSMutableData alloc] init];//data structure to hold the data to be saved after encoding
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:_games forKey:#"Games"];//I believe key here needs to match class name that will be saved. It tells archiver how to encode object properly
[archiver finishEncoding];//finish encoding, with data now in data structure
[data writeToFile:[self dataFilePath] atomically:YES];//write data structure to file determined above
}
Somewhere here there is a difference in the _games array, I guess, between the two situations, but I'm not seeing it. Or it's some other problem.
Thanks.
The problem was in my Games class. I forgot to include the keys to encode and decode the NSIntegers using
[aCoder encodeInteger:self.shotsOnGoal forKey:#"ShotsOnGoal"];
self.shotsOnGoal = [aDecoder decodeIntegerForKey:#"ShotsOnGoal"];
Etc.

showing data from tableview in a text field objective C/iOS

can't for the life of me figure this out.
so I'm creating a contact list app, very similar to the one that comes with iOS7.
a user creates a name with the GUI on a detail view controller.
user goes back to the main contact list page (master view controller)
user taps on the name/contact they just created
brings them back to the view controller, but it shows what they had entered into the text fields.
User starts with this screen:
User hits the add contact button, adds in the user data here which brings them back to the first screen:
Next, they are brought to the first screen and are able to tap on the contact they've added and should bring up all the details entered (firstname,lastname,phone number etc):
here's my code in my master view controller:
- (void) detailViewControllerWillDissapear: (DetailViewController *) dvc {
NSLog(#"test");
DirectoryEntry *person = [[DirectoryEntry alloc] init];
person.firstName = dvc.txtFirstName.text;
person.lastName = dvc.txtLastName.text;
person.phoneNumber = dvc.txtPhoneNumber.text;
person.address = dvc.txtAddress.text;
[self.entries addObject:person];
NSLog(#"index is %#", person.firstName);
NSLog(#"count of array %lu", (unsigned long)self.entries.count);
dvc.holdData = person;
[self.tableView reloadData];
}
here's my segue:
#pragma mark - segue
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//DirectoryEntry *person = [[DirectoryEntry alloc] init];
DetailViewController *dvc = segue.destinationViewController;
dvc.delegate = self;
if ([segue.identifier isEqualToString:(#"tappedData")]) {
NSLog(#"what is the person class have %#", dvc.holdData);
// NSLog(#"dvc.holdData = %#", person.firstName);
}
}
so I've created the public variable holdData which is meant to hold all the data which was entered from the array into the Person class. but whenever I add it, just comes up with a Null value. I am able to access the variables i.e. holdData.firstName, but just everything is null.
any ideas? really confused, thanks.
The dvc that you have in prepareForSegue is almost certainly not the same object that you had in the detailViewControllerWillDissapear method. (Try printing their addresses to prove that for yourself.) That means that either your main controller should save the Person data and provide it during the segue or you could create a separate data model that each DetailViewController would use directly.

How to Update Label From Different View Controller using NSUserDefaults

I am trying to update a label on view controller 1 by pressing a button on different view controller 2. I have this IBAction in the view controller 2:
- (IBAction)done:(id)sender {
DropPinVC * vc = [[DropPinVC alloc]init];
NSString *updatedLabel = [[NSUserDefaults standardUserDefaults] stringForKey:#"Type Location"];
NSLog(#"Updated Label = %#", updatedLabel);
vc.TypeLabel.text = updatedLabel;
[self closeScreen];
}
However, the label on view controller 1 does not update. Does anybody know a solution to this problem?
vc is a new instance of DropPinVC class declared in the scope of the action posted, and is not the same instance as the one you want to update.
One way to achieve this is to keep a pointer to the first view controller.
In order for viewController2(vc2) to update a UILabel that is owned by viewController1(vc1), vc2 needs a reference to vc1 so it can access the label.
Add a property to vc2 of type DropPinVC
#property (nonatomic, strong) DropPinVC *vc1Ref;
Assuming vc1 allocates and initialises vc2 then where after this happens assign the vc1Ref property.
vc2.vc1Ref = self;
Now in the IBAction, your code can now access the correct instance and update the correct label.
- (IBAction)done:(id)sender {
NSString *updatedLabel = [[NSUserDefaults standardUserDefaults] stringForKey:#"Type Location"];
NSLog(#"Updated Label = %#", updatedLabel);
self.vc1Ref.TypeLabel.text = updatedLabel;
[self closeScreen];
}

pass core data item between view controllers

i am making an app that requires core data to store some information, i have managed to get the app to create the entities and add the information, however i am unable to edit it
i have a list view controller that displays a brief outline of this information and another list view controller which acts as a modal view to allow the user to edit their information.
when the user taps a list item i need the app to show the modal view controller with the selected information loaded from the core data model into the relevant text boxes and when the user clicks save, i need the changes to be saved
at the moment when the user taps the list item the information is not passed into the modal view, NSLog confirms that for me - it tells me the item i tapped in the prepareForSegue: method and is null in the modal view's viewDidLoad:
here's the code:
this is from the MedsList view controller, it displays the information
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
} //selMed declared at top of file as NSManagedObject *selMed;
selMed = [self.meds objectAtIndex:indexPath.row];
NSLog(#"SELECTED MED: %#",[selMed valueForKey:#"name"] );
UIStoryboardSegue *segueString = [NSString stringWithFormat:#"%#",#"editMeds"];
NSLog(#"%#",segueString);
NSLog(#"%# %#", #"MED NAME",[self.meds objectAtIndex:indexPath.row]);
[self performSegueWithIdentifier:#"editMeds" sender:indexPath];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue isEqual: #"editMeds"]) {
NSLog(#"%# %#", #"SELMED AT PREPARE FOR SEGUE: ",selMed);
UINavigationController *nav = segue.destinationViewController;
MedsEditViewController *dest = (MedsEditViewController *)nav.topViewController;
dest.med = selMed;
dest.chName = [selMed valueForKey:#"name"];//chosen med's name
}
}
}
here is MedsEdit.m
#import "MedsEditViewController.h"
#import "fibroMappAppDelegate.h"
#import <CoreData/CoreData.h>
#interface MedsEditViewController ()
{
NSManagedObjectContext *context;
}
#end
#implementation MedsEditViewController
#synthesize tbName;
#synthesize tbDose;
#synthesize tbMaxDose;
#synthesize tbType;
#synthesize med;
double dose;
double maxDose;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%# %#", #"recieved MEDICATION", self.med);
NSLog(#"%# %#", #"recieved Name", _chName);
fibroMappAppDelegate *delegate = [[UIApplication sharedApplication]delegate];
context = [delegate managedObjectContext];
tbName.text = _chName;
}
chName is declared in the .h file as a NSString
could somebody please help me work out what i have done wrong? and how to fix it..... i've spent a lot of time on this on and off in the last few months
here is a copy of the log after prepare for segue starts
2013-11-07 14:21:15.475 fibromapp[660:70b] SELECTED MED: med1
2013-11-07 14:21:15.476 fibromapp[660:70b] editMeds
2013-11-07 14:21:15.477 fibromapp[660:70b] MED NAME (entity: Medication; id: 0x8a6b0a0 ;
data: {
dose = 3;
lastTaken = nil;
maxDose = 5;
name = med1;
type = Miligrams;
})
2013-11-07 14:21:15.479 fibromapp[660:70b] Warning: Attempt to present on while a presentation is in progress!
---------------------the following is in the MedsEdit viewDidLoad-------------------------
2013-11-07 14:21:15.497 fibromapp[660:70b] recieved MEDICATION (null)
2013-11-07 14:21:15.498 fibromapp[660:70b] recieved Name (null)
[segue isEqual: #"editMeds"] should be [segue.identifier isEqualToString: #"editMeds"] for a kick off...
Have you ever tried to set the MedsEditViewController as your segue.destinationViewController?
First of all, where in the code you posted are you actually setting the med on the destination controller? I see
dest.med = selMed
but it is commented out. If you never set it, then it won't ever be available. Could you please reformat and post the complete logs? It's not clear to me if your code ever reached the SELMED AT PREPARE FOR SEGUE log.
Assuming you did not mean to comment that out, or you received an error I think it may be here - UINavigationController *nav = segue.destinationViewController - unless your MedsEditViewController is actually a UINavigationController I suspect that the segue.destinationViewController is the navigation controller, which in turn is going to present your MedsEditViewController. Double check the documentiation for UINavigationController, but off the top of my head I think you need this:
dest.topViewController.med = selMed

Object of array in new ViewController keeps empty

I've a problem with an array.
There's an array with some objects of the class Car in my CarViewController:
...Car.h
#interface Car : NSObject
#property (nonatomic, strong) NSString *name;
..and Car.m
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[Car alloc] init];
if (self != nil)
{
name = [coder decodeObjectForKey:#"name"];
}
return self;
}
and CarViewController
Car *car1 = [Car new];
car1.name = #"A1";
...
cars = [NSArray arrayWithObjects: car1, car2, ..., nil];
But when I now try to have access to this array in NewViewController there is a problem:
- (IBAction)btn:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
CarViewController *vc = (CarViewController *)[storyboard instantiateViewControllerWithIdentifier:#"CarVC"];
Car *car = [vc.cars objectAtIndex:0];
NSLog(#"%#", car.name);
}
But in the log is just written that car.name = (null).
Thanks in advance to your effort.
UPDATE:
- (IBAction)btn:(id)sender {
UINavigationController *nav = self.tabBarController.viewControllers[1];
CarViewController *vc = nav.topViewController;
Car *car = [vc.cars objectAtIndex:0];
NSLog(#"%#", car.name);
}
I've tried something (thanks to rdelmar for his effort), but the result is still the same.
if you put this code
Car *car1 = [Car new];
car1.name = #"A1";
...
cars = [NSArray arrayWithObjects: car1, car2, ..., nil];
in the viewDidLoad method of your view controller, the the result is normal because your view is not loaded yet. (the viewDidLoad method has not been called yet)
I'm not familiar with storyboards but I think when you call
CarViewController *vc = (CarViewController *)[storyboard instantiateViewControllerWithIdentifier:#"CarVC"];
it will instantiate a new ViewController. So if you try to access cars property it will be nil if this new viewController is not presented on screen
Apple Documentation
You use this method to create view controller objects that
you want to manipulate and present programmatically in your
application. Before you can use this method to retrieve a view
controller, you must explicitly tag it with an appropriate identifier
string in Interface Builder.
This method creates a new instance of the specified view controller
each time you call it.

Resources