I have an NSMutableArray declared as property in .h and initialized in viewDidLoad in my SPOCVC .m (UIViewController)...
#property (strong, nonatomic) NSMutableArray* SPOCTrackList;
in viewDidLoad
if ([self SPOCTrackList] == nil) {
self.SPOCTrackList = [[NSMutableArray alloc]init];
NSLog(#"SPOTTrackList INITIALIZED");
}
In a separate VC, I'm trying to pass/addObject to SPOCTracklist...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SCTrack* selectedTrack = self.trackList[indexPath.row];
[[[SPOCVC sharedInstance]SPOCTrackList]addObject:selectedTrack];
NSLog(#"%lu", (unsigned long)[[[SPOCVC sharedInstance]SPOCTrackList]count]);
So my NSMutableArray is initialized and I can add dummy objects, but why can't I pass it from another VC using singleton or anything, such as...
SPOCVC* spocVC = self.tabBarController.viewControllers[2];
[spocVC.SPOCTrackList addObject:selectedTrack];
Thanks for pointing me in the right direction.
View controllers are only intended to be around while they are on screen. They are not a place to store data. Generally when one view controller talks directly to another view controller that it didn't create, you're doing something wrong.
Move SPOCTrackList to your model and have both view controllers talk to it rather than to each other.
There should never be a "sharedInstance" on a view controller. That's a sure sign that you're abusing the view controller as the model.
What's probably happening in your particular case is that viewDidLoad is running on a completely different SPOCVC than your sharedInstance.
why not use appdelegate to handle this
appdelegate.h
//add property to hold the reference
#property (nonatomic, copy) NSMutableArray *referenceArray;
//share the app delegate
+(AppDelegate *)sharedAppDelegate;
#end
in appdelegate.m
//synthesize the property
#synthesize referenceArray;
//return the actual delegate
+(AppDelegate *)sharedAppDelegate {return (AppDelegate *)[UIApplication sharedApplication].delegate;}
in viewdidload method
//add the delegate
import "appdelegate.h"
//init the array
self.SPOCTrackList = [[NSMutableArray alloc]init];
//Add reference
[AppDelegate sharedAppDelegate].referenceArray = self.SPOCTrackList;
and add anywhere like this
import "appdelegate.h"
[[AppDelegate sharedAppDelegate].referenceArray addobject:object];
Related
I have a NSMutableArray of a class, and in other class I'd like to initialize it and add various objects. But the problem is that the NSMutableArray isn't retaining the elements. I have the following code:
-(void)viewDidLoad
{
[super viewDidLoad];
MyStops *myStops = [self.storyboard instantiateViewControllerWithIdentifier:#"My Stops"];
myStops.myStopsMArray = [[NSMutableArray alloc] init];
}
And this one:
- (void) addToFavourites:(id)sender
{
UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
NSInteger tag = gesture.view.tag;
UITableViewCell *cell = [stopsTable dequeueReusableCellWithIdentifier:#"cell"];
MyStops *myStops = [self.storyboard instantiateViewControllerWithIdentifier:#"Mis Paradas"];
[myStops.myStopsMArray addObject:[stopsArray objectAtIndex:tag]];
}
And in the other file I have the NSMutableArray declared and synthesized:
//Header file
#property (nonatomic, retain) NSMutableArray *myStopsMArray;
//Implementation file
#synthesize myStopsMArray;
Can you tell me please what I'm doing wrong?
Thanks!
instantiateViewControllerWithIdentifier creates and returns NEW INSTANCE every time!
So you must have a direct pointer to that view controller.
In the header file of your current class make a property like:
#property (strong, nonatomic) MyStops *myStopsVC;
NOTE:
Change strong to weak if your current ViewController already presented from MyStops. But if you're going to push/present MyStops from this vc, then keep it as strong pointer.
You have two distinct instances of your MyStops ViewController, that's why.
In you viewDidLoad, you create a MyStops ViewController then
alloc/init its "myStopsMArray" NSMutableArray.
But in you addToFavorites method, you create another, different and brand new MyStops instance — for which you didn't alloc/init you myStopsMArray property. Hence in that method, myStops.myStopsMArray is still nil.
So I have 2 different table views that use the same array (the array is originally created in the Role table view, the below one). How can I connect those two?
(Usually I use prepareForSegue to pass the data but since there is no segue, I'm not sure how can I do this)
EDIT 1: Add the location of the array.
What is a Model and why you need it
In most of the cases it's useless to pass data around if you don't have a Data Model. You can store your data using a technique called Data Persistence.
An example of a pattern you could use is MVC.
MVC or model-view controlelr is an software pattern widely using when making iOS Apps. In this architectural pattern your Controllers are a bridge between your View and your Model.
In this specific scenario both UITableViewControllers would use the same Model but they would display this data differently.
Persisting your Model
There are several ways to do that, the way I like the most is a little framework called CoreData, you can see this question for some reference on that.
You can also refer to this question to see the use of Singletons. But keep in mind that singletons alone do not persist the data. You'll have to add some sort of mechanism if you want the data to remain there between app sessions.
Persisting user preferences
The simplest way to store small chunks of data is using NSUserDefaults (but it's only meant to store defaults):
Let's assume you have an array
NSArray* testArray = #[#"first", #"second", #"third"];
You can set it to a key by using
[[NSUserDefaults standardUserDefaults] setObject:testArray forKey:#"myArray"];
You can sync NSUserDefaults using
[[NSUserDefaults standardUserDefaults] synchronize];
Then, anywhere in your app you can read it doing
[[NSUserDefaults standardUserDefaults] objectForKey:#"myArray"]
Passing data through the app
On the other hand you have to pass your data around somehow. To do so you can use formal protocols, specifically delegates.
As per the Apple documentation:
In a delegate-based model, the view controller defines a protocol for
its delegate to implement. The protocol defines methods that are
called by the view controller in response to specific actions, such as
taps in a Done button. The delegate is then responsible for
implementing these methods. For example, when a presented view
controller finishes its task, it sends a message to the presenting
view controller and that controller dismisses it.
Using delegation to manage interactions with other app objects has key
advantages over other techniques:
The delegate object has the opportunity to validate or incorporate
changes from the view controller.
The use of a delegate promotes
better encapsulation because the view controller does not have to know
anything about the class of the delegate. This enables you to reuse
that view controller in other parts of your app.
For more information on passing data through view controllers (the main point of this question) take a look at this SO answer.
You should never use data persistence just to pass data through the app. Neither user defaults nor core data.
Also using singletons is not good choice. All will mess up your memory.
Instead use call backs — either as delegates or blocks.
Or use unwind segues.
I explain delegates and unwind segues here: Passing row selection between view controllers
this example passes index paths, as it is appropriate in that situation, but the passed object might be of any type or size, as only pointers are passes.
if you use the NSUserDefaults on the other side, data is copied and written to the disk — there for data is copied and slowly processed — without any use.
I created a sample app how to pass data from one view controller to another view controller in another tab bar branch.
click to enlarge
TabBarController
We need to intercept the section of view controllers to set up some callback mechanism. In this case I am using blocks, but delegate would work as-well.
UITabController has a purely optional delegate. I create a subclass of UITabBarController to serv as it's own delegate, but actually a separate delegate should work in the same way.
#import "GameTabBarController.h"
#import "RoleViewController.h"
#interface GameTabBarController () <UITabBarControllerDelegate>
#end
#implementation GameTabBarController
-(void)viewDidLoad
{
[super viewDidLoad];
self.delegate = self;
}
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navController = (UINavigationController *)viewController;
if ([navController.topViewController isKindOfClass:[RoleViewController class]]) {
RoleViewController *rvc = (RoleViewController *)[navController topViewController];
[rvc setSelectedRole:^(Role *role) {
UIViewController *viewController = self.viewControllers[0];
[viewController setValue:role forKey:#"role"];
[self setSelectedIndex:0];
}];
}
}
return YES;
}
#end
I set the initial tab bar controller to this sub class
Role, RoleDatasource and RoleViewController
The RoleViewController displays a list of Roles, but the datasource and delegate for it's table view are a separate class that I add to the role view controller scene in the storyboard, where i also were it up.
Role
#interface Role : NSObject
#property (nonatomic,copy, readonly) NSString *name;
-(instancetype)initWithName:(NSString *)name;
#end
#import "Role.h"
#interface Role ()
#property (nonatomic,copy) NSString *name;
#end
#implementation Role
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
_name = name;
}
return self;
}
#end
RoleDatasource
#import <UIKit/UIKit.h>
#class Role;
#interface RoleDatasource : NSObject <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, copy) void(^roleSelector)(Role *role);
#end
#import "RoleDatasource.h"
#import "Role.h"
#interface RoleDatasource ()
#property (nonatomic,strong) NSArray *roles;
#end
#implementation RoleDatasource
- (instancetype)init
{
self = [super init];
if (self) {
_roles = #[[[Role alloc] initWithName:#"Magician"], [[Role alloc] initWithName:#"Soldier"], [[Role alloc] initWithName:#"Maid"]];
}
return self;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.roles.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"RoleCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
cell.textLabel.text = [self.roles[indexPath.row] name];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.roleSelector(self.roles[indexPath.row]);
}
#end
RoleViewController
#import <UIKit/UIKit.h>
#class Role;
#interface RoleViewController : UIViewController
#property (nonatomic, copy) void(^selectedRole)(Role *role);
#end
#import "RoleViewController.h"
#import "RoleDatasource.h"
#interface RoleViewController ()
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation RoleViewController
- (void)viewDidLoad {
[super viewDidLoad];
RoleDatasource *roleDataSource = (RoleDatasource *)[self.tableView dataSource];
[roleDataSource setRoleSelector:^(Role *role) {
self.selectedRole(role);
}];
}
#end
PlayViewController
As soon as a role is selected on the role view controller we want to tell our tab bar controller to switch to the game view controller and show the selected role there, see the code for the tab bar controller.
The GameViewController is just a simple view controller subclass that has a property to hold a role and if a role is set, it will displays it name.
#import <UIKit/UIKit.h>
#class Role;
#interface PlayViewController : UIViewController
#property (nonatomic, strong) Role *role;
#end
#import "PlayViewController.h"
#import "Role.h"
#interface PlayViewController ()
#property (weak, nonatomic) IBOutlet UILabel *roleNameLabel;
#end
#implementation PlayViewController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.roleNameLabel.text = (self.role) ? self.role.name : self.roleNameLabel.text;
}
#end
You'll find an example on github.
I think that I should put the array in the Tab bar Controller and connect it to the Role Table view (in order to maintain the behaviour like it is before) and connect it to my new Table view to do what I want to do.
The only problem I can think of is that since my program is small, adding this will not be a big problem. But if I have more vc, it's going to be so much pain.
I have a main ViewController that contains a desginated class. Within that ViewController there is a Container that is linked to an embed ViewController. Within that embed ViewController I am creating an NSMutableArray. I am not trying to access that array inside the main ViewController. I know that if I use:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
NSLog(#"%#",myScript.selectedCells);
The NSLog will output null because I am creating a new ViewController and that gets rid of the already set array. So my question is how can I access that array without overwriting it?
UPDATE:
Heres where the NSMutableArray is being created:
create_challenge_peopleSelect.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
create_challenge_peopleSelect.m:
if([selectedCells containsObject:label.text])
{
cell.accessoryType = UITableViewCellAccessoryNone;
[selectedCells removeObjectIdenticalTo:label.text];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[selectedCells addObject:label.text];
}
This class is the container class off the main ViewController
No I want to access the selectedCells within my main ViewController, I have been doing things such as:
create_challenge_peopleSelect *myScript = [[create_challenge_peopleSelect alloc] init];
I would prefer to stay away from the App Delegate If possible.
You seem to be unclear on the difference between classes and instances. OK, so, say we have two NSArrays:
NSArray *a = [[NSArray alloc] initWithObjects:#"hello", #"I", #"am", #"an", #"array", nil];
NSArray *b = [[NSArray alloc] initWithObjects:#"so", #"am", #"I", nil];
If I do a.count, I'll get 5 as the answer because the array contains five objects. Meanwhile, if I do b.count, I'll get 3, because that array contains three objects. It isn't that creating b "gets rid of the already set count". They are separate objects completely unrelated to each other.
Your view controller class is the same way. When you create a different instance, it doesn't overwrite the old one -- it's just not the same object. In order to use the original view controller object, you need to get a reference to it.
So how do you get a reference to it? Well, the general answer is you design your app so that the two objects know about each other. There are lots of specific ways to accomplish this. A lot of people will say "Just stick a reference in the app delegate." That is one thing you can do, but it's not always the best choice. It can get out of control if you just stick everything in your app delegate. Sometimes it's the right answer, often other things are the right answer. Another approach is to have an object that knows about both of those objects introduce them to each other. But sometimes there is no such object. So it's situational.
Basically, instead of creating a new view controller, you need to maintain a pointer to the original.
I suggest storing an instance of your UIViewController in the AppDelegate in order to retain the particular instance of the view controller you've created by making it a global variable.
ex. In the App Delegate.h
#import "ViewController.h"
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic) ViewController *viewController;
Then from whatever view controllers' .m's from which you need to read/write to the variable, create a pointer to the application's app delegate, ex:
#import "AppDelegate.h"
#interface WhateverViewController ()
AppDelegate *mainDelegate;
- (void)viewDidLoad {
mainDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
}
So wherever you first create that view controller in your code (before ever using it), initialize it using this global variable. ex. If you're using xibs:
mainDelegate.viewController = [[UIViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
ex. If you're using storyboards:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
mainDelegate.viewController = [storyboard instantiateViewControllerWithIdentifier:#"viewControllerID"];
[self.navigationController pushViewController:mainDelegate.viewController animated:YES];
(This is assuming it's in a place other than the app delegate in which case the pointer to the App Delegate isn't needed.)
Then when accessing the array from another UIViewController use
mainDelegate.viewController.array
To access the NSMutableArray from one class to another class use following code.
In the first view controller in which u have declared the object of NSMutableArray, declare the property and synthesize for the same as below,
//In FirstViewcontroller.h class,
#property (nonatomic, strong) NSMutableArray *arrData;
//In FirstViewcontroller.m class
#synthesize arrData;
Also FirstViewcontroller object should be global so you can create the object of FirstViewcontroller in app delegate file.
//appdelegate.h
#property (nonatomic, strong) FirstViewcontroller *objFirst;
//appdelegate.m
#synthesize objFirst;
FirstViewcontroller *objFirst=[[FirstViewcontroller alloc]init];
Now in SecondViewcontroller in which you have to access array,
create the share object of Appdelegate file
//SecondViewcontroller.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then use will get the required array as below,
app.objFirst.arrData
This is your required array I hope it will help you.
The basic idea here is that in your original class, the array is referred to by a pointer. Your original class would allocate it and presumably load it. Other parts of your program can be handed the contents of the property, which is a pointer, assign that to their own pointer holder, and use it as if you had declared it there. Please use the above code;
MyClass *aClass = [[MyClass alloc] initWithMyInitStuff];
NSMutableArray *ThatArray = aClass.MyArray;
NSLog("Count of ThatArray: %d", [That.Array count]);
What you've done in the code provided is set a public property for a mutable array...
#property (strong, nonatomic) NSMutableArray *selectedCells;
The NSMutableArray is not "created" by setting that property. At some point in your code you also have to create the NSMutableArray by initialising...
NSMutableArray *selectedCells = [[NSMutableArray alloc] init];
or by using a convenience method such as...
NSMutableArray *selectedCells = [NSMutableArray arrayWithCapacity:(NSUInteger)<initialising capacity>];
or
NSMutableArray *selectedCells = [NSMutableArray arrayWithArray:(NSArray *)<initialising array>];
Initialising an NSMutableArray is often done only once. If it is repeated, the contents are overwritten against the property used to point to the array. As such, a useful location for this is often within the viewDidLoad view controller lifecycle method.
I'm using a singleton called CSAppData to store data for my iPhone app. I'm storing an object called CSInbox in the singleton. When I logout of my app, I want to clear the data for that object.
Here is my singleton code, including the method for clearing the data:
- (id)init {
self = [super init];
if (self)
{
self.inbox = [[CSInbox alloc] init];
}
return self;
}
+ (CSAppData *)appData {
static CSAppData * appDataInstance;
#synchronized(self) {
if(!appDataInstance) {
appDataInstance = [[CSAppData alloc] init];
}
}
return appDataInstance;
}
+(void) clearData {
CSAppData *appData = [CSAppData appData];
appData.inbox = [[CSInbox alloc] init];
}
However, in one of my view controllers, in the initWithCoder method, I'm storing the inbox variable:
-(id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
self.inbox = [[CSAppData appData] inbox];
}
return self;
}
So, when the app logs out and the clearData method is called, the view controller is still pointing to the old CSInbox object. And even though I am initializing a new view controller and setting it to the root view controller (in the AppDelegate), like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MainTabControllerViewController *viewController = (MainTabControllerViewController *)[storyboard instantiateViewControllerWithIdentifier:#"mainView"];
[self.window setRootViewController:viewController];
The one child view controller that has the CSInbox is never reinitialized, and is still pointing to that old CSInbox object. (I'm not sure why this is happening.)
So, what is the best way to solve this?
Change the clearData method in the singleton to just reset the properties of the CSInbox object, rather than alloc and init and new one?
Move the self.inbox = [[CSAppData alloc] init]; to the viewDidLoad in the view controller class so it gets set properly upon the second login?
Change the logout function in the AppDelegate so that the root view controller and all other view controllers are released, so they will reinitialize upon the second login?
I'm leaning toward #1 or #3...
As requested, here is CSInbox.h:
#interface CSInbox : NSObject
#property (nonatomic,strong) NSMutableArray *threads;
#property (nonatomic, assign) NSInteger newCount;
#property (nonatomic,strong) NSDate *lastUpdate;
-(void) setThreadsFromJSON:(NSDictionary *)json;
#end
And here is CSInboxViewController.h:
#interface CSInboxViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, CSThreadViewControllerDelegate>
#property (strong, nonatomic) IBOutlet UITableView *inboxTableView;
#property (strong,nonatomic) CSInbox *inbox;
#end
And CSAppData.h:
#interface CSAppData : NSObject {
CSInbox *inbox;
}
#property(nonatomic,strong) CSInbox *inbox;
+ (CSAppData *)appData;
+ (void)clearData;
#end
I think the answer lies not in destroying the singleton object and recreating it, but to actually clear the instance variables within that singleton object.
You don't show the declaration of [CSAppData inbox], but if it's an NSMutableArray, for example, then you can clear that, and any existing references to the singleton object can remain:
+(void) clearData {
CSAppData *appData = [CSAppData appData];
[appData.inbox removeAllObjects];
}
One way to handle this, complying with the spirit of using a singleton, is having your view controllers access directly your singleton inbox, i.e.: [CSAppData appData].inbox instead of self.inbox. This is a bit wordier, but it would "magically" fix your issue.
If that is not acceptable to you, I would go with option #1 of those you list. Even better, I would make the inbox in the singleton a singleton itself, or make sure it is never replaced by another instance.
EDIT:
Another approach you have, is using KVO in your controller so that it gets notified when the inbox object has changed. Don't know if it is quite worth it, but could be used.
I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...