How to pass a NSMutableArray including NSManagedObjects to another view controller? - objective-c

I embedded 'Core Data' into my app. I use a predicate and fetch the result. I use a mutableArray called "fetchedObjects" to get the (predicated and then fetched) result. Now I need to pass this result to another view controller. How can I do that?
1.First thing I tried is using 'NSCoding' but it didn't work out. I think Core Data doesn't comply with NSCoding, am I right?
If I can use 'NSCoding', how do I do it? I tried to save and then load the fetchedObjects but it didn't work out.
2.Off course I can define a pointer using
"product = [[Product alloc] initWithEntity:entity insertIntoManagedObjectContext:self.managedObjectContext];"
but how can I get just the "fetchedObjects" mutableArray but not all the objects?
3.Do you know a better way to pass a NSMutableArray having NSManagedObjects in it? and how can we pass the NSManagedObjects to the other view controllers, only in the context?
Thanks!

You could use an NSNotification.
Say View Controller A fetches the results, and View Controller B needs them.
in VCA:
NSArray *data = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"newFetchedDataNotification"
object:nil
userInfo:#{#"data": data}];
in VCB:
in viewWillAppear, start listening for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(processData)
name:#"newFetchedDataNotification"
object:nil];
and add a method:
- (void)processData:(NSNotification *)notification
{
NSArray *data = [notification.userInfo objectForKey:#"succeeded"]
}
And don't forget to unregister for notifications in viewWillDisappear:
[[NSNotificationCenter defaultCenter] removeObserver:self];
I still think the preferred way is using an NSFetchedResultsController to do a first fetch and be notified when objects change. Take a look at this tutorial:
http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller
PS: NSManagedObjects are just like any other objects, no need to use encoding. You need to be careful when modifying and saving them though!

Rather than passing the managed objects directly, if all them are already saved I would pass the ObjectID or the unique url, and have the receiver retrieve them from the store.
Retrieving is fast, and also will avoid many problem that may occur with concurrency.
If the receiving controller just need to display data, I would also think about retrieving just the properties you need with an NSDictionary result type, and pass the resulting array to the controller. But of course, I don't know anything about your design.
UPDATE
If I understood correctly your comment, the big advantage of NSFRC is the bunch of delegate methods it brings with it. If you made your UIViewController the delegate of your NSFRC which is NSFetchedResultsControlerDelegate then the fetched controller itself will invoke your view controllers implemented delegate methods, as soon as there's a change in the model. Then within this methods you have to refresh your table view.
The view is not going to be refreshed by itself.
The difference is that if your UITableView datasource is just an array of managed objects, you would need to build by yourself all the logic to react to model change, recognize which cell need to be refreshed, then decide if it deleted/updated/moved etc.. if you think about it, it is quite a few logic to be implemented, and will not be as efficient as the NSFRC is which is already customized for this purpose.
Also, by reading the rest of your comment, you cannot pass a NSFetchedResultsController, well I suppose you can, but it is useless, the NSFRC is meant to be created and used in the UIViewController on screen. The NSFRC is instantiated with few parameters, one of them is a NSFetchRequest, so you have to customize your request to retrieve the objects you need.
P.S.
Sorry, at the moment I am behind a company firewall which is blocking many sites (including Apple docs), therefore I cannot give you any links.

In simple way ,
VC2.h
#property (strong) NSMutableArray *device;
VC2.m
#synthesize device;
Now in Your VC1 from which you want to pass array
VC2 *v2=[VC2 alloc]initWithNibName:#"VC2" bundle:nil];
v2.device = array;
[self.navigationController pushViewController:v2 animated:YES];
or if you are not using Navigationcontroller.
[self presentViewController:v2 animated:YES completion:nil];

Related

Navigate to a specific UIViewController after receiving a NSNotification

I am developing an iOS application that handles a hierarchy of UIViewController objects using a UINavigationController:
MenuViewController
|
|-ListOfAnimalsViewController
|
|-AnimalDetailsViewController
|
|-ListOfPlantsViewController
|
|-PlantDetailsViewController
The application receives local NSNotification objects with information of a certain animal or plant. The usual behavior when you touch the notification is to open the application and load the first view controller in hierarchy.
Is there a way of programmatically navigating to an instance of a UIViewController deep in the hierarchy instead?
EDIT: I am not asking for pushing the controller into the navigation stack, but for pushing the previous controllers as well, i.e. I would like to keep the navigation schema above.
I'm gonna have to disagree with Suresh. While this solution may work in a simple case like this where you only have 1 previous ViewController, what if you wanted to add a 10th ViewController and keep the entire hierarchy? First of all you'd have to pass the final piece of data you want to show (in this case the plant or animal) through every ViewController and you'd be creating 10 ViewControllers at once. The transition between the current ViewController and the 10th would be far from seamless, performance would be terrible. Also no need to go and create your own navigation system, no need to make things more complicated than they are since this issue isn't too difficult.
Just push the ViewController you want to show, that way you'll only be creating 1 ViewController. In every ViewController that can be pushed as a result of a notification (and all the ones below that), override the behavior of the back button. Check in the viewControllers property of the navigationController if the ViewController before is is the one you expect, using NSStringFromClass. If not, create an NSMutableArray as a copy of the viewControllers, create the ViewController you do expect and insert it at the second to last spot in the array. Then replace the entire stack by calling the setViewControllers:animated: method on the navigationController with the mutable array, animated NO. Finally do the pop. Again you'll have created just 1 ViewController at a time, keeping your performance optimal.
Ican't post code right now since I'm on an iPad but if you need it, just ask and I'll add an example when i have a real keyboard.
If you know the name of the class you can use the following code to navigate.
NSString *className = #"viewControllerName";
UIViewController* myClass = (UIViewController*)[[NSClassFromString(className) alloc] init];
[self.navigationController pushViewController:myClass animated:YES];
I think you will have to invent your own mechanism for following the navigation order since there's nothing in an iOS view controller's static definition that defines an ordering. (Segues seem to be as close as it gets and that's not very close.)
If I had to do this for a hierarchy of view controllers with some unknown depth, I'd consider creating a superclass that worked with a path, sort of like network routing. It would have a method such as displayNextControllerOnPath:(NSString *)path. A MenuViewController that was a RoutingController subclass would receive a path like #"ListOfAnimalsViewController.AnimalDetailsViewController", strip off the first path element, instantiate it (it being another RoutingController subclass), and send it #"AnimalDetailsViewController".
Other details would involve knowing when to stop and perhaps passing an object or dictionary as a payload for the last controller to use as content.
I would reconstruct the stack in the following way:
MenuViewController *menuController = [[MenuViewController alloc] init];
ListOfAnimalsViewController *listController = [[ListOfAnimalsViewController alloc] init];
AnimalDetailsViewController *detailsController = [[AnimalDetailsViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[navigationController setViewControllers:#[menuController, listController, detailsController]];
self.window.rootViewController = navigationController;
You may need to set some properties to based on your implementations.

passing data in nsmutabledictionary from child view to parent view

I have parent view and child view.In child view i have a NSMutableDictionary .and i add values on it.then go back to parent view and want to get values that I added in child view, any one can describe with code. any idea??
The iVars are only accessible while the view is in the run loop.
If it gets deallocated the iVar will be deallocated as well.
There are numerous ways to achieve what you are trying at.
One approach is to store global variables in your App Delegate, which are accessible through all views and the entire application run.
Another approach is, if you don't need the variable to be global accessible to use the NSNotificationCenter and subscribe for a notification.
The second approach is explained in this SO answer by me
Basically you can pass any value to the userInfo, so in your case you would be passing your Dictionary, this way:
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:self userInfo:MyDictionary];
create property in parentView.
And in child view code like below.
((parentView*)childView.superView).dic = dic;
may it help you.
Create an object for "ChildView" in "ParentView", then use the child object to access the NSMutableDictionary.
e.g:
//ParentViewController.m
ChildViewController *cvc = [[ChildViewController alloc]init];
NSDictionary *dict = cvc.myDict;

Communication between UIViewControllers

I am new with Objective-C so apologies for a dumb question.
I am opening an "options" view controller from my main view controller. Both are built in the storyboard. Before and after presenting the options controller I need to stop and start a timer on my main view controller. After the options controller is closed (a button calls dismiss) I need to send some info back to my main controller or at least let my main controller know that it needs to refresh some values.
MAIN QUESTION
What's the best way of presenting a view controller and executing some presenter's methods before and after opening?
WHAT I'VE TRIED
I found a few ways to do it, but they are all cumbersome and I assume that there must be some plausible way of doing it.
Ideally I'd like to use the segue I set up in the storyboard between the two controllers.
I managed to call the options controller programmatically by accessing the storyboard and calling instantiateViewControllerWithIdentifier. It worked but looks a bit complex.
I was not able to find a delegate method on the UIViewController to handle the dismiss event
When I was trying to access the main controller in the options controller via presentingViewController and downcasting it, I got a linkage error by including my .h file twice (not sure what are the Obj-C standards of using #define).
Appreciate your help...
For communication between ViewControllers that are weakly linked, you could use the NSNotificationCenter:
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html
Here you can send a message to all ViewControllers listening, which need to process some changes (for example an option to change the font size).
It's really easy to implement and it keeps certain ViewControllers less dependent on each other.
All of this can be done quite easily with storyboard and NSNotificationCenter, and NSCoding. In the viewDidLoad method of your main controller, put this code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"Update"
object:nil];
Then create this method in the same controller:
(void)receiveNotification:(NSNotification*)notification
{
//...
}
When you want to make the main controller update from the options controller:
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self];
Also, I would suggest using NSArchiving for Basic Data Persistence. Just found this tutorial, looks pretty good.
http://samsoff.es/posts/archiving-objective-c-objects-with-nscoding
Basically, create an object that can store information, code it using nscoding, and then uncode it whenever you need it. It has worked great for me.
Hope that helps!
MAIN QUESTION What's the best way of presenting a view controller and executing some presenter's methods before and after opening?
Just in case the answers above are a bit more involved than you'd like, I'll suggest that the easiest way to execute a presenter's methods before opening is to do so in the presenter's prepareForSegue method. If you need to send data to the destination view controller, you can access its properties this way:
ViewController *destinationVC = [segue destinationViewController];
An easy way to execute the presenter's methods after opening would be:
ViewControllerSubclass *previousVC = [self presentingViewController];
And then use the class or instance to execute your class or instance methods. You could do this in the destination's viewWillAppear.
Sorry if you already knew all this; it's often difficult to surmise what level of complexity is needed.
I have run into this with almost every app I have on the market. Difference is I have never decided to go down the storyboard path.
The way I have always been able to accomplish this is to provide accessor functions between the controllers. You get past the linker issue by defining the cross defined controller as simply a UIViewController type within your options view header, then including the main view controller' header only in the .m file. Now when you call a main view controller routine from your options view, you will have to cast it to the type of your main view controller!
You will also have to provide a routine in your options view that will allow you to set the variable that will hold a pointer to your main view controller to self.
Example for your optionsView
#interface optionsViewController : UIViewController{
UIViewController * myReactiveMainViewController;
}
-(void)setMyReactiveMainViewController:(UIViewController *)controller;
No in the .m file for the optionsView
#import "myMainViewController.h"
-(void)setMyReactiveMainViewController:(UIViewController *)controller{
myReactiveMainViewController = controller;
}
In any other call back to the main view controller you will have to do this:
-(void)returnToMain{
[(myMainViewController *)myReactiveMainViewController someCall:variable];
}
This example would of course assume that your myMainViewController implements a method called "someCall" that take on input parameter.
Thanks for replies.
I ended up with
Calling prepareForSegue to execute pre-transition code
Calling performSelector on presentingViewController when releasing presented view controller.
I am sure other suggestions would work too.

Sharing data between multiple view controllers with Core Data

I have a handful of view controllers that inherit from a subclass of UIViewController, we will call it SpecialViewController. The point of SpecialViewController is to store all of the redundant properties, etc that each of the other view controllers can inherit from. The lifecycle of the application goes from the first to the last view controller, collecting a few pieces of information, so that by the last view controller, there is a full entry of data to be saved to Core Data. Currently my AppDelegate is what holds my model, context, and persistent store coordinator, and I have a subclass of NSManagedObject called Person. My question is, what is the best way to build up the data to the last view controller, then save it?
Can I pass an instance of my Person NSManagedObject from view controller to view controller until it's ready to be committed to the Core Data database?
If so, would it be bad design to have the designated initializer of my SpecialViewController be a method like:
-(id)initWithManagedObjectInstance:(Person *)personManagedObject
and then in the end I could take the completed set of "Person" data (in the last view controller) and commit it to the database...?
Feel free to argue my idea, I want to build it the best way possible.
No, I don't think that will work. The way you would normally do this, or at least the way I do it, is to pass your ManagedObjectContext from your AppDelegate to your first SpecialViewController and from there pass it to each successive SpecialViewController. Make sure SpecialViewController has a property defined as #property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; and then set it as the ManagedObjectContext of whatever class you are in: specialViewController.managedObjectContext = self.managedObjectContext; for each new SpecialViewController you create. Then you can save it at the end with:
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Error saving: %#", error);
// Error handling
}
EDIT: Oh wait, I see what you are saying, sorry. Yes, I think you can. lol. Haven't tried it though. I would probably do this by creating a Person unaffiliated with core data, passing that from view to view, and then when i needed to save it, creating one affiliated with core data with insertNewObjectForEntityForName:inManagedObjectContext: and copying all of the info from the other one to the new one.

Cocoa Touch UIViewController Properties and class design

I'm creating a custom ViewController. This VC needs to load some data that is known in the event that creates it and pushes it to the top of the NavigationController that it is going to be part of.
My question is, how should I pass data from the view that handles the custom ViewController's creation into that custom ViewController.
I've thought of four possible options, and I was hoping to get feedback on why each one is good or not for this functionality.
1) Expose public properties in the custom ViewController and set the UI elements in the view based on those properties in - (void) ViewDidLoad.
2) Expose the actual UI elements themselves and set their .text/.image/.whatever attributes as the ViewController is being created.
3) Create a custom constructor for the custom view and pass in the values I need to set up the UI elements
4) Create a custom model that both views have access to, set the data before the CustomView is created/pushed, and access that data in the ViewDidLoad event.
I'm still new to all of this, and I want to make sure that I understand the proper handling of these handoffs of data. It seems like something like this is probably a simple answer, but I'm still a little confused and its probably really important to do this right to avoid memory loss/leaks.
Also, in case anyone cares, I'm using Stanford's CS193p class on iTunes U and Mark/Lamarche's "Beginning iPhone Development" to teach myself cocoa for the iPhone. I'm working on an application with a NavigationController and a couple ViewControllers (Presence 1 if you're familiar with 193p).
Well, I believe there are advantages & disadvantages to each of those methods depending on your requirements...often it will require some combination of approaches. I believe the most common, for me anyway, is to do something like this where you give it enough to get started.
MyViewController *vc = [[MyViewController alloc] init]; // (or initWithNibName:bundle:)
// transfer vc values here
vc.value1 = aValue;
vc.value2 = anotherValue;
[self.navigationController pushViewController:vc animated:YES];
[vc release];
After your view controller is instantiated you have an opportunity to pass objects to it. Say MyViewController is a detail view then you'd give it the object it will be displaying the details for. Or, if it's a table view you can give it the NSArray it will need for display. Then in viewDidLoad or awakeFromNib or awakeFromCoder, or... you can fill out the view...so to speak.
#1 is fine, with or without #3 (these two are not mutually exclusive)
#4 is my preferred solution. For instance, if I had a UserViewController, I would probably also like to have a User object and create it this way:
User *user = [self.users objectAtIndex:someIndex];
UserViewController *uvc = [[[UserViewController alloc] initWithUser:user] autorelease];
#2 is not a good idea. Objects should not access the UI elements of other objects. Much trouble comes from this when you decide to change your UI around (and you will).