from second ViewController to first ViewController - iphone

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];

Related

Parent View Controller and Model View Controller dismissal detection not working

I have a UIViewController called ShowListViewController that uses a Modal View Controller to push another view onto the stack:
AddShowViewController *addShowViewController = [[AddShowViewController alloc] init];
[addShowViewController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:addShowViewController animated:YES];
I would then like to call my method populateTableData of the ShowListViewController class when the addShowViewController disappears.
I would think that the answer found here would work, but it doesn't. My method populateTableData is not detected as an optional method to use.
Essentially my questions is: How do I detect when a Modal View Controller disappears so as to call a method within the class that pushed it on the stack?
This may not be a best solution, but can do what you want at this time.
In your showlistcontroller add an instance variable like
BOOL pushedView;
#implementation ShowListViewController
and before you do the modal presentation set its values as YES like
pushedView = YES;
[self.navigationController presentModalViewController:popView animated:YES];
in the viewWillAppear of ShowListViewController you can detect whether it is appearing because pop getting dismissed or not like
if (pushedView) {
NSLog(#"Do things you would like to on pop dismissal");
pushedView = NO;
}
I think you would like something like this.
You make a delegate inside ur modalVC like this:
#protocol ModalViewDelegate <NSObject>
- (void)didDismissModalView;
#end
and implement it in your MainVC like this:
#interface MainViewController : UIViewController <ModalViewDelegate>
{
Then u will make a delegate property in your modalVC like this:
#interface ModalShizzle : UIViewController
{
id<ModalViewDelegate> dismissDelegate;
}
You set the dismissDelegate of your ModalVC to your MainVC and then you make the delegate method. Before you dismiss it however you will call the ModalVC to do one last thing. (which is populate your table). You will call for the data inside your MainVC and then do whatever you feel like it, just before you dismissed your modalVC.
-(void)didDismissModalView
{
//call ModalVC data here...
//then do something with that data. set it to a property inside this MainVC or call a method with it.
//method/data from modalVC is called here and now u can safely dismiss modalVC
[self dismissModalViewControllerAnimated:YES];
}
Hope it helps ;)
OK so it appears that in Apple's template for Utility App's they ignore what the docs for [UIViewController][1] say and actually go out of their way to call dismissModalViewControllerAnimated: from the UIViewController that pushed the modal view onto screen.
The basic idea in your case will be
Define a protocol for AddShowViewControllerDelegate
Make ShowListViewController implement this protocol
Call a method on the delegate to ask it to dimiss the modal view controller
For a full example just create a new project with Utility template and look at the source for FlipsideViewController and MainViewController
Here is an example adapted for your needs:
AddShowViewController.h
#class AddShowViewController;
#protocol AddShowViewControllerDelegate
- (void)addShowViewControllerDidFinish:(AddShowViewController *)controller;
#end
#interface AddShowViewController : UIViewController
#property (nonatomic, assign) id <AddShowViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
AddShowViewController.m
- (IBAction)done:(id)sender
{
[self.delegate addShowViewControllerDidFinish:self];
}
ShowListViewController.h
#interface ShowListViewController : UIViewController <AddShowViewControllerDelegate>
{
...
}
ShowListViewController.m
- (void)addShowViewControllerDidFinish:(AddShowViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
[self populateTableData];
}

Adding to table view using a modal view controller

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];

UIAlertView Question

