I'm new to iOS development and I want to pass an NSMutableArray from one viewcontroller to another but always gives me null values
FirstViewController.h
#interface FirstViewController : UIViewController
#property (nonatomic, retain) NSMutableArray *colorArray;
-(IBAction)btn:(id)sender;
FirstViewController.m
#implementation FirstViewController
-(IBAction)btn:(id)sender
{
SecondViewController* secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondViewController.animalArray = self.colorArray;
NSLog(#"%#",secondViewController.animalArray); // here its not null
[self.navigationController pushViewController:secondViewController animated:YES];
}
SecondViewController.h
#interface SecondViewController : UIViewController
#property (nonatomic, retain) NSMutableArray *animalArray;
SecondViewController.m
I only used NSLog(#"animalArray:%#",self.animalArray); in viewDidLoad to check the values but gives me null
is there anything I'm missing?
Edit :
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"indidLoad%#",self.animalArray);
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(#"inwillAppear%#",self.animalArray);
}
Replace with following method
-(IBAction)btn:(id)sender{
SecondViewController* secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondViewController.animalArray=[[NSMutableArray alloc]initWithArray:self.colorArray];
[self.navigationController pushViewController:secondViewController animated:YES];
}
:) +1
If you call your NSLog from (void)viewWillAppear:(BOOL) animated, you should see something.
In your code example the viewDidLoad method is called straight after initWithNibName :
[[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
and before you have a chance to set your property.
**FirstViewController.h**
#interface FirstViewController : UIViewController
{
NSMutableArray *SongArray;
}
#property(nonatomic,retain)NSMutableArray *SongArray;
**FirstViewController.m**
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
secondView.SongArray = self.SongArray;
[self.navigationController secondView animated:YES];
**SecondViewController.h**
#interface SecondViewController : UIViewController
{
NSMutableArray *SongArray;
}
#property(nonatomic,retain)NSMutableArray *SongArray;
Do it like this your values arent being retained. Please also check Pass NSMutableArray to one view controller to another
Check the value in viewWillAppear instead of viewDidLoad.
It should work
-(IBAction)btn:(id)sender
{
SecondViewController* secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
NSLog(#"%#",secondViewController.animalArray); // here its not null
[self.navigationController pushViewController:secondViewController animated:YES];
secondViewController.animalArray = self.colorArray;
}
Sometimes if you pass value before pushing the the view it will give null value in second view
Better way is to create your NSMutableArray in Appdelegate file like below...
#property (nonatomic, retain) NSMutableArray *animalArray;//define this in Appdelegate.h file
synthesize it in Appdelegate.m file like
#synthesize animalArray;
Create Appdelegate object in prefix.pch file like below.
#define AppDel ((AppDelegate *)[UIApplication sharedApplication].delegate)
and import your Appdelegate file in prefix.pch like below..
#import "AppDelegate.h"
now where you want to use that array...just write like below..
AppDel.animalArray=//you can do whatever you want with array....
By doing this no need to pass array to other view controller, you can use that global array in whole project ,you can insert object in that array in any class and can access in any class.
Let me know it is working or not!!!
Happy coding!!!!
Related
This question already has answers here:
What's the best way to communicate between view controllers?
(4 answers)
Closed 9 years ago.
I have NSObject class that has a NSString called tweetTitle;
TweetDesc.h file
#import <Foundation/Foundation.h>
#interface TweetDesc : NSObject
#property (strong, nonatomic) NSString * tweetTitle;
-(void) setFullTweetTitle:(NSString *) fullTweetTitle;
#end
TweetDesc.m file
#implementation TweetDesc
#synthesize tweetTitle;
-(void) setFullTweetTitle:(NSString *) fullTweetTitle
{
self.tweetTitle = fullTweetTitle;
}
#end
I have three classes (View Controllers), FirstViewController, SecondViewController and ThirdViewController.
Here are the code of FirstViewController
FirstViewController.h
#interface TweetViewController : UIViewController <UITextViewDelegate>
#property (strong, nonatomic) NSString * tweet;
#property (weak, nonatomic) IBOutlet UITextView *tvTweetTitle;
- (IBAction)btnCreateTweet:(id)sender;
#end
FirstViewController.m
#implementation TweetViewController
#synthesize tvTweetTitle, tweet;
- (void)viewDidLoad
{
[super viewDidLoad];
self.tvTweetTitle.delegate = self;
}
- (IBAction)btnCreateTweet:(id)sender
{
tweet = [[NSString alloc]initWithFormat:#"%#", tvTweetTitle.text];
TweetDesc * td = [[TweetDesc alloc]init];
[td setFullTweetTitle:tweet];
SecondViewController * svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self.navigationController pushViewController:ivc animated:YES];
}
My Question is: How can I use tweetTitle in Second and third ViewController without creating new instance of TweetDesc class and set again the tweetTitle in every ViewControler.
In the second view controller I tried:
TweetDesc * td = [[TweetDesc alloc]init];
NSLog(#"%#", td.tweetTitle);
but I get null, it seems that it was released already or something else.
Thanks for the help in advance.
Just give SecondViewController a TweetDesc property, like this:
#interface TweetDesc : UIViewController
#property (nonatomic, strong) TweetDesc *tweetDesc;
...
Then, after you instantiate a SecondViewController, set its tweetDesc property:
SecondViewController * svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
svc.tweetDesc = td;
[self.navigationController pushViewController:svc animated:YES];
In your SecondViewController implementation, use self.tweetDesc to access the instance.
First thing you don't need a class for a single property use the string only. There are several ways of doing this.
First you can have a tweet property globally defined in AppDelegate.
You can pass the variable reference from one to another viewcontrolleres.
Or, you can use NSUserDefaults to set and get the text.
In your case first one is more useful. Just do as follows
In AppDelegate.h
Define a property as NSString *tweetText;
Now in you action
- (IBAction)btnCreateTweet:(id)sender
{
AppDelegate *appDelegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.tweetText=tvTweetTitle.text;
}
Then in any controller you want to access the value just use
AppDelegate *appDelegate=(AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog("%#",appDelegate.tweetText);
Same approach can be used for NSUserDefaults as to set value
[[NSUserDefaults standardUserDefaults] setObject:tvTweetTitle.text forKey:#"Tweet"];
[[NSUserDefaults standardUserDefaults] synchronize];
and to get
NSLog("%#",[[NSUserDefaults standardUserDefaults] valueForKey:#"Tweet"]);
Make sure you set the values before navigating to other controllers..
The above should solve your purpose..
Based on your comment I feel you want each of your view controllers to hold a TweetDesc object. For that you could have a base class like
#interface baseViewController:UIViewController
#property(strong, nonatomic)TweetDesc *td;
#end
All your viewcontrollers should derive from this base class. So that your controllers definition look like this -
#interface FirstViewController:baseViewController
...
#end
#interface SecondViewController:baseViewController
...
#end
#interface ThirdViewController:baseViewController
...
#end
U instantiate it in the FirstViewController -
TweetDesc * td = [[TweetDesc alloc]init];
[td setFullTweetTitle:#"whatever string you want"];
And pass it on to SecondViewController - //like in Rob Mayoff's answer
SecondViewController * svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
svc.tweetDesc = td;
[self.navigationController pushViewController:svc animated:YES];
INSIDE SecondViewController you may refer it as self.tweetDesc. And pass it on to whichever viewcontroller you want after this using the above code.
ThirdViewController *third= [self.storyboard instantiateViewControllerWithIdentifier:#"ThirdViewController "];
third.tweetDesc = self.tweetDesc;
[self.navigationController pushViewController:third animated:YES];
Pass it on the ANY viewcontroller just make sure it's base class is the baseClassViewController.
You need to use category of TweetDesc where you create method that adds your default title. Or you can create singleton of TweetDesc that will always hold one title.
In your ViewController create a method called initWithTweetDesc. The method will look like this:
- (id)initWithTweetDesc:(TweetDesc*)tweet
{
self = [super init];
if (self) {
_tweet = tweet;
}
return self;
}
Now, when you want to move to the new view controller, run this code:
TweetViewController *vc = [[TweetViewController alloc] initWithTweetDesc:tweetDesc];
[self.navigationController pushViewController:vc animated:YES];
You can use your AppDelegate for this.
eclare the property #property(nonatomic, strong) TweetDesc * td; in AppDelegate.h file.
And change the method like:
- (IBAction)btnCreateTweet:(id)sender
{
tweet = [[NSString alloc]initWithFormat:#"%#", tvTweetTitle.text];
TweetDesc * td = [[TweetDesc alloc]init];
[td setFullTweetTitle:tweet];
[[[UIApplication sharedApplication] delegate] setTd:td];
SecondViewController * svc = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self.navigationController pushViewController:ivc animated:YES];
}
And in your second and third view controllers you can get the value using:
TweetDesc *desc = [[[UIApplication sharedApplication] delegate] td];
NSLog(#"%#",desc.fullTweetTitle);
i am using UITabBar to display two viewControllers, firstViewController is a uiview and secondViewController is a table view. what my need is when i clicked the second view table cell, the value should be updated on UILabel of firstViewController.
my code is here,
In secondviewcontroller.h
#interface firstviewcontroller {
NSMutableArray *stationnamestobepassed;
}
#property (nonatomic, retain) NSMutableArray *stationnamestobepassed;
#end
In secondviewcontroller.m declare this
#implementation firstviewcontroller
#synthesize stationnamestobepassed;
-(void) someaction{
stationnamestobepassed = [NSMutableArray
arrayWithObjects:#"string1",#"string2",#"string3"];
secondviewcontroller = [[secondviewcontroller alloc]initWithNibName:#"NIbName"
Bundle:nil];
secondviewcontroller.stationnamespassed = self.stationnamestobepassed;
//i am not pushing the view controller.
}
#end
In firstviewcontroller.h declare this
#interface secondviewcontroller {
NSMutableArray *stationnamespassed;
}
#property (nonatomic, retain) NSMutableArray *stationnamespassed;
#end
In firstviewcontroller.m declare this
#implementation secondviewcontroller
#synthesize stationnamespassed;
-(void)viewWillAppear:(BOOL)animated
{
//stationNameDisplay is a uilabel
stationNameDisplay.text=[stationnamespassed objectAtIndex:0];
NSLog(#"station name %#",[stationnamespassed objectAtIndex:0]);
}
-(void) dealloc{
[stationnamespassed release];
[super release];
}
#end
problem is , value is not updating and it gives NULL. but i tried with pushing the first vie and it works. actually i don't want to push that view controller because i was already exist on tab.
your view will appear called out when you select the tab. while this line makes a new object of that controller, not that of tab controller.
secondviewcontroller = [[secondviewcontroller alloc]initWithNibName:#"NIbName"
Bundle:nil];
to do what you want. you will have to do some thing like this
UINavigationController* navController = [[self.tabBarController viewControllers] objectAtIndex:/*your tab index on which desiredControllerResides*/];
NSArray* viewControllers= [navController viewControllers];
for(UIViewController *theController in viewControllers)
{
if([theController isKindOfClass:[secondviewcontroller]])
{
theController.stationnamespassed = self.stationnamestobepassed;
//i.e this line of your code secondviewcontroller.stationnamespassed = self.stationnamestobepassed;
}
}
I have two views with navigation controller: first view, there are empty text fields while the second view is a table view where the user can select a row. At the touch of a row there is an action that sets a value to a text field of the first view. Unfortunately when I go back to the first view field is not set.
This is my code:
FirtViewController.h
#interface FirstViewController : UIViewController
{
UITextField *firstField;
UITextField *secondField;
}
#property(nonatomic, retain) IBOutlet UITextField *firstField;
#property(nonatomic, retain) IBOutlet UITextField *secondField;
#property(copy) NSString *selectedRow;
-(IBAction)showTable:(id)sender
FirstViewController.m
#import "FirstViewController.h"
#import "AppDelegate.h"
#import "SecondViewController.h"
#implementation FirstViewController
#synthesize .....
.............
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.firstField.text = selectedRow;
}
-(IBAction)showTable:(id)sender
{
SecondViewController *controllerSecond = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:controllerSecond animated:YES];
}
SecondViewController.h
#class FirstViewController;
#interface ListaViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>
{
UITableView *table;
UISearchBar *search;
FirstViewController *controller;
}
#property (nonatomic, retain) FirstViewController *controller;
SeconViewController.m
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *selectedRow = [tableData objectAtIndex:indexPath.row];
controller.selectedRow = selectedRow;
[self.navigationController popViewControllerAnimated:YES];
}
Based on your code above, you never set the connection in secondViewController back to first
You probably need something like this:
-(IBAction)showTable:(id)sender
{
SecondViewController *controllerSecond = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[controllerSecond setController:self]; //this sets the reference back
[self.navigationController pushViewController:controllerSecond animated:YES];
}
What you need to use is delegate. It is very commonly used pattern in Object-C. Check out my answer to this SO post. Let me know if you still need code after.
I'm not certain I fully understand what you mean, but why don't you creating an NSString containing the value you need in the SecondViewController. That way you can do something like this when you set up the SecondViewController.
SecondViewController *nextView = [[SecondViewController alloc] init];
nextView.myNSString = #"Value you need in the next view";
Then in SecondViewController.h you'll be able to access the NSString like so:
#NSLog(#"%#", self.myNSString);
The main thing you are missing is the Value passing between views. You are not using any object in any of the view that is passing the value to another view. Like, If you want to pass a text from FirstViewController to SecondViewController. You Can do it as follwos.
NSString *textToPass = self.firstField.text;
In Your SecondViewController there need to be a string object say passedText. When you create object of secondViewController in firstViewController, Do it as follows:
SecondViewController *controllerSecond = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
controllerSecond.passedText = textToPass;
[self.navigationController pushViewController:controllerSecond animated:YES];
Now Wherever you want to show the text of first screen, just use "passedtext" String.
Hope it Helps.
:)
===============================================
In your code you may try the followinf modification:
NSString *selectedRow = [tableData objectAtIndex:indexPath.row];
controller = (FirstViewController *)[self.navigationController topViewController];
controller.selectedRow = selectedRow;
[self.navigationController popViewControllerAnimated:YES];
It Will resolve your problem forsure.
FirstViewController.h:
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
{
NSArray *listData;
}
-(IBAction) GoToInsert: (id) sender;
#property (nonatomic, retain) NSArray *listData;
#end
FirstViewController.m:
-(IBAction) upisiRezultat:(id)sender
{
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName: nil bundle: nil];
[self presentModalViewController: secondView animated: NO];
[secondView release];
}
- (void)viewDidLoad
{NSArray *array = [[NSArray alloc] initWithObjects:#"236", #"46",
#"147", #"8", #"56", #"69", #"114", #"2",
#"96", #"518", #"2", #"54", #"236", nil];
self.listData = array;
[array release];
[super viewDidLoad];
}
SecondViewontroller.h
#interface SecondViewController : UIViewController {
}
-(IBAction) insert;
#end
SecondViewontroller.m
-(IBAction) insert
{
/* Here should be the code to insert some number in listData from FirstViewController */
}
So when the app loads it loads FirstViewController.xib and shows the listData array on screen, when I click button "Go to Insert" another view is loaded (SecondViewController.xib) with button "Insert" which should add some number into the array and display the new array on the first view.
How to do that?
You can access the parent view controller with self.parentViewController. Therefore something along these lines (meaning I haven't tested this -- you should) should work:
FirstViewController *firstViewController = self.parentViewController;
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:firstViewControl.listData];
[newArray addObject:#"42"];
[firstViewController setListData:[NSArray arrayWithArray:newArray]];
[newArray release];
However, since you want to add objects to listArray, it would be more natural to use an NSMutableArray instead. Also, you are currently adding NSStrings to the array, when it looks more like you want to have NSNumber objects.
Alternately, and maybe easier, you could have the variables in your main AppDelegate.
Put:
int myNumber;
in the projectname_AppDelegate.h file
Then in each of your view controllers, you can import your AppDelegate.h file and then do something like:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
and then read or change:
appDelegate.myNumber
===
This is not something you should be doing all the time (you don't want your appDelegate to be a giant data repository) but it could give you a quick fix when needed...
I do not know if you have imported the SecondViewController.h , and I think I have an idea an what you are trying to do.
I have an existing TableViewController as follows
// TableViewController.h
#interface TableViewController : UITableViewController { NSArray *dataArray; }
#property (nonatomic, retain) NSArray *dataArray;
And a navAppDelegate - to be specific:
// navAppDelegate.h
#import <UIKit/UIKit.h>
#interface navwAppDelegate : NSObject
<UIApplicationDelegate, UINavigationControllerDelegate> {
UIWindow *window;
IBOutlet UINavigationController *navigationController;}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) UINavigationController *navigationController;
// navAppDelegate.m
#import "navAppDelegate.h"
#implementation navigationtableviewAppDelegate
#synthesize window, navigationController;
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[window makeKeyAndVisible];
[window addSubview:[navigationController view]];
}
Now, I simply added the files to an existing project, except I put the content of (void)applicationDidFinishLaunching{} in a (void)viewDidLoad{} since it's a view from now on and not a window (right?). It doesn't work and my guess is that I have to change all the window calls from above to a view? What am I making here fundamentally wrong?
I'm making a call from
// StartOffHere.m
- (void)LetsGoButtonTouched {
navAppDelegate *newview = [[navAppDelegate alloc] initWithNibName:nil bundle:nil]; // I get a SIGABRT here
[[self navigationController] pushViewController: newview animated: YES];
}
- (void)LetsGoButtonTouched {
TableViewController *tableViewController = [[TableViewController alloc] init];
[[self navigationController] pushViewController:tableViewController animated: YES];
}
Try that. Is that what you wanted to happen?
If so, what I have done is created a new instance of your table view controller and pushed that. In your original code, you were trying to push on the app delegate which cannot be done; the app delegate is not a view controller. TableViewController, your subclass of UITableViewController, is though, so you can use this for the pushViewController: method - and the table view will appear on screen.
Thanks Benjamin. Excellent. But it didn't work quite that way though - after two hard days I managed it to run. For those who are interesed, here's what I did:
If you want to add an existing TableViewController with a NavigationController you'll only need the TableViewController and the DetailViewController files. Forget the AppDelegate.
Do twice new file -> Subclass and copy your existing code into identical TableViewController .h/.m/.xib - and DetailViewController .h/.m/.xib respectively.
When you make the method call you have to integrate both the TableViewController and the NavigationController - like this:
- (void)LetsGoButtonTouched {
TableViewController *tvc = [[[TableViewController alloc] init] autorelease];
UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:tvc]autorelease];
[self presentModalViewController:navigationController animated:YES];
By the way, this question gave me the hint:
Add a UINavigationBar to a UITableViewController without a UINavigationController