Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 616 Vote(s) - 3.42 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Passing data between view controllers

#21
There are various ways by which data can be received by a different class in iOS. For example -

1. Direct initialization after the allocation of another class.
2. Delegation - for passing data back
3. Notification - for broadcasting data to multiple classes at a single time
4. Saving in `NSUserDefaults` - for accessing it later
5. Singleton classes
6. Databases and other storage mechanisms, like [p-list files][1], etc.

But for the simple scenario of passing a value to a different class whose allocation is done in the current class, the most common and preferred method would be the direct setting of values after allocation. This is done as follows:

We can understand it using two controllers - **Controller1 and Controller2**

Suppose in Controller1 class you want to create the Controller2 object and push it with a String value being passed. This can be done as this:

- (void)pushToController2 {

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@"String"];
[self pushViewController:obj animated:YES];
}

In the implementation of the Controller2 class there will be this function as:

@interface Controller2 : NSObject

@property (nonatomic, strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

_stringPassed = value; // Or self.stringPassed = value
}

@end

You can also directly set the properties of the Controller2 class in the similar way as this:

- (void)pushToController2 {

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj setStringPassed:@"String"];
[self pushViewController:obj animated:YES];
}

To pass multiple values, you can use the multiple parameters like:

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date];

Or if you need to pass more than three parameters which are related to a common feature, you can store the values in a model class and pass that modelObject to the next class

ModelClass *modelObject = [[ModelClass alloc] init];
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

So in short, if you want to -

1. set the private variables of the second class initialise the values by calling a custom function and passing the values.
2. setProperties do it by directlyInitialising it using the setter method.
3. pass more that 3-4 values related to each other in some manner, then create a model class and set values to its object and pass the object using any of the above process.

[1]:

[To see links please register here]



Reply

#22
Passing data back from ViewController 2 (destination) to viewController 1 (source) is the more interesting thing.
Assuming you use storyBoard, these are all the ways I found out:

- Delegate
- Notification
- User defaults
- Singleton

Those were discussed here already.

I found there are more ways:

**Using Block callbacks:**

Use it in the `prepareForSegue` method in the VC1

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
self.blockLabel.text = destination.blockTextField.text;
}];

**Using storyboards Unwind (Exit)**

Implement a method with a UIStoryboardSegue argument in VC 1,like this one:

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

In the storyBoard, hook the "return" button to the green Exit button (Unwind) of the vc. Now you have a segue that "goes back" so you can use the destinationViewController property in the prepareForSegue of VC2 and
change any property of VC1 before it goes back.

- Another option of using storyboards Undwind (Exit) - you can use the method you wrote in VC1

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
NextViewController *nextViewController = segue.sourceViewController;
self.unwindLabel.text = nextViewController.unwindPropertyPass;
}

And in the prepareForSegue of VC1 you can change any property you want to share.

In both unwind options, you can set the tag property of the button and check it in the prepareForSegue.
Reply

#23
I like the idea of *model* objects and *mock* objects based on NSProxy to commit or discard data if what the user selects can be cancelled.

It's easy to pass data around since it's a single object or couple of objects and if you have, let's say, a UINavigationController controller, you can keep the reference to the model inside and all pushed view controllers can access it directly from the navigation controller.

Reply

#24
This is not the way to do it. You should use delegates.

I'll assume we have two view controllers, ViewController1 and ViewController2, and this check thing is in the first one and when its state changes, you want to do something in ViewController2. To achieve that in the proper way, you should do the below:

Add a new file to your project (Objective-C Protocol) menu *File* → *New*. Now name it ViewController1Delegate or whatever you want and write these between the @interface and @end directives:

@optional

- (void)checkStateDidChange:(BOOL)checked;

Now go to ViewController2.h and add:

#import "ViewController1Delegate.h"

Then change its definition to:

@interface ViewController2: UIViewController<ViewController1Delegate>

Now go to ViewController2.m and inside the implementation add:

- (void)checkStateDidChange:(BOOL)checked {
if (checked) {
// Do whatever you want here
NSLog(@"Checked");
}
else {
// Also do whatever you want here
NSLog(@"Not checked");
}
}

Now go to ViewController1.h and add the following property:

@property (weak, nonatomic) id<ViewController1Delegate> delegate;

Now if you are creating ViewController1 inside ViewController2 after some event, then you should do it this way using NIB files:

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];

Now you are all set. Whenever you detect the event of check changed in ViewController1, all you have to do is the below:

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control


Reply

#25
I am currently contributing to an open source solution to this problem through a project called MCViewFactory, which may be found here:

*[Manticore iOS View Factory][1]*