I have a small doubt. I have a NSObject class where I am trying to display an alert view. So after the alert view is displayed when I tap on OK button I want to push a navigation controller onto the stack. Can I push a navigation controller from general NSObject class? Please let me know guys..thanks for your time..
This is the code..
- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)buttonIndex{
SettingsViewController *homeView = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
[self.navigationController pushViewController:homeView animated:NO];
[homeView release];
}
I am creating a property called navigationController of type UINavigationController and when I catch the error I am displaying an alert view and I am using above method to push the view controller but it doesn't work..
Yes and no... depending on how you have your application set up. To push views onto the navigation stack you need to have a navigation controller.
Does your NSObject have access to this navigation controller - you might have to set up a delegate method that gets called from your delegate view when the alert view delegate gets called in your NSObject.
I'm just wondering why you're displaying a UIAlertView in an NSObject, why aren't you displaying it in a UIView or a UIViewController?
CustomObject.h
#protocol CustomObjectDelegate<NSObject>
#optional
- (void)customObjectAlertViewDidClickOk;
#end
#interface CustomObject : NSObject <UIAlertViewDelegate>{
id<CustomObjectDelegate> delegate;
}
#property (nonatomic, assign) id<CustomObjectDelegate> delegate;
#end;
CustomObject.m
#synthesize delegate;
// then put this:
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
[delegate customObjectAlertViewDidClickOk];
}
Then your ViewController .h file needs to include the custom object and assign the delegate methods:
#include "CustomObject.h"
#interface MyViewController : UIViewController <CustomObjectDelegate> {
}
#end
and the .m viewDidLoad (or similar):
- (void)viewDidLoad{
CustomObject *obj = [[CustomObject alloc] init];
[obj setDelegate:self];
}
- (void)customObjectAlertViewDidClickOk{
AnotherViewController *page = [[AnotherViewController alloc] initWithNibName:nil bundles:nil];
[self.navigationController pushViewController:page];
}
Thats how I would do it - given I'm not too sure i understand quite what you're asking. :) thats all off the top of my head as well - so don't take it letter for letter, but you have the basis there to start off with. You can build on it. Look up #protocols and delegate methods, its all in there. :)

UIView notification when modal UIImagePickerController is dismissed?

Is there a way to call code when a modal view is finished dismissing?
EDIT:
I'm sorry, I didn't clarify earlier. I'm trying to dismiss a UIImagePickerController and then show a MFMailComposeViewController and attach the image data to the email. When I try to call
[self presentModalViewController: mailController]
right after
[self dismissModalViewController];
I get errors and such.
You use a delegate pattern for the modal view to inform whoever presented it when it's finished.
MyModalViewController.h:
#protocol MyModalViewControllerDelegate;
#interface MyModalViewController : UIViewController
{
id<MyModalViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id<MyModalViewControllerDelegate> delegate;
#end
#protocol MyModalViewControllerDelegate
- (void)myModalViewControllerFinished:(MyModalViewController*)myModalViewController;
#end
MyModalViewController.m:
#synthesize delegate;
// Call this method when the modal view is finished
- (void)dismissSelf
{
[delegate myModalViewControllerFinished:self];
}
ParentViewController.h:
#import "MyModalViewController.h"
#interface ParentViewController : UIViewController <MyModalViewControllerDelegate>
{
}
ParentViewController.m:
- (void)presentMyModalViewController
{
MyModalViewController* myModalViewController = [[MyModalViewController alloc] initWithNibName:#"MyModalView" bundle:nil];
myModalViewController.delegate = self;
[self presentModalViewController:myModalViewController animated:YES];
[myModalViewController release];
}
- (void)myModalViewControllerFinished:(MyModalViewController*)myModalViewController
{
[self dismissModalViewControllerAnimated:YES];
}
EDIT:
I haven't used UIImagePickerController, but looking at the docs, it looks like you already have most of the code done for you, as there is an existing UIImagePickerControllerDelegate class that has three different "dismissal" delegate callbacks (although one is deprecated). So you should make your ParentViewController class (whatever that is) implement the UIImagePickerControllerDelegate pattern and then implement those methods. While each method will do something different (since you have to handle when the user actually selects an image, or if they cancel), they each will do the same thing at the end: call dismissModalViewControllerAnimated: to dismiss the picker.
You have to dismiss the modalViewController somehow right? Either a UIButton, or by code:
- (void)dismissModalViewControllerAnimated:(BOOL)animated
In the IBAction (e.g. delegate) for the UIButton or in the method above, call whatever code you want.
I don't think there is a specific notification yet can subscribe to, to know when dismiss animation is done,...BUT. You can implement viewDidAppear: in the view controller that presented the modal view. This is what I do, when I use the (to UIImagePickerController quite similar) ABPeoplePickerNavigationController.
In the callback from people picker, I remember the person tapped in the picker on an instance variable, like this:
- (void)callbackFromModalView:(id)dataFromModalView {
// remember dataFromModalView as I need it when dismissed
self.dataFromModalView = dataFromModalView;
// now initiate dismissal
[self dismissModalViewControllerAnimated:YES];
}
then, in your view controller, implement this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.dataFromModalView) {
//...present now view here
// don't forget to reset this one
self.dataFromModalView = nil;
}
}
in effect, you are using the combination of viewWillAppear: and the dataFromModalView property as the "notification about modal view dismissed".

Transitioning between subviews in a navigationController?

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.