Passing Data From Views in Core Data - iphone

Hello everyone — I am a beginner in iPhone programming and Core Data. I am currently trying to learn some of the theory behind Core Data, and have been using this tutorial to help me implement it in my app.
The tutorial teaches by making the main view a UITableViewController that lists the saved objects and another UITableViewController that saves objects (where you enter in the attributes).
The app that I am creating has 3 views. The main view is a plain UIViewController (it handles calculations), you are able to save your calculations by tapping a UIBarButtonItem that brings you to the second view where you enter in more specific attributes. Once you tap save, you are taken BACK to the main view, where you are able to tap a Show Saved button to access the UITableViewController containing saved objects.
I have included #imported the UITableViewController files into my main view's interface file, but when I run the program, I get an error on this line in my prepareForSegue method:
addShoeSizeTVC.managedObjectContext = self.managedObjectContext;
The error is "Property managedObjectContext not found on object of type 'SSFViewController*'" I understand the meaning of this error — I don't have any object called managedObjectContext in my SSFViewController class, but I figured that if I included my file that DOES contain managedObjectContext that it would still be recognized. I should add, that in the tutorial, the prepareForSegue method was contained in the list view for the segue to the add new object UITableViewController. I moved this method to my mainViewController.
I also get an error in my App Delegate in my ApplicationDidFinishLaunchingWithOptions method:
controller.managedObjectContext = self.managedObjectContext;
I understand that this stems from the same problem with the other error (it gives the same error message).
I do not understand how to pass data going from my viewA (mainView), to viewB (add object), back to viewA, then to viewC (view saved objects). I have heard about delegation and am using it in my prepareForSegue method in my SSFViewController main view:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Add Object Segue"]) {
NSLog(#"Setting ObjectsTVC as a delegate of AddObjectTVC");
AddObjectTVC *addObjectTVC = segue.destinationViewController;
addObjectTVC.delegate = addObjectTVC.self;
addObjectTVC.managedObjectContext = addObjectTVC.self.managedObjectContext;
}
}
Also on the addObjectTVC.delegate = addObjectTVC.self; line I get a warning that says "Passing 'AddObjectTVC*' to parameter of incompatible type 'id'"
Do I have to set up an NSManagedContext or another delegation method in my main view? Or is it something that I must add to any of my Table views???
Thank you very much. I feel like this is a simple problem to solve, if provided with the right information. I am happy to post any other methods that I used if needed to solve the problem. I am a beginner, so it would be great if you could explain in a beginner-friendly way.
Thanks!

First of all, if you want data from ViewA to ViewB, insert a property in the ViewB and you can pass data from ViewA to this #property
Example
ViewB:
#property (nonatomic, strong) NSString *yourName;
(don't forget to call #synthesize yourName )
ViewA: (in prepareForSegue method)
"ViewB-Controller" *controller = segue.destinationViewController;
controller.yourName = self.name
--> name will be passed to ViewB
Second:
I prefer a delegate which send from ViewB to ViewA "Hey please save your data". It keeps your controller easy and smart, and you don't have to manage the save method from all view controllers. Delegate is an important chapter in iOS and it can be very frustrated for a beginner. (I was in the same situation 9 months before ;))
Search for a delegate example and try to understand how it works (learning by doing), if you have further question about delegate, I will friendly respond to your question.

It isn't the view controller that has the managedObjectContext property, but your UIManagedDocument.
The context is typically described as the 'scratch pad' in which your app will work with the data store.

Related

referencing views elements in xcode iphone programming

I am trying to do something really simple, but I can't find any examples and can't figure out.
I am coding in xcode 4 for iphone 5.
I have 2 views on my storyboard. One has a button (ButtonFromFirstView) which leads to my second view. The second view has a picker view object and a "select" button. When I click "select" it takes me to my first view. And I would like the ButtonFromFirstView title to be the string selected in the picker from second view. How do I get what value was selected in the picker view?
in my second view I have the following method which catches the changes in the picker. However ButtonFromFirstView is NOT available. How can I get to it? If the button is on the same view where picker is, that it works, but this is not what I want.
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
NSString *SelectedString = [DeparturePickerArray objectAtIndex:row];
[ButtonFromFirstView setTitle:SelectedString forState:UIControlStateNormal];
}
The general paradigm for iOS development is that each "screenful" of content should have a separate view controller. So, for each of your two screens, you should have a subclass of UIViewController, rather than a subclass of UIView.
Your question is essentially - how do you pass information between the two view controllers? In a situation as simple as yours, the best way is to hook them up from your app delegate class. So when you create the view controllers (e.g. from a nib), you want to create a method which you call like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
... (create view controllers)
[secondViewController setButtonToUpdate:firstViewController.buttonFromFirstView];
....
}
Within secondViewController, you then need to keep a reference to the button that's owned by firstViewController. So, something like this in the header file:
#interface SecondViewController : UIViewController {
UIButton *buttonToUpdate;
}
#property (nonatomic, retain) UIButton *buttonToUpdate;
This is a simple answer for a simple question, but to do things "properly", you would probably want to remove the dependency between the two view controllers, and look into using formal or informal protocols.
Also, iOS uses MVC (Model-View-Controller), which would dictate that SecondViewController should first send the string from the picker to some Model class that might then notify an observer such as FirstViewController that its data has been updated.
Does this make sense or do you need a more full explanation?
Check out "Introducing Interface Builder Storyboarding" in the WWDC 2011 Session videos: https://developer.apple.com/videos/wwdc/2011/. It shows you how to manage a sequence of views (or more precisely view controllers) in iOS, how to send data down to a detail view, and how to get user input or other data back. This video pulls together: (1) how to make the UI without programming, (2) how UINavigationController helps the screen flow, and (3) how to pass data back and forward, both using protocols and via a Core Data database. The parts that directly answer the question start at time 12:00.

