I have two view controllers. MainViewController pushes SignUp ViewController. Once SignUp View Controller dismisses, I need access to the same instance of MainViewController to update a UIButton.title.
.h MainView
//MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UITableViewController
-(void)loggedIn;
#end
.m
#interface MainViewController ()
#end
-(void)loggedIn
{
NSLog (#"This is Logged in inside MainView.m");
self.logInOutButton.title = #"Logout";
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
Signup *signUp = [destinationViewController isKindOfClass:[SignUp class]] ? (Signup*)destinationViewController : nil;
signUp.mainViewController = self;
NSLog(#"Preapre For Segue %#", self);
}
.h SignUp
#import <UIKit/UIKit.h>
#import "MainViewController.h"
#interface SignUp : UIViewController
#property (strong, nonatomic) MainViewController *mainViewController;
#end
.m
#synthesize mainViewController;
- (void) loggedIn
{
NSLog(#" MainViewController %#", mainViewController);
[mainViewController loggedIn];
self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
When I do NSLog I'm getting different values:
Preapre For Seg <STMainViewController: 0x9f7d600>
MainViewController (null)
Create a protocol / delegate. There are numerous resources on how to achieve this. In summary, you create the protocol on the destination viewController with the method you wish to run on the source and then you subscribe to that delegate from the source viewController.
Protocol Delegate
Dont forget to set the delegate from your prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
Signup *signUp = segue.destinationViewController;
signup.delegate = self;
NSLog(#"Preapre For Segue %#", self);
}
Related
I have two view controllers in my project ViewController and SecondViewController and my ViewController has a button which performs a popover segue to SecondViewController then in my SecondViewController I have a button which I would like to use to a call a method from ViewController and I assumed it was possible using [self presentingViewController] but that pointer is set to nil so I'm not quite sure how to call a method in the first ViewController inside the SecondViewController after a popover segue.
This is the code for my ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
- (IBAction)dosegue:(id)sender;
-(void)tobecalledfromseguepopover;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
}
-(void)tobecalledfromseguepopover{
NSLog(#"Called from segue popover");
}
- (IBAction)dosegue:(id)sender {
[self performSegueWithIdentifier:#"segueforpopup" sender:self];
}
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface SecondViewController : UIViewController
- (IBAction)returntocaller:(id)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController()
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)returntocaller:(id)sender {
[self dismissViewControllerAnimated:YES completion:^{
ViewController* caller = (ViewController*)[self presentingViewController];
if(caller!=nil){
[caller tobecalledfromseguepopover];
}else{
NSLog(#"nil");
}
}];
}
#end
My UI
So any ideas on how I can call tobecalledfromseguepopover inside SecondViewController after the segue? Also is there a way for SecondViewCOntroller to know it's being called from a segue popover?
The usual thing is a delegate architecture. Give the popover a delegate property and set it to self in your prepareForSegue. Now the popover knows where to find the first view controller.
This is often combined with a protocol so that the popover knows the name of the method to be called without worrying about the class of the first view controller, but that part isn’t crucial, especially in Objective C.
I tried many custom delegate examples in my code to send data from view B back to parent view A. Why am I getting NULL? There should be something I am missing. Please help. How can I call setupDate to receive (NSDate *)setAlarmDate in Parent view?
Parent view
#import "SetupViewController.h"
#interface ViewController : UIViewController <SetupViewControllerDelegate> {
NSTimer *timer;
}
#end
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"setupview"]) {
NSLog(#"prepare for segue");
SetupViewController *svc = [[SetupViewController alloc] init];
svc.delegate = self;
-(void)setupDate:(NSDate *)setAlarmDate{
NSLog(#"Hey Hey");
NSLog(#"%#", setAlarmDate);
}
#end
View B
#protocol SetupViewControllerDelegate <NSObject>
#required
-(void)setupDate:(NSDate *)setAlarmDate;
#end
#interface SetupViewController : UIViewController
#property (nonatomic, strong) id <SetupViewControllerDelegate> delegate;
//IB
#property (weak, nonatomic) IBOutlet UIDatePicker *pDatePicker;
#end
.m
#import "SetupViewController.h"
#import "ViewController.h"
#implementation SetupViewController
-(void)viewWillDisappear:(BOOL)animated{
NSDate *date = [self.pDatePicker date];
[[self delegate] setupDate:date];
}
#end
You should access the UIViewController using the segue's destinationViewController property instead of allocating an instance of your own, which is why your alarmDate = NULL.
E.g.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"setupview"])
{
NSLog(#"prepare for segue");
SetupViewController *svc = [segue destinationViewController];
svc.delegate = self;
}
}
Also, as #Neru mentioned your delegate should be qualified as weak and not strong. The reason for this is because you will cause a retain cycle if you do not explicitly set the delegate = nil in the dealloc method.
I have my first viewController "SFCrearMomentoViewController" and the second view is a tableViewController "MasterViewController". I need to select a row didSelectRowAtIndexPath and call the method didCelected which is implemented in my "first viewController" and close the view using dismissViewControllerAnimated.
The problem is that the method didCelected is never called. I've already tested this code in a test project using "two viewControllers" and it works but I don't know what's the problem in my current project.
SFCrearMomentoViewController.h
...
#import "MasterViewController.h"
#interface SFCrearMomentoViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextViewDelegate, MasterViewControllerDelegate>{
UIImagePickerController *picker;
}
...
SFCrearMomentoViewController.m
...
-(void)didSelected:(NSString *)nombre{
NSLog(#"didSelected method %#", nombre);
}
#end
MasterViewController.h
#import <UIKit/UIKit.h>
#protocol MasterViewControllerDelegate <NSObject>
#required
-(void) didSelected:(NSString *)nombre;
#end
#interface MasterViewController : UITableViewController
#property (weak, nonatomic) id <MasterViewControllerDelegate> delegate;
#end
MasterViewController.m
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.delegate didSelected:#"row selected"];
[self dismissViewControllerAnimated:YES completion:nil];
}
...
Solution: I had to set the delegate self. I implemented it in the Segue.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.destinationViewController isKindOfClass:[MasterViewController class]]) {
MasterViewController *nextView = segue.destinationViewController;
nextView.delegate = self;
}
}
I had the same problem a few hours ago. In my case I set the delegate in prepareForSegue to the segue.destinationViewController but my destination view controller wasn't the View Controller with the delegate, it was a UINavigationController.
So I used this one:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"modalNeuerRaum"])
{
[(MyCustomModalViewController*)[(UINavigationController*)segue.destinationViewController viewControllers][0] setDelegate:self];
}
}
That worked for me, but I don't know if you have a UINavigationController..
You need to set the delegate in SFCrearMomentoViewController, you can do it in viewDidLoad, for example.
masterViewController.delegate = self;
I assume that you allocated and initialised new masterViewController object of MasterViewController type.
Using a storyboard I've set up two different view controllers (A&B) where one of them (B) have a UITextView.
How can I access and change the text in the textview from View Controller A?
The code below doesn't give any errors but the text isn't set, even though the function is called correctly.
If I run [self.desc setText:text]; inside viewDidLoad in ViewControllerB it works.
ViewControllerA.h
#interface ViewControllerA : UIViewController
{
}
#end
ViewControllerA.m
#import "ViewControllerA.h"
#import "ViewControllerB.h"
#interface ViewControllerA ()
#end
#implementation ViewControllerA
- (void)viewDidLoad
{
[super viewDidLoad];
ViewControllerB *bInstance = [[ViewControllerB alloc] init];
[bInstance setDescription:#"this is some new text";
}
#end
ViewControllerB.h
#interface ViewControllerB : UIViewController
{
UITextView *desc;
}
- (void)setDescription:(NSString *) text;
#property (nonatomic, retain) IBOutlet UITextView *desc;
#end
ViewControllerB.m
#import "ViewControllerB.h"
#interface ViewControllerB ()
#end
#implementation ViewControllerB
#synthesize desc;
- (void)setDescription:(NSString *)text{
NSLog(#"called!");
[self.desc setText:text];
}
#end
You should not manipulate another view controller's views, ever. It is bad design in general, and often doesn't work at all (as you've discovered.)
Instead, set up a string property in your second view controller. Set that (in prepareForSegue, if you're using storyboards.)
Then, in your second view controller's viewWillAppear, put the string into the text field.
Do the following:
#interface ViewControllerA : UIViewController
{
UITextView *desc;
#public
ViewControllerB objB;
}
When you launch:
ObjectOfAToLaunch.objB = ObjectOfBToLaunch;
In view did load
- (void)viewDidLoad
{
[super viewDidLoad];
[Objb setDescription:#"this is some new text"];
}
Good Luck.
You can implement prepareForSegue method in the controller that launch A and B.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
UIViewController *vc = [segue destinationViewController];
if ([vc isKindOfClass:ViewControllerB])
{
ObjB = vc; // ObjB is a public member you must declare in interface
if (ObjA!=nil)vc->ObjA = ObjB;
}
if ([vc isKindOfClass:ViewControllerA])
{
ObjA = vc; // ObjA is a public member you must declare in interface
if (ObjB!=nil)vc->ObjA = ObjB;
}
}
}
Good Luck.
bInstance is another instance of the class. not the same one.
I have a ViewController1 with a UILabel that can present a ViewController2 with a Modal Segue. I have a UITextField in ViewController2 that I need to access from ViewController1 so I can set my UILabel with the collected text.
I've tried working with the prepareForSegue without success. What should I do?
EDIT:
I'm using a Delegate, but I'm doing something wrong. Here's the code I'm using in ViewController2.h:
#class ViewController2;
#protocol VCProtocol
-(void)setName:(NSString *)name;
#end
#interface ViewController2 : UIViewController
#property (nonatomic, weak) id<VCProtocol> delegate;
#property (strong, nonatomic) IBOutlet UITextField *nameField;
- (IBAction)setButton:(id)sender
#end
ViewController2.m
-(IBAction)setButton:(id)sender
{
[self.delegate setName:nameField.text];
}
I conform to VCProtocol in my ViewController1.h. Then, in my ViewController1.m, I have this code:
- (void)setName:(NSString *)name
{
self.firstSignatureNameLabel.text = name;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqual:#"Sign"])
{
ViewController2 *VC = [segue destinationViewController];
VC.delegate = self;
}
}
You can create a protocol and set VC1 as delegate of VC2, and using prepareForSegue to set VC1 as VC2's delegate should work. I know that you said it didn't work, but I can't see why. Have a try with this :
Give an identifier to your segue (on storyboard), and implement prepareForSegue as shown below :
VC2Delegate.h :
#protocol VC2Delegate
- (void)updateLabel:(NSString *)text;
#end
VC1.h :
#import "VC2Delegate.h"
#interface VC1 : UIViewController <VC2Delegate>
// All your stuff
#end
VC1.m :
- (void)updateLabel:(NSString *)text {
[_label setText:text];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"YourSegueIdentifier"]) {
VC2 * vc = [segue destinationViewController];
[vc2 setDelegate:self];
}
}
VC2.h :
#import "VC2Delegate.h"
#interface VC2 : UIViewController
#property (weak, nonatomic) id<VC2Delegate>delegate;
#end
VC2.m
- (void)textWasUpdated { // or whatever method where you detect the text has been changed
if (_delegate)
[_delegate updateLabel:[_textView text]];
}
Tell me if it works. Else, is prepareForSegue even been called ?
EDIT : Updated my answer (wasn't exactly what you needed).
But as you say it doesn't work :
Is prepareForSegue called ?
If so, is the delegate method called ?
If the delegate method isn't called, check that delegate is not nil.
You might want to remove the segue, and present it modally by yourself, using presentViewController:animated:completion:, like that :
- (IBAction)buttonWasTapped {
static NSString * const idModalView = #"modalView";
static NSString * const storyBoardName = #"MainStoryBoard"
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyBoardName bundle:nil];
VC2 * vc = [storyboard instantiateViewControllerWithIdentifier:idModalView];
[vc setDelegate:self];
[self.navigationController presentViewController:vc2 animated:YES completion:nil];
}
When I ran into this problem, I chose the singleton approach and it works.