I am using this code to push a view controller in ViewControllerA:
DatePickerViewController *viewController = [[DatePickerViewController alloc] initWithNibName:#"DatePickerViewController" bundle:nil];
NSMutableArray *info = [[NSMutableArray alloc] initWithObjects:#"Exam Duration",nil];
[viewController setInfo:info];
[info release];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
In the DatePickerViewController that is slid in, it has a text field. In ViewControllerA how can I get the value of this text field once the viewWillDisappear method is fired, it's to save data.
There are several ways to do this. Best would be to use a delegate pattern or a notification system.
Which one you choose depends on your whishes. If you just want the 'parent controller' to know about the value, use a delegate method.
Using delegates or notifications creates a loosely coupled structure and makes reusable classes possible.
EDIT: Some sample code. Beware: untested and from the top of my head!
DatePickerViewController.h
#protocol DatePickerViewControllerDelegate
-(void)datePicker:(DatePickerViewController *)picker pickedDate:(NSDate *)date;
#end
DatePickerViewController.m
- (void)viewWillDisappear:(BOOL)animated {
[self.delegate datePicker:self pickedDate:theDate]; //Delegate is an ivar
[super viewWillDisappear:animated];
}
ViewControllerA.m
//
DatePickerViewController *viewController = [[DatePickerViewController alloc] initWithNibName:#"DatePickerViewController" bundle:nil];
NSMutableArray *info = [[NSMutableArray alloc] initWithObjects:#"Exam Duration",nil];
[viewController setInfo:info];
[info release];
[viewController setDelegate:self]; // Set the delegate of the date picker
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
//
-(void)datePicker:(DatePickerViewController *)picker pickedDate:(NSDate *)date; {
//Deal with the new date here
}
Related
I'm using delegation to try to change the text from a UILabel in ViewController2.
In ViewController1.h I have:
#protocol WelcomeDelegate
-(void) updateLabelWithString:(NSString*)string;
#end
#interface ViewController1 : UIViewController
#property (weak, nonatomic) id<WelcomeDelegate>delegate;
Inside ViewController1.m:
- (void)presentWelcomeAlert {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2* contentVC = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2ID"];
[self.delegate updateLabelWithString:[NSString stringWithFormat:#"Welcome to the 11Health Family %#! We are here to accompany and support you through the journey as an ostomate. Our new Hydration Tracker is ready to follow your hydration levels. Tap 'Continue' to begin!", [dcsContext sharedContext].activeParticipant.firstName]];
UIViewController *rootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
[rootVC presentViewController:contentVC animated:YES completion:nil];
}
Inside ViewController2.h I have:
#import "SignInViewController.h"
#interface WelcomePopoverViewController () <UIViewControllerTransitioningDelegate>
{
SignInViewController *signInViewController;
}
In the viewDidLoad:
viewController1 = [[ViewController1 alloc] init];
[viewController1 setDelegate:self];
My method in ViewController2.m:
- (void)updateLabelWithString:(NSString *)string {
welcomeLabel.text = string;
}
My issue is that the method above isn't getting called even when I call it from the first view controller.
This is a bit messy, sorry to say.....and you are re-instantiating ViewController1 in ViewController2....
If you only need to set that label once, then ditch all the delegate/protocol stuff and just call the method directly:
- (void)presentWelcomeAlert {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2* contentVC = [storyboard instantiateViewControllerWithIdentifier:#"ViewController2ID"];
UIViewController *rootVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
[rootVC presentViewController:contentVC animated:YES completion:^ {
[contentVC updateLabelWithString:[NSString stringWithFormat:#"Welcome to the 11Health Family %#! We are here to accompany and support you through the journey as an ostomate. Our new Hydration Tracker is ready to follow your hydration levels. Tap 'Continue' to begin!", [dcsContext sharedContext].activeParticipant.firstName]];
}];
}
and then of cause expose that method in ViewController2.h:
-(void) updateLabelWithString:(NSString*)string;
You can directly call this method no need to create delegate.
ViewController2 *obj=[[ViewController2 alloc] init];
[obj updateLabelWithString:#"title"];
[self.navigationController pushViewController:obj animated:YES];
I was test the delegate pass the value in objective-c.
I know there are other methods can pass string between UIViewControllers like NSNotifyCenter..etc.
Now I want to try to use the delegate pass value .
But I encounter some problems.
I use the navigation and there have two UIViewController(FirstUIViewcontroller and SecondUIViewController).
Now I want to use manual to change to SecondUIViewController,not use the button drag to the SecondUIViewController at FirstUIViewController.
So I add the code in the FirstUIViewController.m button action.
- (IBAction)pushBtnAction:(id)sender {
SecondViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self.navigationController pushViewController:controller animated:YES];
}
Then I want to pass the value from the SecondUIViewcontroller when I pop the view controller.
So I add the delegate implement and se the delegate in the FirstUIViewController.m.
- (void)viewDidLoad {
[super viewDidLoad];
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
secondVC = (SecondViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
secondVC.delegate = self;
}
-(void) passSecondVC:(SecondViewController *)vc didAddValue:(NSString *)str
{
NSLog(#"second str:%#",str);
}
In the SecondUIViewController.h , I had declare delegate method.
#class SecondViewController;
#protocol SecondViewControllerDelegate <NSObject>
#optional
-(void)passSecondVC:(SecondViewController*)vc didAddValue:(NSString*) str;
#end
#interface SecondViewController : UIViewController
#property (nonatomic,assign) id<SecondViewControllerDelegate> delegate;
- (IBAction)passValueDelegatBtnAction:(id)sender;
In the SecondViewController.m ,
when I click the button will pop self uiviewcontroller and pass the value to FirstUIViewController.
- (IBAction)passValueDelegatBtnAction:(id)sender {
if( [self.delegate respondsToSelector:#selector(passSecondVC:didAddValue:)])
{
[self.delegate passSecondVC:self didAddValue:#"this is string from sencond vc"];
}
[self.navigationController popViewControllerAnimated:YES];
}
(My problems)
But in this status , I always can't get the value in the delegate method in the FirstUIViewController.
I had try to other method like below in the FirstViewController.m
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"segue");
id vc = segue.destinationViewController;
if( [vc isKindOfClass:[SecondViewController class]])
{
SecondViewController *secondVC = vc;
secondVC.delegate = self;
}
}
There are same problem.
I can't get the value from the delegate method.
Have anyone know where the problems?
I post my completely code in here.
Thank you very much.
Alright!
Remove your code from the viewDidLoad: method and set the delegate when you push the secondViewController.
- (IBAction)pushBtnAction:(id)sender {
SecondViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[controller setDelegate:self];
[self.navigationController pushViewController:controller animated:YES];
}
So what was going wrong?
Ans: You create a new object in your viewDidLoad: method and set your firstViewController as delegate to it. Now while pushing you are creating another object of SecondViewController whose delegate is not set.
I downloaded your code & fixed it.
You don't want this line. so comment it,
// UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
// secondVC = (SecondViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
// secondVC.delegate = self;
Edit this method as below,
- (IBAction)pushBtnAction:(id)sender {
SecondViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
controller.delegate = self;
[self.navigationController pushViewController:controller animated:YES];
}
Also edit this method as below,
- (IBAction)passValueDelegatBtnAction:(id)sender {
// if( [self.delegate respondsToSelector:#selector(passSecondVC:didAddValue:)])
// {
[self.delegate passSecondVC:self didAddValue:#"this is string from sencond vc"];
// }
[self.navigationController popViewControllerAnimated:YES];
}
you forgot some code in pushBtnAction..
- (IBAction)pushBtnAction:(id)sender {
SecondViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"]; // It's different to VC you init in viewDidLoad
controller.delegate = self; // you forgot....
[self.navigationController pushViewController:controller animated:YES];
}
prepareForSegue be call when you use segue to navigation.
I am trying to show a image picker view controller from a NSObject subclass but when i call presentViewController on the app's root vc the app crashes without a error message.
My code is here:
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
UIViewController *root = [[[[UIApplication sharedApplication] windows] objectAtIndex:0] rootViewController];
[root presentViewController:picker animated:YES completion:nil];
If i run this in a view controller it just works fine.
EDIT:
I can't use a delegate because i am building a plugin for Unity and i can't get the view controller in other ways than this.
I noticed the crash while calling this code in the current view controller from a IBAction:
- (IBAction)showPicker:(id)sender {
PhotoManager *manager = [[PhotoManager alloc] init];
[manager loadPhoto];
}
In answer to the comments here's the class code:
PhotoManager.h:
#interface PhotoManager : NSObject <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
- (void)loadPhoto;
#end
.m:
#implementation PhotoManager {
UIViewController *root;
}
- (void)loadPhoto {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
root = [[[[UIApplication sharedApplication] windows] objectAtIndex:0] rootViewController];
[root presentViewController:picker animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[root dismissViewControllerAnimated:YES completion:nil];
NSLog(#"%#", info);
}
#end
The proper way would be to create a protocol / delegate and call back to the presenting viewController telling it to present the picker. This allows you to do much more and is a great habit to learn right away IMO.
How do I set up a simple delegate to communicate between two view controllers?
I'm using ICETutorial with cocoapods.
I'm using it in a SettingsViewController where you can view the tutorial in settings.
// SettingsViewController.m
Tutorial2ViewController *vc = [[Tutorial2ViewController alloc] init];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController pushViewController:vc animated:NO];
And Tutorial2ViewController inherits from ICETutorialController
#interface Tutorial2ViewController : ICETutorialController
ICETutorialPages have buttons that will trigger a callback. It takes in a block. So in my implementation, I have this:
- (id)init
{
ICETutorialPage *layer1 = [[ICETutorialPage alloc] initWithSubTitle:#"Page 1" description:#"Page 1" pictureName:#"Tutorial1_640x1136.png"];
NSArray *tutorialLayers = #[layer1];
self = [super initWithNibName:#"ICETutorialController_iPhone" bundle:nil andPages:tutorialLayers];
__weak Tutorial2ViewController *vc = self;
[self setButton1Block:^(UIButton *button){
NSLog(#"Button 1 pressed.");
[[vc.navigationController topViewController] dismissViewControllerAnimated:NO completion:nil];
}];
if (self != nil)
{
}
return self;
}
The reason why I put all the code in init is that I don't want SettingsViewController to know anything about how the Tutorial2ViewController works. Settings should alloc and init, push to the navigation controller stack and the Tutorial2ViewController should know how to handle itself.
I do get the NSLog that button1 is pressed but the view controller does not dismiss itself and return me to the SettingsViewController.
I will contact the creator of the library and ask him/her to see this question also. I feel that this is me not misunderstanding blocks, navigation controllers, cocoapods, etc...
Thanks
Just try [self.navigationController popViewControllerAnimated:YES];
I have a View Controller which displays a table view. The VC calls another VC to display the send SMS view to the user, the code for this SMS VC is:
- (void) sendSMSWithBody: (NSString*) body andRecipients: (NSArray*) recipients
{
MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init];
if ([MFMessageComposeViewController canSendText])
{
controller.messageComposeDelegate = self;
controller.body = body;
controller.recipients = recipients;
[[UIApplication sharedApplication].delegate.window.rootViewController addChildViewController:self];
[self presentModalViewController:controller animated:YES];
}
}
- (void) messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissModalViewControllerAnimated:YES];
[[UIApplication sharedApplication].delegate.window.rootViewController removeFromParentViewController];
}
(I know the call to sharedApplication is a bit hacky, but it will suffice for now. the rootViewController is a UINavigationController which has its root controller set to the table view controller)
I am invoking the SMS VC from the table VC like so:
- (void ) viewDidAppear:(BOOL)animated
{
static BOOL presentedSMSVC = NO;
if (!presentedSMSVC)
{
SendSMSController *sendSMS = [[SendSMSController alloc] init];
[sendSMS sendSMSWithBody:#"body"
andRecipients:[NSArray arrayWithObject:#"123456789"]];
presentedRegisterVC = YES;
}
}
The problem is that after the user sends the SMS the table view cells are not displaying.
I thought maybe I need to refresh the view/table so I added a protocol callback from the 2nd VC to the first which gets invoked when the user sends the SMS, and then within the callback call [self.tableView reloadData] But it made no difference.
So I got rid of the intermediary class and edited the table view to display the SMS view directly like this:
- (void ) viewDidAppear:(BOOL)animated
{
static BOOL presentedRegisterVC = NO;
if (!presentedRegisterVC)
{
MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init];
if ([MFMessageComposeViewController canSendText])
{
controller.messageComposeDelegate = self;
controller.body = #"body";
controller.recipients = [NSArray arrayWithObject:#"12345678"];
[self presentModalViewController:controller animated:NO];
}
}
}
- (void) messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
[self dismissModalViewControllerAnimated:NO];
}
But now the MFMessageComposeViewController doesn't dismiss (although messageComposeViewController:didFinishWithResult: does get called)
What is the problem with both approaches? Thanks
For the second variant, I changed to:
[self presentViewController: controller animated: YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
And that worked, haven't tried applying to the first method.
I faced a similar UI issue.
My case was: The controller, let's say controller A, in which I had written the code to present and dismiss the MFMessageComposeController, was not being used directly as the active controller rather I was using A.view as a subview over another controller, say controller B. So writing the following was distorting the view:
[self presentViewController:composeVC animated:YES completion:nil];
But then writing the following solved my issue:
[self.customTabbarNavigation presentViewController:composeVC animated:YES completion:nil];
The customTabbarNavigation, was the controller B, ans was actually the navigation controller which was active when controller A's view was visible.
Same change had to be made for dismissing the composeVC.