everywhere on the internet I find examples of how to add rows into a table view by having a special row "Add Row" with a green plus. But I don't want that.
I want to have a plus button in the titlebar of MyTableViewController, and invoke some Modal Add View Controller with a XIB file with just a single text field to fill it in. In this Modal Add View Controller I want to fill in this text field, and after I press Done, Modal Add View Controller dismisses, and I want to find that text added to a MyTableViewController table view.
I have a property in my MyTableViewController to hold all the lines of it:
#property (nonatomic, retain) NSMutableArray *list;;
I just can't get adding rows to work. I don't see where I could do the
[list addObject:];
Here's the code of the MyTableViewController addItem method which I invoke, when a user presses a plus button in the titlebar:
- (IBAction) addItem: (id) sender;
{
NSLog(#"Adding item...");
//Preparing "Add View" which has a single text field
AddViewController *addViewController = [[AddViewController alloc] init];
addViewController.title = #"Add Item";
UINavigationController *modalController = [[UINavigationController alloc]
initWithRootViewController:addViewController];
[addViewController release];
// Showing the prepared Add View controller modally
[self.navigationController presentModalViewController:modalController animated:YES]
NSLog(#"Modal controller has been presented.");
[modalController release];
}
And here is the code in the AddViewController, which is invoked after the textfield is typed in and pressing Done in the titlebar:
- (IBAction) done: (id) sender
{
NSLog(#"Reached Done");
if (textField != nil) {
self.fieldText = textField.text;
}
NSLog(#"About to dissmiss modal controller...");
[[self parentViewController] dismissModalViewControllerAnimated:YES];
NSLog(#"Modal controller has been dismissed.");
}
It is pretty common to create a delegate protocol for such Add Controller and make the parent controller its delegate.
When the Add Controller is "done" (i.e. not cancelled with a possible Cancel button), it calls a delegate method, say, addControllerIsDone: to let the parent table view controller know that it should take the set value, add it to the list, and dismiss the Add controller.
You could also pass the list to the Add Controller and let it add the set value itself before the [parentViewController dismissModalViewControllerAnimated:YES] call.
It depends whether you want to keep the control of the list in your table view controller or you want to pass it to the Add Controller.
And after the Add Controller is dismissed, you can either figure out where the cell for new entry should be added in the tableView and insert it with a nice animation, reload the section (animation also possible) or whole tableView (animation not possible).
First option could looke like this:
#class AddViewController;
#protocol AddViewControllerDelegate <NSObject>
- (void)controllerIsDone:(AddViewController *)controller;
#end
#interface AddViewController : UIViewController
#property (nonatomic, assign) id<AddViewControllerDelegate> delegate;
#end
And the 'done' code
- (IBAction) done: (id) sender
{
......
[self.delegate controllerIsDone:self];
NSLog(#"About to dissmiss modal controller...");
[[self parentViewController] dismissModalViewControllerAnimated:YES];
NSLog(#"Modal controller has been dismissed.");
}
And the MyViewController:
#interface MyViewController : UIViewController <AddViewControllerDelegate>
#end
So it has to implement the controllerIsDone: method. Like this for example:
- (void)controllerIsDone:(AddViewController *)controller
{
[self.list addObject:controller.textField.text];
}
As the AddViewController dismisses itself, MyViewController doesn't have to do it in the delegate method. But the good practice would be that if you popped up the modal view controller, you should also dismiss it, just for symmetry's sake. ;)
In this case, of course the textField has to be a publicly accessible property.
I'm sure you'll figure out the second option.
Read up on Decorator pattern in Cocoa Fundamentals Guide.
After you dismiss your modal controller:
[self addObjectToMyModel:newObject];
such that if you called [tableView reloadData] it would show up, but you don't need to call that, instead:
you need to know where the new object will appear in your table, determine the indexPath, and:
NSIndexPath *indexPathOfInsertedCell = …;
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPathOfInsertCell]
withRowAnimation:UITableViewRowAnimationFade];
Related
I have 2 ViewControllers, in 1st - TableView and in 2nd - button with label on it. When I click on the button in 2nd ViewController I need to go back on TableView and set in
cell.detailTextLabel.text
text from label on the button.
For go back to first view I use:
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
but how I can set label from second view to:
cell.detailTextLabel.text
in first view?????
I would define a protocol & delegate in the second view controller
#protocol SecondViewController;
#interface SecondViewController : UIViewController
#property (nonatomic, assign) id<SecondViewController> delegate;
#end
#protocol SecondViewController <NSObject>
- (void)secondViewController:(SecondViewController *)controller didTappedOnButton:(UIButton *)button;
#end
then when the button is tapped call the delegate:
- (IBAction)buttonTapped:(UIButton *)sender
{
// do somthing..
// then tell the delegate about the button tapped
[self.delegate secondViewController:self didTappedOnButton:sender];
}
In your first view controller implement the protocol
#interface FirstViewController : UIViewController <SecondViewControllerDelegate>
when you push the second view controller, set the first as the second delegate:
- (void)someMethodThatPushTheSecondViewController
{
SecondViewController *svc = [[SecondViewController alloc] init];
[self.navigationController pushViewController:svc animated:YES];
svc.delegate = self;
}
And implement the delegate method to get notified when the button tapped
- (void)secondViewController:(SecondViewController *)controller didTappedOnButton:(UIButton *)button
{
// do somthing after button tapped
// you can get the button title from button.titleLabel.text
}
To access a parent class method or property you gotta implement a protocol, and use it's delegates. You can access child class methods/properties using it's class object that you create in the current (parent) class. But how you want to access a parent class entity from a child class? YES, implementing protocols.
Or the newbie way: after tapping your button, save the needed value into NSUserDefaults. Then, when you go to your parent class (viewController 1), ion viewWillAppear, check that saved value, and if it's not nil, display it.
[self.navigationController popViewControllerAnimated:YES];
I am presenting a WEPPopoverController in my iPhone Application. In that WEPPopoverController, I have added a UITableViewController. This popover I am using for the purpose of search.
Now when I open this Popover, the screen looks something like this:
Now, I want to close dismiss this Popover from the UITableViewController's didSelectRowAtIndexPath,
How to do this?
Code:
self.searchTableViewController = [[SearchTableViewController alloc] initWithStyle:UITableViewStylePlain]; // UITableViewController
self.seachPopoverController = [[[WEPPopoverClass alloc] initWithContentViewController:self.searchTableViewController] autorelease]; // WepPopoverController
self.seachPopoverController.delegate = self;
[self.seachPopoverController presentPopoverFromRect:[searchB frame] inView:topPanelV permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
Thanks in advance!!!
Add 1 variable in SearchTableViewController as parent like
id parent;
Create property for this as
#property (nonatomic,assign)id parent;
Synthesize it
#synthesize parent;
Now
when you create self.searchTableViewController
assign its parent as
self.searchTableViewController.parent = self;
Now in didSelectRowAtIndex
[parent dismisMyPopoverMethod];
-(void)dismisMyPopoverMethod
{
[self.seachPopoverController dismissPopoverAnimated:YES];
self.seachPopoverController = nil;
}
And thats it. Your are done.
Do it this way: make your view controller a delegate of table view controller for only one method - dismissPopover (for other methods the delegate must be table view controller). So your table view controller will have two delegates. Implement this method in your delegate (view controller). But call it from table view controller. In more details step by step:
Declare a property of your popover in a view controller, where you show this popover.
#property (strong, nonatomic) UIPopoverController *popoverController;
Create a new protocol (let's name it TableInPopoverDelegate), that has this method:
- (void) dismissPopover;
Add this method to the implementation file of your view controller and add this:
- (void) dismissPopover {
[self.popoverController dismissPopoverAnimated:YES];
}
Add a header file of protocol and add a delegate property to your table view controller:
#property (strong, nonatomic) id <TableInPopoverDelegate> popoverDelegate;
Make your view controller a delegate of your table view controller before adding it:
tableViewController.popoverDelegate = self; // Here self is your view controller
Call dismissPopover on the delegate when user selects a row.
[popoverDelegate dismissPopover];
I have a View-based app. The first view that is loaded has a button which loads another view using this code:
AddPost *addView = [[AddPost alloc] initWithNibName:#"AddPost" bundle:nil];
addView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:addView animated:YES];
What I want to do is provide a button on the view (AddPost) that will let me close it and go back to the original view. How can I do this?
It seems a little strange, but you can actually have addView call:
[self dismissModalViewControllerAnimated:YES];
From the docs:
"The parent view controller is responsible for dismissing the modal view controller it presented using the presentModalViewController:animated: method. If you call this method on the modal view controller itself, however, the modal view controller automatically forwards the message to its parent view controller."
The answer Conrad gave will work perfectly well. In the name of slightly better encapsulation you could put a delegate protocol on addView and have your first view implement this.
So in the header file for your addView controller:
#protocol addViewDelegate <NSObject>
- (void)addViewRequestDismissal;
#end
You will also need an external properly on the addView controller:
#property (assign) id<addViewDelegate> delegate;
Then make your first view controller implement this, so in it's .h file you should have
#interface firstView : NSObject <addViewDelegate> {
}
When you instantiate your addView remember to set the delegate:
addView.delegate = self;
In the addView controller when your button is pressed call back:
- (void)buttonPressed {
[self.delegate addViewRequestDismissal];
}
Finally in your first view remember to implement this method
- (void)addViewRequestDismissal {
[self dismissModalViewControllerAnimated:YES];
}
Hope all goes well with this. Post back if you have any further problems :)
I need to pop up a quick dialog for the user to select one option in a UITableView from a list of roughly 2-5 items. Dialog will be modal and only take up about 1/2 of screen. I go back and forth between how to handle this. Should I subclass UIView and make it a UITableViewDelegate & DataSource?
I'd also prefer to lay out this view in IB. So to display I'd do something like this from my view controller (assume I have a property in my view controller for DialogView *myDialog;)
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"DialogView" owner:myDialog options:nil];
myDialog = [nibViews objectAtIndex:0];
[self.view addSubview:myDialog];
problem is i'm trying to pass owner:myDialog which is nil as it hasn't been instantiated...i could pass owner:self but that would make my view controller the File's Owner and that's not how that dialog view is wired in IB.
So that leads me to think this dialog wants to be another full blown UIViewController... But, from all I've read you should only have ONE UIViewController per screen so this confuses me because I could benefit from viewDidLoad, etc. that come along with view controllers...
Can someone please straighten this out for me?
There is no such thing as a view controller being on the screen; its view is on the screen. With that said, you can present as many views as you want on the screen at once.
I would create a new view and view controller. You would not make a UIView be a UITableViewDelegate, you make a UIViewController be a UITableViewDelegate. But instead of doing that manually, instead make your new view controller a subclass of UITableViewController, if you're using iPhone OS 3.x+. You can then present this view controller modally.
You probably want to give the user a chance to cancel out of the selection. A good way to do that is to wrap your new dialog view controller in a UINavigationController and then put a "Cancel" button in the nav bar. Then use the delegate pattern to inform the parent view controller that the user has made their choice so you can pop the stack.
Here's what the code will look like inside your parent view controller, when you want to present this option dialog:
- (void)showOptionView
{
OptionViewController* optionViewController = [[OptionViewController alloc] initWithNibName:#"OptionView" bundle:nil];
optionViewController.delegate = self;
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:optionViewController];
[self.navigationController presentModalViewController:navController animated:YES];
[navController release];
[optionViewController release];
}
Your OptionViewController .h will look like this:
#protocol OptionViewControllerDelegate;
#interface OptionViewController : UITableViewController
{
id<OptionViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id<OptionViewControllerDelegate> delegate;
#end
#protocol OptionViewControllerDelegate <NSObject>
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSString*)selection;
// or maybe
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSUInteger)selection;
// etc.
#end
Your OptionViewController.m will have something like this:
- (void)madeSelection:(NSUInteger)selection
{
[delegate OptionViewController:self didFinishWithSelection:selection];
}
Which has a matching method back in your original view controller like:
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSUInteger)selection
{
// Do something with selection here
[self.navigationController dismissModalViewControllerAnimated:YES];
}
There are plenty of examples throughout Apple's sample source code that follow this general pattern.
In my app I have a drill-down type interface as follows:
My root view, which has a list of items and an "Add" button.
Selecting an item pushes the "Detail" view on the navigationController.
Selecting the "Add" button pushes an "Add" view.
How would I transition between the Add view to the Detail view?
I'm thinking of doing an unanimated "pop" on the Add view and push the Detail controller on, but how do I make the second part animated, so the detail view would either slide in vertically or fade in from the Add view?
Thanks,
Kelso
Based on Ramin's reply, you can try this way, which is used by Apple in several samples:
MyListViewController.h
#import "MyAddViewController.h"
#interface MyListViewController : UITableViewController <MyAddViewControllerDelegate> {
}
- (IBAction)add:(id)sender;
#end
MyListViewController.m
// Action for bring up add view controller
- (IBAction)add:(id)sender {
MyAddViewController *addViewController = [[MyAddViewController alloc] initWithStyle:UITableViewStyleGrouped];
addViewController.delegate = self;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:addViewController];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[addViewController release];
}
// MyAddViewController's delegate method, dismiss the add view controller in here
- (void)addViewController:(MyAddViewController *)addViewController didAddData:(MyData *)data{
if (data) {
MyDetailViewController *detailViewController = [[MyDetailViewController alloc] initWithStyle:UITableViewStylePlain];
detailViewController.data = data;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
[self dismissModalViewControllerAnimated:YES];
}
MyAddViewController.h
#protocol MyAddViewControllerDelegate;
#class MyData;
#interface MCCourseAddTableViewController : UITableViewController {
#private
MyData *data;
id <MCCourseAddDelegate> delegate;
}
// MyData could be NSManagedObject if you want to use Core Data
#property(nonatomic, retain) MyData *data;
#property(nonatomic, assign) id <MyAddViewControllerDelegate> delegate;
- (void)save;
- (void)cancel;
#end
#protocol MyAddViewControllerDelegate <NSObject>
#optional
- (void)addViewController:(MyAddViewController *)addViewController didAddData:(MyData *)data;;
#end
MyAddViewController.m
- (void)save {
if (self.delegate != nil) {
if ([self.delegate conformsToProtocol:#protocol(MyAddViewControllerDelegate)]){
if ([self.delegate respondsToSelector:#selector(addViewController:didAddData:)]){
// Send data back to List View, to bring up detail view controller and dismiss add view controller
[self.delegate addViewController:self didAddData:self.data];
}
}
}
}
- (void)cancel {
if (self.delegate != nil) {
if ([self.delegate conformsToProtocol:#protocol(MyAddViewControllerDelegate)]){
if ([self.delegate respondsToSelector:#selector(addViewController:didAddData:)]){
// Send nil back to ListView, to dismiss the add view controller only
[self.delegate addViewController:self didAddData:nil];
}
}
}
}
You could push the Add View onto the Details view and set a variable to remember that.
When the user try to pop the Add View, you check the previous variable and, if set, you pop directly to the root controller(that should pop the Details view automatically).
Marco
Make the add view a modal and in there provide a "Done" (or "Save") and a "Cancel" button. If the user hits cancel, you just close the modal. If they hit Done you save the new record to the table model, then do a table reload on the root view before returning. To be nice, you can flash the newly added item.
A clean way to set this up is to make the modal controller implement a delegate that expects a 'Done' protocol method and have the root controller implement it and set itself as the delegate.
This way, the root controller is notified when the user hits 'Done' so it can encapsulate all that needs to happen. If you want to go directly from add to detail view the delegate method can do a 'push' for the newly added record and you'll get a nice transition from modal sliding down to detail view.