I am new in programming and I have problem with moving objects from one VC to another.
My second VC have NSArray * objects. Third VC have NSMutableArray *products. I have modal segue from 2 -> 3
Here is my segue method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"Products segue"]) {
ProductsViewController*pvc = segue.destinationViewController;
pvc.products = [self.objects mutableCopy];
}
}
In ProductsViewController i create few objects:
-(IBAction)addSomeObjects {
products = [NSMutableArray new];
[products addObject:#"Cola"];
[products addObject:#"Pepsi"];
[products addObject:#"Fanta"];
[products addObject:#"Red bull"];
[products addObject:#"Monster"];
If I NSLog my IBAction method products successfully added my objects but when I dissmissViewController my objects array is empty.
Thanks for any help.
M.
Koptak. Welcome to programming.
In this example, I would suggest you create a model to manage products. A model is a class that represents data. In this example, create a class to represent the collection of products. For this I will call it a catalog.
// In Catalog.h
#interface Catalog : NSObject
#property (nonatomic, readonly) NSMutableArray *products;
#end
// In Catalog.m
#implementation Catalog
- (id)init
{
self = [super init];
if (self) {
_products = [NSMutableArray array];
}
}
#end
Now that I have a class which can manage products, I need to have catalog properties (of type Catalog) in both the first view controller and ProductsViewController.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Products segue"]) {
ProductsViewController *pvc = segue.destinationViewController;
pvc.catalog = self.catalog; // Pass the catalog between the view controllers.
}
}
- (void)viewDidLoad
{
…
if (self.catalog == nil) // Make sure an instance of `Catalog` has been created!
self.catalog = [[Catalog alloc] init];
…
}
Finally, in ProductsViewController
- (IBAction)addSomeObjects
{
[self.catalog.products addObject:#"Cola"];
[self.catalog.products addObject:#"Pepsi"];
[self.catalog.products addObject:#"Fanta"];
[self.catalog.products addObject:#"Red bull"];
[self.catalog.products addObject:#"Monster"];
}
Now when you dismiss ProductsViewController, the first view controller will have all the new products.
Note: This is the very first step in how to share data. It skips proper class names, data protection, and data validation, but it gets you started.
Related
I have made a segue passing a string which tells the next view controller which instance to parse the CoreData for. The problem is, I am using some code that calls init methods and the sent string is not initialized when it is called. However, the segue is working when I display the string in the destination view controller's viewDidLoad
- (id)init
{
self = [super init];
if (self)
{
[self initFakeData];
}
return self;
}
When that initFakeData method is called it sets up a graph and needs the exercise to hold a valid value
- (void)initFakeData
{
NSString *myExercise=exercise; //returns nil
if (myExercise==nil)
{
myExercise=#"Default";
}
}
Meanwhile...
-(void)viewDidLoad{
NSString *myExercise=exercise; //returns value
}
exercise is a property that is initialized by the previous view controller in a tableview
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if ([segue.identifier isEqualToString:#"showGraph"]) {
JBLineChartViewController *destViewController = segue.destinationViewController;
NSString *myExericse=[NSString stringWithFormat:#"%#", [[_exercises valueForKey:#"exercise"]objectAtIndex:indexPath.row]];
NSLog(#"%#",myExericse);
destViewController.exercise = myExericse;
}
}
The behaviour is correct because during init the exercise in JBLineChartViewController was not set. If you need the exercise attribute in the init method to set certain behaviour that have to be before viewDidLoad, my suggestion is to not use segue but do a designated initWithExercise and push the controller in code. Maybe like this:
- (IBAction)chartButtonPressed:(id)sender {
JBLineChartViewController *vc = [[ShopViewController alloc]initWithExercise:#"EXERCISE_STRING_HERE"];
[self showViewController:vc sender:self];
}
The new view controller is allocated and initialized before prepareForSegue is called. Anything you need to do with CoreData should be done in viewDidLoad. Or you can do it later, e.g. in viewWillAppear or viewDidAppear.
I am trying to pass a string value namely,'_passedDataDate' from a viewController to a TableView Controller. And there in the TableViewController,I am trying to keep it in a array namely,'_dateArray'. while trying to do so, at first a nil object gets inserted and then the real object. how to avoid this nil value getting added?
Here is the code bellow,
- (void)viewDidLoad {
[super viewDidLoad];
_dateArray = [[NSMutableArray alloc] init];
_dateString = _passedDataDate;
if(_dateString){
[_dateArray addObject:_passedDataDate];
NSLog(#"Added Passed Data");
NSLog(#"%ld",[_dateArray count]);
}
else{
NSLog(#"No object Added");
NSLog(#"%ld",[_dateArray count]);
}
}
Output is as follow:
No object Added
0
Added Passed Data
1
Why No object Added is getting printed? I do not want it what shall Ido about it?
Also,I get the below warning,
Warning: Attempt to present on whose view is not in the window hierarchy!
Don't use Segue inside the button action.
Hide the segue
[self performSegueWithIdentifier:#"pass" sender:nil];
And try this :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString: #"pass"])
{
TableViewController *tableView = [segue destinationViewController];
tableView.passedDataDate = #"HAI";tableView.passedDataDestination = #"Hellio";
}
}
I have two views :
View1 (Shop) : URL stocked in NSString for displaying image.
View2 (ModifyShop) : Text field with URL from view1.
I can pass data from view1 to view2 : The URL stocked in NSString appears in Text field.
Now I would like to modify this URL with Text field from view2 and that modify the NSString in view1. How can I make that ?
Here is my code :
Shop:
- (void)viewDidLoad {
[super viewDidLoad];
[self.modifyButton setHidden:YES];
dispatch_async(dispatch_get_global_queue(0,0), ^{
self.imageButtonURL = #"http://bundoransurfshop.com/wp-content/uploads/2015/02/72-torq-pink.jpg";
imageButtonData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:self.imageButtonURL]];
if ( imageButtonData == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
self.imageButton.imageView.image = [UIImage imageWithData: imageButtonData];
});
});
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"modifyShop"]) {
ShopModify *viewcontroller = (ShopModify *)segue.destinationViewController;
viewcontroller.imageButtonURL = self.imageButtonURL; }
}
-(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue {
NSLog(#"%#", self.imageButtonURL);}
ModifyShop:
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.photoURL.text = [NSString stringWithFormat:#"%#", self.imageButtonURL];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
Shop *viewcontroller = (Shop *)segue.destinationViewController;
viewcontroller.imageButtonURL = self.photoURL.text;
}
That makes my app crashes :
[Reports setImageButtonURL:]: unrecognized selector sent to instance
The error is saying that you are tying to set imageButtonURL on an instance of Reports, not on Shop which is what you think your destination view controller is. It appears that your unwind is going to the wrong controller. You must have hooked up the unwind segue incorrectly. You say that you have 2 views (view controllers actually), but you must also have a Reports class in your app.
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.
I've searched a lot and still couldn't find an answer to this...
I'm working on an iphone App (for college) in xcode 5.0 using storyboards.
I have a View Controller with a table view and everything works fine (data sources, delegate...). Items are stored in an array (call it "array1"). I have an ADD button which brings up a list of items which I want to add (if checked) to array1. I store the checked items in another array ("array2"). The thing is, when I pass array2 to the previous view controller I lose all data in array1 (it becomes nil).
The code I'm using to pass data from array2 to the VC is:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"updateFavoritesSegue"]) {
FavoritesViewController *targetVC = (FavoritesViewController*) segue.destinationViewController;
[targetVC updateFavorites:[self newFavoritesArray]];
}
}
The updateFavorites method is implemented as below.
-(void) updateFavorites:(NSArray *)newFavorites {
[self.favorites addObjectsFromArray:newFavorites];
[self.favoritesTableView reloadData];
}
Thank you very much for your help.
Why don't you just use some handler?
secondVC.h
#property (nonatomic, copy) void (^didFinishPickingItemsHandler)(NSArray *items);
then from the firstVC:
- (void)showSecondScreen {
MYSecondVC *secondVC = /* initialisation code here */
__weak MyFirstVC *self_ = self;
secondVC.didFinishPickingItemsHandler = ^(NSArray *items) {
/* update you data here with your array1 and received picked items */
[self.navigationController popViewControllerAnimated:YES];
};
[self.navigationController pushViewController:secondVC animated:YES];
}