Trying to update UILabel with contents of UITextField on different view controller

I have two view controllers, one is MainViewController, the other is SetupViewController. I want a UILabel on MainViewController to set the text to the contents of a UITextField from the SetupViewController when a button is pressed in the SetupViewController.
In SetupViewController, I have this in the IBAction:
- (IBAction)donePressed:(id)sender {
MainViewController *mvc = [[MainViewController alloc] init];
[mvc.testLabelOnMVC setText:testTextFieldOnSVC.text];
[release mvc];
}
testLabelOnMVC (and testTextFieldOnSCV, with respective terms) is
#property (nonatomic, retain) UILabel *testLabelOnMVC;
and is also synthesized.
Every time I try, it doesn't work. Nothing happens, nothing changes. I have no errors or warnings. Can anyone help me out?
The view of your MainViewController does not exist until you reference the MainViewController's view property (which forces viewDidLoad to execute). You must reference the view (or otherwise force the view to be constructed) before you attempt to modify any UI objects in the MainViewController.
You are allocating a new MainViewController when you press the button, then you are setting the text of the label on this new controller, not on the MainViewController that your app is showing.
To fix this, create either and IBOutlet or iVar that points to the original MainViewController and set the text on that instead.
Easiest way is to create a #property in the main view controller and write the text in there. Then just read it in the second MVC's viewDidLoad.
The only views that MainViewController should worry about are the ones that it owns; it shouldn't be trying to access the view hierarchy managed by SetupViewController. Likewise, SetupViewController should not directly modify views in MainViewController's view graph.
The right way to do what you're asking is for the two controllers to talk to each other, either directly or via the data model. For example, let's say that your MainViewController instantiates SetupViewController. If that's the case, it'd be natural for mvc to set itself as svc's delegate, so that svc sends it a messages like -setupController:didUpdateTestStringTo:. MainViewController's implementation of that method could then save the new test string somewhere and update it's testLabel field.
Another example: MainViewController instantiates SetupViewController. SetupViewController contains a field where the user can enter a new value for the test string. Before exiting, SetupViewController writes the contents of that field into NSUserDefaults or some other common data storage. When control returns to MainViewController, that object reads the shared data and updates itself as necessary, including setting the new value for testLabel.
There are other variations on the same theme, but the common thread here is that neither view controller has to directly access views that it doesn't own.
You can change the text of the label if the view is already loaded. Instead of initializing the viewcontroller, retrieve it from the view stack if you are using navigation controller.
I dont know if your viewController is already loaded or not.

UITableViewController loading inside a UIViewController inside a UIViewController issue