The idea is imitate Android's intent paradigm, using a global factory to manage which view you are looking at and using "intents" to switch and pass data between views. All the documentation is on the GitHub page, but here are some highlights:

You setup all your views in .XIB files and register them in the app delegate, while initializing the factory.

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// The following two lines are optional.
[factory registerView:@"YourSectionViewController"];

Now, in your view controller (VC), anytime you want to move to a new VC and pass data, you create a new intent and add data to its dictionary (savedInstanceState). Then, just set the current intent of factory:

MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];

All of your views that conform to this need to be subclasses of MCViewController, which allow you to override the new onResume: method, allowing you access to the data you've passed in.

-(void)onResume:(MCIntent *)intent {
NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];

// ...

// Ensure the following line is called, especially for MCSectionViewController
[super onResume:intent];
}


[1]:

[To see links please register here]


Reply

#26
I have seen a lot of people over complicating this using the `didSelectRowAtPath` method. I am using [Core Data][1] in my example.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

// This solution is for using Core Data
YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];

YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"]; // Make sure in storyboards you give your second VC an identifier

// Make sure you declare your value in the second view controller
details.selectedValue = value;

// Now that you have said to pass value all you need to do is change views
[self.navigationController pushViewController: details animated:YES];

}

Four lines of code inside the method and you are done.

[1]:

[To see links please register here]





Reply

#27
There are tons of ways to do this and it's important to pick the right one. Probably one of the biggest architectural decisions lies on how the model code will be shared or accessed throughout the app.

I wrote a blog post about this a while back: [Sharing Model Code][1]. Here's a brief summary:


Shared data
-----------
One approach is to share pointers to the model objects between view controllers.

- Brute force iteration on view controllers (in Navigation or Tab Bar Controller) to set the data
- Set data in prepareForSegue (if storyboards) or init (if programmatic)

Since prepare for segue is the most common here is an example:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}

Independent access
------------------
Another approach is to handle a screen full of data at a time and instead of coupling the view controllers to each other couple each view controller to single data source that they can get to independently.

The most common way I've seen this done is a [singleton][2] instance. So if your singleton object was `DataAccess` you could do the following in the viewDidLoad method of UIViewController:

func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}

There are addition tools that also help pass along data:

- Key-Value Observing
- NSNotification
- [Core Data][3]
- NSFetchedResultsController
- Data Source

Core Data
---------

The nice thing about Core Data is that it has inverse relationships. So if you want to just give a NotesViewController the notes object you can because it'll have an inverse relationship to something else like the notebook. If you need data on the notebook in the NotesViewController you can walk back up the object graph by doing the following:

let notebookName = note.notebook.name

Read more about this in my blog post: [Sharing Model Code][1]

