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.
Related
I'm still learning objective-c and having a difficult time dismissing presented VCs from presenting VCs.
I read that to achieve this, you establish delegates to send the right messages back to the presenting VC from the presented VC.
My storyboard looks like so:
The issue I have is if I click UIBUtton2, it doesn't go back to Main VC. In fact, it does nothing.
However, clicking on any cells from VC1 segue to VC2 and clicking on UIButton3 transition back to VC1 as achieved.
MainVC.h:
#import "VC1.h"
#interface MainVC : UIViewController <VC1Delegate>
....
MainVC.m:
- (void)didGoBackToMainVC
{
[self dismissViewControllerAnimated:YES completion:nil];
}
....
VC1 .h:
#import "VC2.h"
#protocol VC1Delegate <NSObject>
#required
- (void)didGoBackToMainVC;
#end
#interface VC1 : UIViewController <UITableViewDataSource, UITableViewDelegate,
VC2Delegate>
#property (weak, nonatomic) id <VC1Delegate> delegate;
- (IBAction)UIButton2:(UIButton *)sender;
VC1.m:
- (void)didGoBackToVC1
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)UIButton2:(UIButton *)sender
{
[self.delegate didGoBackToMainVC];
}
VC2.h:
#protocol VC2Delegate <NSObject>
#required
- (void)didGoBackToVC1;
#end
#interface VC2 : UIViewController
#property (weak, nonatomic) id <VC2Delegate> delegate;
- (IBAction)UIButton3:(UIButton *)sender;
VC2.m:
- (IBAction)UIButton3:(UIButton *)sender
{
[self.delegate didGoBackToSponsors];
}
I'm sure I'm not understanding this relationship properly. Can someone tell me what I'm doing wrong?
Thanks
From what I understand in your comments above I saw that your Main VC shows VC1 Modally correctly.
So I guess you have this code in your VC1.m
- (IBAction)goToMainVC:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Next make sure your Cell in VC1 has 'Triggered Seques' Selection type of 'Show'. And you probably had this code in your VC1.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"pushVC2"]) {
VC2 *vc2 = (VC2 *)segue.destinationViewController;
}
}
Next in your VC2.m you also should have this
- (IBAction)dismissVC2:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
If these are all you have, it should work like a charm. And no Delegates needed in this case. Hope this helps.
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 a publicly declared NSString, NSString *characterString in my mainViewController.m and just before I perform a [self performSegueWithIdentifier:#"segueToFinal" sender:self]; I update this characterString with the latest data. like so:
characterString=[NSString stringWithFormat:#"final string %#",truncatedString];
[self performSegueWithIdentifier:#"segueToBlueprints" sender:self];
When the new view controller finishes appearing, the user will press a button some time that will call a method that will want the characterString and possibly other publicly declared instance variables from that previous view controller. I was trying [[mainViewController alloc]getCharacterString] (with getCharacterString being a method implemented in mainViewController) but that of course creates a new instance of mainViewController and does not solve the issue.
How Can I access the data currently in 'characterString' and other variables from the old view controller?
Keep all you global variables in you AppDelegate:
YourAppDelegate.h
#interface BTAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSString * characterString;
#end
YourViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
YourAppDelegate* app = (YourAppDelegate*)[UIApplication sharedApplication].delegate;
app.characterString = #"Hello";
}
YourSecondViewController.m
- (IBAction)pressButton:(id)sender{
YourAppDelegate* app = (YourAppDelegate*)[UIApplication sharedApplication].delegate;
app.characterString = #"Hello2";
}
etc.
Try this out :
#interface NewVC :UIViewController
#property (nonatomic) NSString *newCharacterString;
#end
Now in the mainViewController import the NewViewController class :
#import "NewViewController.h"
.......
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NewViewController *destinationViewController = [segue destinationViewController];
destinationViewController.newCharacterString = self.characterString;
}
and you can access the value of the character string in your NewViewController through the newCharacterString property.
HTH :)
The only thing I can think of is passing the old data from the view controller along with the segue. Or, you could even pass the entire old view controller if you have multiple properties you want to keep.
In the new VC (the one being segued to), add a property that you want to keep from the old VC (the one being segued from) such as the NSString *.
#interface NewVC :UIViewController
#property (nonatomic) NSString *newCharacterString;
#end
Then, while you are segue - ing, just pass the value to the new VC.
characterString=[NSString stringWithFormat:#"final string %#",truncatedString];
newViewControllerIWantToPassAValueTo.newCharacterString = characterString;
// passes new string forward to View Controller
[self performSegueWithIdentifier:#"segueToBlueprints" sender:self];
Why you dont pass this characterString to the secondViewController
In SecondViewController:
#interface SecondViewController:UIViewController
#property (strong, nonatomic) NSString *newCharacterString;
#end
In MainViewController:
[self performSegueWithIdentifier:#"segueToBlueprints" sender:self];
And you have to add the preparePerform Segue to MainViewController
- (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"])
{
SecondViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
vc.newCharacterString = self.characterString;
}
}
And you could use newCharacterString in SecondViewController without callback to the MainViewController
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 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);
}