I don't really know if what I'm doing is the right way to do it. Right now it seems to be working until it hits a certain point with a EXC_BAD_ACCESS message.
I'll describe what I'm doing as best and with the most relevant details I can tell:
I have a CalendarViewController that inherits UIViewController which is loading from a .xib file (CalendarViewController.xib). The class contains a UIView called contentView which I created and which I initialize with another nib file based on a class which is also inherited from UIViewController:
- (void)viewDidLoad {
[super viewDidLoad];
calendarView = [[CalendarView alloc] initWithNibName:#"CalendarView" bundle:nil];
[contentView addSubview:calendarView.view];
}
(calendarView is the class inheriting UIViewController and viewDidLoad is from CalendarViewController.
CalendarView.xib has a UITableViewController with it's respective UITableView. This Table View Controller is linked to a CalendarTableController to which I also generated a .xib file for it.
Everything is being created just right (apparently) but it is crashing somewhere very unexpected. CalendarTableController also implements a DateLoaderDelegate which loads information from an xml on an external url.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// When the data has all finished loading, we set a copy of the
// loaded data for us to access. This will allow us to not worry about
// whether a load is already in progress when accessing the data.
self.lastLoadedMatchXMLData = [self.matchXMLData copy];
// Make sure the _delegate object actually has the xmlDidFinishLoading
// method, and if it does, call it to notify the delegate that the
// data has finished loading.
if ([_delegate respondsToSelector:#selector(xmlDidFinishLoading)])
{
[_delegate xmlDidFinishLoading];
}
}
The application is getting to this point without any problem. _delegate is containing the correct object (a CalendarTableController which implements the DateLoaderDelegate). But when it arrives to the line:
if ([_delegate respondsToSelector:#selector(xmlDidFinishLoading)])
it crashes with the EXC_BAD_ACCESS, I don't really know the reason, if I look at the debugger, the crash is not occurring in any of my classes as any of them are appearing in the execution stack. URLConnectionClient seems to be generating it, but I don't really know why. The loading of the xml had worked earlier before I rearranged the ViewControllers to work as I have described now.
Any ideas?
It's weird. I fixed the problem but I had to dedicate the whole UIViewController to contain the UITableView. What I did was this:
Create an IBOutlet with the custom UITableViewController (CalendarTableViewController) in the custom UIViewController.
In the loaded .xib file I linked the IBOutlet to a created UITableViewController declared as a CalendarTableViewController.
This way I solved the problem of the UITableViewController being deallocated without reason. But now the image views I had placed in the intermediate UIViewController wouldn't appear. I had to set that UIViewController to contain solely the CalendarTableView and place the image views in the initial UIViewController. Weird, isn't it? I don't like much the hierarchy I created... but for now that will do =S
Check that you have defined properties for all of your subviews and that you are retaining everything that you need to be. Bad Access May mean that you're attempting to call respondsToSelector on an object that has been released.
Have you tried calling loadView before adding the nested controller's view to the parent's view?
Maybe viewDidLoad is not executing before you add the view and some variables were never initialized.

Accessing a instance method within another view controller

I have a tab bar controller with 4 tabs. Each tab has its own view controller and a UIWebView.
Let's say I have a button (button1) in vc1 and an instance method onClick1 as well. In vc2 I have a method named reload. My question is, how do I access the specific instance method, onClick1 in vc2, from vc1?
For further detail, I'm actually trying to code a simple shopping utility for the iPhone. When a user adds an item to the cart from the browse view, I want to be able to automatically reload the cart view.
Below are some examples of what I mean. This problem has been more difficult than I thought. I'm not sure if I have redesign my application or what. Perhaps have both vc1 and vc2 belong to a subclass of vcmain and have reference to each of them there? However, if I do that, then how do I refer them to their corresponding .xib? Thanks guys!
#implementation viewController 1
//Reloads vc2
-(IBAction) onClick1: (id) sender {
//Calls vc2 reload
[vc2 reload];
}
#end
#implementation viewController 2
//Reload View
-(void)reload {
[webView reload];
}
#end
You've basically got three approaches, each one of which has plusses and minuses. I'm going to give you a high-level overview and let you go to Apple's quite comprehensive documentation for details. Hopefully I can give you the right terms to google for more specific help.
The approach that #dredful quite ably details is to have a handle to the "other" view controller(s) and call methods on them directly. That works fine, but it can be confusing and cumbersome handing pointers to all your controllers around, and traversing the view hierarchy to get get at the controller you want can be very tricky indeed.
The second approach is Key-Value Observing. You can register one view controller to "watch" a particular key (named property) of another view controller, and fire a particular selector when various things happen with it. This is kind of magical and nice, although at some point you have to have pointers to both controllers at the same time, which doesn't entirely relieve the downside of the "call it directly" method above. It is also a sort of unfortunate coupling of view control and data, kind of breaks MVC.
The third approach is using NSNotificationCenter. A class can post a notification, and any other object that registers itself to listen for that sort of notification can be triggered when that happens. It's nice because you might have LOTS of different objects adding items to the cart, and they can just shoot the notification center a note (even passing it an object or arbitrary data, if it wants), and the cart view can consume those notifications, catch the passed objects, and do its thing, not caring in particular who's talking to it. It keeps separate pieces of your application nicely decoupled. The downside is, it's got a bit of overhead to it, and whatever selector the notification-consuming class performs happens synchronously, so you can't hide network activity or some other long process there.
I'm thinking you should already have something like a base UIViewController (let's call it MyTabBars) that has a UITabBarController *tabBarController containing all your Tab Bar View Controllers. If that sounds familiar, you will want a method in MyTabBars called -(void)reloadCart. reloadCart will walk the array of tabBarController.viewControllers. On each viewController you can perform a respondsToSelector:#selector(reload) and if the specific viewController qualifies then it calls that selector method.
In order to do this you probably want all your vc1, vc2, ... files to have an id delegate defined and synthesized. When MyTabBars creates the different tab bars it sets the vc1 and vc2 delegate to self.
#implementation MyTabBars
//Reload Cart View
-(void)reloadCart {
for (UIViewController *thisUIViewController in tabBarController.viewControllers){
if ([thisUIViewController respondsToSelector:#selector(reload)]) {
[thisUIViewController reload];
}
}
}
#end
Assuming you know how to pass a delegate of MyTabBars into your vc1 and vc2, then you can now have the following code in vc1:
#implementation viewController1
//Reloads vc2
-(IBAction) onClick1: (id) sender {
//Calls MyTabBars reloadCart which will look for all tab bar view controllers
//that have the 'reload' method
[delegate reloadCart];
}
#end
This solution idea will cause MyTabBars to trigger any reload method found in any of our tab bar view controllers. Therefore be careful with the naming of such a method in your vc1, vc2, etc files. This solution will trigger a unique vc method or multiple vcs with the same method depending on your naming convention.
Hope this helps.

Calling function in the first view from the other viewcontroller

I hope that you will succeed in at least a little to clarify me how and what to do, I'm sure I'm wrong, so I really need an expert opinion.
I have two viewcontroller together with nibs what I want is to call function that is in first class from another ViewController, the problem is that another viewcontroller manages to call this function but the only thing I can see that is done is NSlog which outputs in the console (which I put in that position to know that the function is called)
example:
FirstViewController.m
-(void)drawingFunction:(NSString*)inputText{
NSLog("Feature was launched");
/* showing stuff in FirstView.xib */
}
SecondViewController.m
-(void)turnOnFunction:(id)sender{
FirstViewController *stuff= [[PrviViewController alloc] init];
[stuff drawingFunction:#"ShowAll"];
}
Basically when I call that function in the PrviViewController.m meaning without reference to the outside e.g.
[self drawingFunction:#"ShowAll"];
everything is well displayed, but when I try to call this function from SecondViewController.m all I see is just a "feature was launched" on the console
I really don't know what to do ...
if i get you right, the FirstViewController is on top of the view controller hierarchy (either within a navigation controller or presented modal)? So, if that is the case just creating an instance of SecondViewController and calling a function which should change the user interface on the view which corresponds to SecondViewController will do nothing at all.
You have to present the SecondViewController and its view with presentModalViewController:animated: for instance. After that the view of SecondViewController will be shown and you can do any changes on that view. Also calling the method drawingFunction: will act as you expect it.
Cheers,
Andreas
You're confusing instances with classes. You don't send messages (call functions) to classes, you send them to instances.
In this case, you have two entirely separate instances of FirstViewController. The first is created by loading the nib and the second is created in -[SecondController turnOnFunction:]. The drawingFunction: message is being sent to the second instance when you want it to be sent to the first.
You need to link the nib instance of FirstViewController to the SecondController instance. You can do that with an outlet in interface builder or you can assign it when you create the display the SecondController.