[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[To see links please register here]



Reply

#28
There is some good information in many of the answers given, but none address the question fully.

The question asks about passing information between view controllers. The specific example given asks about passing information between views, but given the self-stated newness to iOS, the original poster likely meant between viewControllers, not between views (without any involvement from the ViewControllers). It seems that all the answers focus on two view controllers, but what if the app evolves to need to involve more than two view controllers in the information exchange?

The original poster also asked about **Singletons** and the use of the **AppDelegate**. These questions need to be answered.

To help anyone else looking at this question, who wants a full answer, I'm going to attempt to provide it.

**Application Scenarios**

Rather than having a highly hypothetical, abstract discussion, it helps to have concrete applications in mind. To help define a two-view-controller situation and a more-than-two-view-controller situation, I am going to define two concrete application scenarios.


**Scenario one:** maximum two view controllers ever need to share information.

See diagram one.

![Diagram of original problem][1]

There are two view controllers in the application. There is a ViewControllerA (Data Entry Form), and View Controller B (Product List). The items selected in the product list must match the items displayed in the text box in the data entry form. In this scenario, ViewControllerA and ViewControllerB must communicate directly with each other and no other view controllers.

**Scenario two**: more than two view controllers need to share the same information.

See diagram two.

![Home inventory application diagram][2]

There are four view controllers in the application. It is a tab-based application for managing home inventory. Three view controllers present differently filtered views of the same data:

- ViewControllerA - Luxury Items
- ViewControllerB - Non-insured Items
- ViewControllerC - Entire Home Inventory
- ViewControllerD - Add New Item Form

Any time an individual item is created or edited, it must also synchronize with the other view controllers. For example, if we add a boat in ViewControllerD, but it is not yet insured, then the boat must appear when the user goes to ViewControllerA (Luxury Items), and also ViewControllerC (Entire Home Inventory), but not when the user goes to ViewControllerB (Non-insured Items). We need be concerned with not only adding new items, but also deleting items (which may be allowed from any of the four view controllers), or editing existing items (which may be allowed from the "Add New Item Form", repurposing the same for editing).

Since all the view controllers do need to share the same data, all four view controllers need to remain in synchronization, and therefore there needs to be some sort of communication to all other view controllers, whenever any single view controller changes the underlying data. It should be fairly obvious that we do not want each view controller communicating directly with each other view controller in this scenario. In case it is not obvious, consider if we had 20 different view controllers (rather than just 4). How difficult and error-prone would it be to notify each of the other 19 view controllers any time one view controller made a change?

**The Solutions: Delegates and the Observer Pattern, and Singletons**

In scenario one, we have several viable solutions, as other answers have given

- segues
- delegates
- setting properties on view controllers directly
- NSUserDefaults (actually a poor choice)

In scenario two, we have other viable solutions:

- Observer Pattern
- Singletons

A **singleton** is an instance of a class, that instance being the only instance in existence during its lifetime. A singleton gets its name from the fact that it is the single instance. Normally developers who use singletons have special class methods for accessing them.

+ (HouseholdInventoryManager*) sharedManager; {
static dispatch_once_t onceQueue;
static HouseholdInventoryManager* _sharedInstance;

// dispatch_once is guaranteed to only be executed
// once in the lifetime of the application
dispatch_once(&onceQueue, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}

Now that we understand what a singleton is, let's discuss how a singleton fits into the observer pattern. The observer pattern is used for one object to respond to changes by another object. In the second scenario, we have four different view controllers, who all want to know about changes to the underlying data. The "underlying data" should belong to a single instance, a singleton. The "know about changes" is accomplished by observing changes made to the singleton.

The home inventory application would have a single instance of a class which is designed to manage a list of inventory items. The manager would manage a collection of household items. The following is a class definition for the data manager:

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
The global singleton for accessing application data
*/
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

When the collection of home inventory items changes, the view controllers need to be made aware of this change. The class definition above does not make it obvious how this will happen. We need to follow the observer pattern. The view controllers must formally observe the sharedManager. There are two ways to observe another object:

- Key-Value-Observing (KVO)
- NSNotificationCenter.

In scenario two, we do not have a single property of the HouseholdInventoryManager which could be observed using KVO. Because we do not have a single property which is easily observable, the observer pattern, in this case, must be implemented using NSNotificationCenter. Each of the four view controllers would subscribe to notifications, and the sharedManager would send notifications to the notification center when appropriate. The inventory manager does not need to know anything about the view controllers or instances of any other classes which may be interested in knowing when the collection of inventory items changes; the NSNotificationCenter takes care of these implementation details. The View Controllers simply subscribe to notifications, and the data manager simply posts notifications.

Many beginner programmers take advantage of the fact that there is always exactly one **Application Delegate** in the lifetime of the application, which is globally accessible. Beginning programmers use this fact to stuff objects and functionality into the appDelegate as a convenience for access from anywhere else in the application. Just because the AppDelegate is a singleton doesn't mean it should replace all other singletons. This is a poor practice as it places too much burden on one class, breaking good object-oriented practices. Each class should have a clear role that is easily explained, often just by the name of the class.

Any time your Application Delegate starts to get bloated, start to remove functionality into singletons. For example, the Core Data Stack should not be left in the AppDelegate, but should instead be put in its own class, a coreDataManager class.

**References**

- [Managing Data Flow Between View Controllers][3]
- [Passing Data Between View Controllers][4]
- [Asynchronous JSON Requests in Objective-C][5]

[1]:

[2]:

[3]:

[To see links please register here]

[4]:

[To see links please register here]

[5]:

[To see links please register here]



Reply

#29
If you want to pass data from ViewControlerOne to ViewControllerTwo, try these...

Do these in ViewControlerOne.h:

@property (nonatomic, strong) NSString *str1;

Do these in ViewControllerTwo.h:

@property (nonatomic, strong) NSString *str2;

Synthesize str2 in ViewControllerTwo.m:

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;

Do these in ViewControlerOne.m:

- (void)viewDidLoad
{
[super viewDidLoad];

// Data or string you wants to pass in ViewControllerTwo...
self.str1 = @"hello world";
}

O the buttons click event, do this:

-(IBAction)ButtonClicked
{
// Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
ViewControllerTwo *objViewTwo = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
obj.str2 = str1;
[self.navigationController pushViewController: objViewTwo animated:YES];
}

Do these in ViewControllerTwo.m:

- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@", str2);
}



Reply

#30
You can save data in an App delegate to access it across view controllers in your application. All you have to do is create a shared instance of an app delegate:

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

**For Example**

If you declare a `NSArray object *arrayXYZ`, then you can access it in any view controller by `appDelegate.arrayXYZ`.



Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through