Accessing an instance variable from a different view controller - ios

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

Related

iOS7 Pass data backward by protocol at Navigation controller push view setting

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.

Setting text of textview in another class doesn't work

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.

Access text from UITextField in modal View Controller from Home View Controller

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.

iOS: Accessing the same instance of UIViewController that called the Segue

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);
}

Add objects to an Array from another View Controller

I have a HomeViewController which has a tableView populated with the array tableViewArray (originally empty). When I tap on a barButton, I segue modally to another View Controller called OutsideViewController which has another tableView populated by a different array.
What I would like to do is the following:
When I tap on a row in my OutsideViewController, I would like to add the selected string value to the tableViewArray so that when I go back to HomeViewController, the tableView has that new item listed in the tableView.
So far, this is what I have tried:
In the -didSelectRowAtIndexPath method of my OutsideViewController.m I have this piece of code:
NSString *selectedRow = [outsideArray objectAtIndex:indexPath.row];
NSMutableArray *temporaryArray = [NSMutableArray arrayWithObject:selectedRow];
HomeViewController *homeVC = [[HomeViewController alloc] init];
homeVC.tableViewArray = temporaryArray;
That code works but the tableView in HomeViewController is still empty when I return. Do I have to reload the tableView data?
Am I doing this right?
This is how I have set up my View Controllers in Storyboard:
HomeViewController -(modal segue)-> Navigation Controller --> OutsideViewController
Also, the return from OutsideViewController to HomeViewController is done by this line of code:
[self dismissViewControllerAnimated:YES completion:^{ }];
What you're doing wrong is you're allocationg a new HomeViewController. What I would do is keeep a reference to your HomeViewController in your OutsideViewController. Here is how.
First, in OutsideViewController.h, create a property, like this :
#property (nonatomic, weak) HomeViewController *homeVC;
Don't forget to add #class HomeViewController; in your .h, and #import "HomeViewController.h" in your .m
In HomeViewController, implement the prepareForSegue: method like this (replace ModalSegueIdentifier with your segue's identifier) :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ModalSegueIdentifier"]) {
OutsideViewController *modalVC = (OutsideViewController*)segue.destinationViewController;
modalVC.homeVC = self;
}
}
Then, in OutsideViewController.m, instead of doing :
HomeViewController *homeVC = [[HomeViewController alloc] init];
homeVC.tableViewArray = temporaryArray;
Do this :
_homeVC.tableViewArray = temporaryArray;
When you leave your modal VC, your HomeVC will have the correct array. Don't forget to refresh your UITableView !
NB: Of course, there are many other ways, and it's maybe not the best one. But still, it should work.
You can achieve this too using delegation. You have to create a protocol in your OutsideViewController with a method that is responsible for sending the new object to your HomeViewController. Do this in OutsideViewController.h:
#protocol OutsideViewDelegate <NSObject>
- (void)OutsideViewController:(OutsideViewController *)controller didAddObject:(NSString *)object;
#end
In the implementation file you have to change a little bit the didSelectRowAtIndexPath: method:
NSString *selectedRow = [outsideArray objectAtIndex:indexPath.row];
[self.delegate OutsideViewController:self didAddObject:selectedRow];
In your HomeViewController.h you have to make your class conforms to the protocol:
#interface HomeViewController : UIViewController <OutsideViewDelegate>
After, create a property for the delegate:
#property (nonatomic, weak) id <OutsideViewDelegate> delegate;
To finish the process, implement the protocol in your HomeViewController.m to receive the new object from the OutsideViewController:
- (void)OutsideViewController:(OutsideViewController *)controller didAddObject:(NSString *)object
{
if (object != nil)
{
[self.tableViewArray addObject:object];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
The code above depends of if your tableViewArray object is mutable or not. If it's not, you can change the type of the object argument in the protocol method to an inmutable array object and just assign tableViewArray to the new array.
EDIT:
In the prepareForSegue: method don't forget to set the delegate:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"SEGUE_IDENTIFIER"]) {
OutsideViewController *outsideVC = (OutsideViewController *)[segue destinationViewController];
[outsideVC setDelegate:self];
}
}
First of all make sure you alloc, init your tableViewArray in HomeViewController
Second , In this line
HomeViewController *homeVC = [[HomeViewController alloc] init]
you are creating a new reference to your HomeViewController which is not correct, you need to pass correct reference, possibly creating HomeViewController variable in your OutsideViewController
Even though you correctly do first and second suggestion you will still see an empty tableview because you dont reload the tableview, somehow you need to fire [self.tableview reloadData]; method.
That means; you need to learn Delegate or NSNotifications pattern to communicate between child->parent scenarios
How do I set up a simple delegate to communicate between two view controllers?
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_nsnotificationcenter/
For your question just create a delegate in your Outside;
in your OutsideViewController.h
#import <UIKit/UIKit.h>
#protocol OutsideDelegate;
#interface{}//bunch of interface stuff
// Declare a property for the delegate
#property (weak) id <OutsideDelegate> delegate;
#end
// Protocol Header
#protocol OutsideDelegate <NSObject>
#optional
- (void)dismissPop:(NSMutableArray *)list;
#end
in your OutsideViewController.m
#synthesize delegate;
//run delegate method
[delegate dismissPop:temporaryArray];
[self dismissViewControllerAnimated:YES completion:^{ }];
in your HomeViewController.h
#import "OutsideViewController.h"
#interface OutsideViewController : UITableViewController<OutsideDelegate>
{}
#property (strong, nonatomic) OutsideViewController *pvc;
in your HomeViewController.m
#synthesize pvc;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"your segue"]) {
pvc = [segue destinationViewController];
[pvc setDelegate:self];
}
}
// delegate callback function
- (void)dismissPop:(NSMutableArray *)list {
self.tableViewArray=list;
[self.tableView reloadData];
}
Another Solution Would be
Change your view stack to this:
Navigation Controller --> HomeViewController -(push segue)--> OutsideViewController
and apply rdurand's answer
and add this to your HomeViewController :
-(void)viewDidAppear:(BOOL)animated
{
[self.tableview reloadData];
}
In this solution since you are just push-pop viewcontrollers in a nabigation stack viewDidAppear will be called in HomeViewController everytime when you pop OutsideViewController.

Resources