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.
Related
I want to do something like UITabBarController. UITabBarController has property viewControllers into which I can add instances of UIViewController * in IB. I'm trying to do the same think with my own VC:
#property (nonatomic, copy) IBOutletCollection(UIViewController) NSArray *viewControllers;
but this doesn't work. Is it even possible for us?
EDIT 1.
Ramshad posted the proper sample, but using XIB. I would like to achieve it using storyboards.
EDIT 2. - at the end of bounty worth...
I question vaderkvarn's post because in case of UITabBarController it works. Also as Ramshad posted, it is possible in NIBs. So far, dasblinkenlight's post is the most correct, but not answers the question. I'm holding this question opened because we shall find out if it's restricted for us or what is the way.
PS: Why these downvotes?
Although it does not look like you can connect UIViewControllers to IBOutletCollections (or there are too many restrictions placed on using them), there is a simple solution that works when you use storyboards. This code goes into your viewDidLoad method:
_viewControllers = [NSArray arrayWithObjects:
, [self.storyboard instantiateViewControllerWithIdentifier:#"Controller1"]
, [self.storyboard instantiateViewControllerWithIdentifier:#"Controller2"]
, [self.storyboard instantiateViewControllerWithIdentifier:#"Controller3"]
, nil];
The reason why the outlet collection solution does not work is that a View Controller is not an outlet. From the documentation:
An outlet is a property that is annotated with the symbol IBOutlet and
whose value you can set graphically in a nib file or a storyboard.
A view controller is not a graphical object.
If you want to use an IBOutletCollection, you should only use one view controller, and put the views in the collection.
But if you want one controller for every view, you need to go for a more programmatic approach. An array with view controllers might be a good start, but I couldn't say as I don't know what you want to do with them.
Edit:
To be more clear as you don't seem to catch my point:
No, it does not has to be a way. A Storyboard is not an API, it is a graphical tool for drawing scenes and segues. It is specially designed for things like Tab Bar based apps.
If you right click on your Storyboard file and choose open as -> Source Code, you will see that a Tab Bar Controller have special elements that other View Controllers do not have. To mess around with the XML in a Storyboard file is beyond me.
If you want to go with Nib files, use Ramshads answer.
If you want to get as close as possible with storyboards, go with dasblinkenlights answer.
But the answer to your question (as far as I know) is NO, there is no way to accomplish this with storyboards. If it were, it would have been documented, which it is not.
I have done your requirement using UISegmentedControl + IBOutletCollection + presentViewController.
You can use any control instead of UISegmentedControl as your wish.
I have Added 3 different UIViewControllers to Xib and labelled as ViewController1,2,3.
I also added 2 extra methods. One for presenting the corresponding view and another one for dismissing the earlier populated view.
I have attached the Xib settings screen-shot below.
You can use only one UIViewController instead of 3 and reuse it with some logic :)
The methods are below:
#property (strong, nonatomic) IBOutletCollection(UIViewController) NSArray *ViewCollection;
//dismissing the earlier populated view
- (IBAction)dismiss:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
//presenting the corresponding view.
- (IBAction)resetAction:(id)sender {
UISegmentedControl *mySegment = (UISegmentedControl *)sender;
int index = 0;
for (UIViewController *viewItem in _ViewCollection) {
if (index == mySegment.selectedSegmentIndex) {
[self presentViewController:viewItem animated:YES completion:nil];
}
index++;
}
}
You can download the my sample application from here
Xib Settings Screen-shot
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.
I'm building an app the works similar to the iPhone Notes app.
My app consist of two screens, first screen is a UITableView listing all the records. The second screen appears when you either click on one of the records or click an add button. This second view contains a UITextView where the user can add/edit the text for that record.
Both screens have a View Controller. The MyListViewController loads the records into the UITableView. When the user clicks on a record I create an instance of the MyEditViewController and push it using the pushViewController method of the Navigation Controller.
MyListViewController -> MyEditViewController
My question is which controller should handle the CRUD logic, should it be the parent controller (i.e. MyListViewController) or the edit controller (i.e. MyEditViewController)?
One thing to note is that you should be able to delete a record from MyListController by swiping a table cell and selecting delete.
You can should also be able to delete from the MyEditViewController by clicking on a delete icon.
I'm basically trying to duplicate the Notes app but am unsure what is best practice in terms of where the CRUD logic should go.
In the scenario that you describe, the best pattern to use, would be the delegate pattern.
Simply make a delegate for your MyEditViewController and make it's delegate your MyListViewController.
You define a delegate as a protocol. So in your MyEditViewController.h put in this:
#class MyEditViewController;
#protocol MyEditViewControllerDelegate <NSObject>
#required
- (void)myEditViewController:(MyEditViewController *)controller didSaveNote:(BOOL)save;
#end
and add this to your already existing MyEditViewController.h code.
#interface MyEditViewController : UIViewController
....
#property (nonatomic, retain) id <MyEditViewControllerDelegate> delegate;
#end
In your MyEditViewController.m code when you push weather the save or the cancel button you send the following message:
[self.delegate myEditViewController:self didSaveNote:YES]
or
[self.delegate myEditViewController:self didSaveNote:YES]
depending on you pushed save or cancel.
In your MyListViewController.h you adopt your newly created protocol like this:
#interface MyListViewController : UIViewController <MyEditViewControllerDelegate>
and in your MyListViewController.m you remember two things. First to implement the required delegate method:
#implementation MyListViewController
...
- (void)myEditViewController:(MyEditViewController *)controller didSaveNote:(BOOL)save
{
// Do business logic here depending on the value of save
}
and the last thing is setting your MyListViewController to the delegate of your MyEditViewController like this:
MyEditViewController *myEditViewController = [[MyEditViewController alloc] initWithNibNamed:#"MyEditViewController" bundle:nil];
[myEditViewController setDelegate:self];
That way you handle all CRUD logic in your MyListViewController and that way you can update the Table View accordingly.
I have recently developed an application that has very similar requirements. I think you should be very clear about your Model, Views & Controllers.
Model is the non-UI part of your application, the management of Notes in your case. I created a singleton object, say NotesManager, whose shared instance can be accessed from anywhere in my code. Something like [NotesManager sharedInstance]. In my application, the view controller does not read/enumerate the documents directory's contents (because thats not its job), the NotesManager does. The List view controller asks the notes manager for the notes to display. [[NotesManager sharedInstance] notesFromDocsDir];
Views are the UI part of your application. In this case, it would be the table view & the note's edit view.
Controllers are the ones that act as the link between your Views & Model. As you know, there is the ListViewController & the EditViewController.
Now, there are two types of interactions:
The first one originates from the UI & has to update the Model. For example, the user taps delete or save. In my application, i do something like [[NotesManager sharedInstance] deleteNote:Note]. You can do this from both the View controllers.
The second one originates from the Model end & updates your UI. For example, in my application, I have enabled iTunesSharing & hence a user can add/delete a note via iTunes. When such an event occurs, my UI has to update itself to reflect the current state of the documents directory. To accomplish this, the NotesManager dispatches an NSNotification. The Controller(s) registers for these notifications & updates the view.
Now for your original question, the CRUD methods reside in the NotesManager. They can be called by the Controllers or by NotesManager itself, when it detects something has changed.
HTH,
Akshay
Both. And neither.
You should use a model to store/provide the data.
ViewController's should control the views and pass instruction to the model to save changes etc.
I would do the business logic in the model - simply call the methods in the model from the viewcontrollers.
Seeing as your child viewcontroller is doing the editing it should be that which instructs the model in that instance.
The parent viewcontroller should instruct the model when you handle the deletion of the data.
I'd say that a good solution is to implement all these operations in your Model. Say, you have a class called Note that can handle CRUD operations. You will also need something like NoteCollection that will provide valid data for your table view.
MyEditViewController will always deal with a single note that should handle operations like save and delete. These should update its state in your note collection.
I am new to iPhone and objective c. I have spent hours and hours and hours reading documents and trying to understand how things work. I have RTFM or at least am in the process.
My main problem is that I want to understand how to specify where an event gets passed to and the only way I have been able to do it is by specifying delegates but I am certain there is an easier/quicker way in IB.
So, an example.
Lets say I have 20 different views and view controllers and one MyAppDelegate.
I want to be able to build all of these different Xib files in IB and add however many buttons and text fields and whatever and then specify that they all produce some event in the MyAppDelegate object. To do this I added a MyAppDelegate object in each view controller in IB's list view. Then I created an IBAction method in MyAppDelegate in XCode and went back to IB and linked all of the events to the MyAppDelegate object in each Xib file.
However when I tried running it it just crashed with a bad read exception.
My guess is that each Xib file is putting a MyAppDelegate object pointer that has nothing to do with the eventual MyAppDelegate adress that will actually be created at runtime.
So my question is...how can I do this?!!!
If you create an instance of MyAppDelegate in each nib file then, yes, you do end up with a lot of different instances of the class when all the nibs load. The app delegate is not identified by class or even protocol but rather by being the object pointed to by the application instance's delegate property. To find the true app delegate, you have have to ask the application object itself for its delegate
You should have all your view controllers descend from a parent view controller class that has an appDelegate property. Implement something like this:
#import "MyAppDelegateClass.h"
#interface ViewControllerBaseClass :UIViewController {
MyAppDelegateClass *appDelegate;
}
#property(nonatomic, retain) *appDelegate;
#end
#implementation ViewControllerBaseClass
#synthesize appDelegate;
-(MyAppDelegateClass *) appDelegate{
self.appDelegate=(MyAppDelegateClass *)[[UIApplication sharedInstance] delegate];
return appDelegate;
}
#end
When the view controller needs the app delegate it just calls self.appDelegate. If you want to access an attribute of the app delegate use self.appDelegate.attributeName.
The important thing is that you ask the application for its specific delegate instance at runtime. You can't do that from a nib file.
I'm not entirely clear what exactly you're trying to do, but it's probably a bad idea. There should only be one app delegate per application, and it should deal with behavior for the whole application. Typically, the app delegate initializes the root view controller(s) and displays them, but not much else (other than handling things like opening and saving data sources).
The view controllers (subclasses of UIViewController) should interact with the XIBs. Having the view-specific behavior in the view controllers makes the app much easier to manage and maintain. Typically, there should be 0 or 1 XIBs per view controller (more than that is complicated). You set up the interaction with the views using the Target/Action pattern with IBOutlets and IBActions (see here for a complete guide). It's generally a bad idea to make view controllers or XIBs dependent on the app delegate (since reducing dependencies again makes the code easier to manage).
In general you should be making a view controller for each of the views you are building, and link events to those view controllers - not the app delegate. In fact usually no event ever is wired to the app delegate from any nib file, even in the sample projects you'll note that view controllers are created and held onto by the app delegate, but it does not receive events.
I'm developing an iPhone app. I need to create a Quiz application that has different Question views embedded in it (see my similar question).
Different types of Question will have different behavior, so I plan to create a controller class for each type of Question. The MultipleChoiceQuestionController would set up a question and 3-4 buttons for the user to select an answer. Similarly, the IdentifyPictureQuestionController would load an image and present a text box to the user.
However, the docs say that a UIViewController should only be used for views that take up the entire application window. How else can I create a class to manage events in my subviews?
Thanks,
Subclassing UIViewController will provide this functionality. For example, MultipleChoiceQuestionController would be a subclass of UIViewController. MultipleChoiceQuestionController would contain the question text (UILabel or UITextView) and several buttons (UIButton). You could create a custom constructor in MultipleChoiceQuestionController that would fill the view with the relevant question string and other relevant info.
When you want to add MultipleChoiceQuestionController's view to your main view's subview, simply do the following:
[myMainView addSubview:instanceOfMultipleChoiceQuestionController.view];
I have the same problem, and according to Apple's doc, here's what you should do:
Note: If you want to divide a single
screen into multiple areas and manage
each one separately, use generic
controller objects (custom objects
descending from NSObject) instead of
view controller objects to manage each
subsection of the screen. Then use a
single view controller object to
manage the generic controller objects.
The view controller coordinates the
overall screen interactions but
forwards messages as needed to the
generic controller objects it manages.
http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html#//apple_ref/doc/uid/TP40007457-CH112-SW12
You can handle the events on the view itself, or your view controller could have a delegate class that changes for different types of question. That delegate would process the different input, and react in a different way to user touches.
Here's some code with the idea.
// In QuestionViewControllerDelegateProtocol.h
#protocol QuestionViewControllerDelegateProtocol
// Define the methods you want here
- (void)touchesBegan;
- (void)touchesEnded;
- (void)questionLoaded;
#end
// In QuestionViewController.h
#interface QuestionViewController {
id<QuestionViewControllerDelegateProtocol> delegate;
}
#end
// In QuestionViewController.m
#implementation QuestionViewController
- (void)viewDidLoad:(BOOL)animated {
[delegate questionLoaded];
}
- (void)touchesBegan {
// Some processing logic.
[delegate touchesBegan];
}
#end
This is a very nice little solution, gives you all of the advantages of a view controller without breaking apples rules.
From the page:
This is a generic controller class
that can be used to handle a subarea.
It is modelled after UIViewController,
but conforms to Apple's
recommendation.
Your view controller creates the
instances and is responsible for
managing the subview controllers.
Alternatively you can further
subdivided your view hierachy and
create subview controllers inside
other subview controllers. In both
cases the controller instantiating the
object is responsible for managing the
subview controller. The responsible
controller is referred to as 'parent
controller.' Subclasses can use the
view controller when they for example
need to show a modal dialog.
https://github.com/Koolistov/Subview-Controller