Passing NSArray makes array empty - ios

i'm trying to pass an array from a view to another using the PrepareForSegue method.
In the first view i got a button called "Submit" that, if pressed, reads a textView and store the text in a NSArray, and then should pass this array to another view (push segue), but when the array arrives is empty.
Here is the code
//.h
#property (strong, nonatomic) NSArray *words;
//.m
- (IBAction)Submit:(id)sender{
//read textView
_words = [self.myTextView.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//if Submit is pressed go to SecondViewController
if ([segue.identifier isEqualToString:#"secondSegue"]) {
SecondViewController *vc = [segue destinationViewController];
vc.array = _words;
}
}
So here is the code of the SecondViewController that receives the array
- (void)viewDidLoad
{
[self Calculate];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)Calculate
{
int size = [array count];
NSLog(#"size is %d",size);
}
the log always says "size is 0".
Did i make some mistake?
Could it be that the Segue happens before i can read the TextView and fill the array so it's always empty?
thanks in advance!
EDIT:
I tried to NSLog the Submit action and i discovered that the program never accesses to it, so it never reads! (p.s. yes i connected the button)
So the segue happens before the Submit action
How can i solve? can i copy the PrepareForSegue code in the Submit action?

Try copying the array when you send it, Could be getting freed also are you sure -(IBAction)submit.. is being called?
Also the submit button I assume has the action that performs the segue . I'm not sure on the order of execution. Try calling the method Submit from prepare for segue and not from the button. That way you can guarantee it is being called first.

Remove the #synthesize, the compiler will automatically synthesize
the ivar to be _words
Use self.words when assigning and accessing the 'words' array. This ensures the ivar gets set properly as well, if you plan to refer to it directly (which you won't in most cases).
Your problem is that #synthesize creates an ivar called 'words', not '_words'.

Related

Pass data between two ViewControllers [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 7 years ago.
I need to pass data between two ViewControllers but without UIButton, in a few words, I need to access a variable which is in other ViewController.
My code is:
LoginViewController *lvc;
NSString name=lvc.name;
This specific case might be a little easier than delegates.
From what I see, you're trying to pass login credentials (name/login/password/something). I would use two things depending on the actual matter here.
Either NSUserDefaults or -performSegueWithIdentifier:
NSUserDefaults is a file that is loaded in every app that you can read and edit, simply using the following commands :
Setting a variable :
NSString *aName;
[[NSUserDefaults standardUserDefaults]setObject:aName forKey:#"userName"];
Getting a variable :
NSString *aName = [[NSUserDefaults standardUserDefaults]objectForKey:#"userName"];
Note that you can save the following objects NSDictionary, NSArray, NSString, NSNumber, NSData, and probably a couple that I'm forgetting but someone can edit if I do.
Note that this file is loaded at every startup, so you don't wanna use that as a database but more of a small-sized storage easy to use, like for user name, preferences/settings, and stuff like that.
The other way is using performsegue between two controllers, but that requires storyboards.
Drag a segue between two of your controllers, name it (for example) fromLoginToHome. I'm assuming that the flow goes from the login controller to the home controller.
when you move between the two views (when the user presses "Login" for example), call this method
[self performSegueWithidentifier:#"fromLoginToHome" sender:self];
Then you'll need to implement this method, that is usually there but in a comment block (it's always like that when you create your Vc)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"fromLoginToHome"]){
HomeViewController *vc = (HomeViewController*)segue.destinationViewController;
vc.myName = _myName;
}
}
Xcode using delegate to pass data between controllers This is for child to parent by usuing delegates
And For parent to child,you can use segues simply.
HTH!enjoy Coding.
You can have a look of delegate method in here delegate. can you tell me if you are looking for delegate or not
Try using as below
FirstViewController.h
#interface FirstViewController: UIViewController
- (void)GetItNow;
FirstViewController.m
- (void)GetItNow{
NSLog(#"I acheived"); }
- (IBAction)goToSecondView:(id)sender {
SecondViewController* Second= [[SecondViewControlleralloc] initWithNibName:#"SecondViewController" bundle:nil];
rqVC.addId = self.addId;
[self.view addSubview:Second.view];
}
SecondViewController.h
#property (nonatomic, assign) id delegate;
SecondViewController.m
- (IBAction)Action_LoadFunds:(id)sender {
[self.view removeFromSuperview];
[_delegate GetItNow];
}

Executing code/triggering UI Elements between two viewcontrollers

I'm new to objective C and design patterns like MVC, protocols and so on but this is it:
I am trying to write an iOS app within two viewcontrollers: the first has a textview where the user can write into, and the second has a UISwitch that triggers on "Value changed" and saves a file.
If I toggle by hand the switch on the SecondViewController it will save the file and that's ok.
But I wish the file could be saved from the FirstView just when the user types a specific word, it auto-switches to the second view, and auto-activates the UIswitch and all the method already behind it.
I still can't get the two interfaces working this way. Thanks everybody in advance for helping. Cheers!
this is connected in SecondViewController.h in the storyboard
-(IBAction)toggleFileSave:(id)sender;
and it is implemented as usual...
#interface SecondViewController ()
#property (nonatomic,weak) IBOutlet UISwitch *mySaveFileSwitch;
#end
- (void) toggleFileSave:(id)sender {
// how do I execute this code when the user
// type a specific word in the first view??
}
Create a BOOL flag in your SecondViewController.
Set it when the specific word is typed and push the view controller.
In the viewDidLoad of SecondViewController check the flag condition.If it is set call the required method.
When the specific word is typed:
ViewController2 *viewController = [ViewController2 alloc]init];
viewController2.flag = YES;
[self.navigationController pushViewController:viewController2 animated:YES];
In your text field delegate (add one if it doesn't exist) add this method:
- (void)textFieldDidEndEditing:(UITextField *)textField {
/* at this point the user finished editing */
NSString *currentText = /* read text field value */
if ([currentText isEqualToString:/* the magic word */]) {
/* save the file, present a view controller, etc. */
}
}
Check UITextFieldDelegate to know the available methods, you may need more than one to get the desired behaviour.
If you want to load the second view controller in order to show the UI and the save the file you can do as サンディープ said in his or her answer:
SecondViewController *controller = [SecondViewController new]; /* init as usual */
controller.saveOnLoad = YES;
[self.navigationController pushViewController:controller animated:YES];
Then, in SecondViewController:
- (void)viewDidLoad {
if (self.saveOnLoad) {
/* save file in async block */
/* set switch on */
}
}
If you don't need to show the second view I'd move the saving functionality to its own class and use it from the first controller, showing just a confirmation message for instance.

Passing a PFObject between views is causing values to be null

I'm new to Parse and iOS app development, so please excuse my question if it has an obvious answer.
In my app, the user needs to enter data across multiple views, and for resource efficiency, I am initiating the PFObject as a property in the first view and it is being handed via prepareForSegue to by each scene to its segue's destination view controller.
However, when checking the key-value pairs in the object, I noticed that they are not getting stored in the object. In the debugger, it shows the data in the "estimatedData" section. What is the cause of this? When I try to saveInBackground the object, it fails and says that the object is null.
Here is the code from the FirstViewController.h of the PFObject property declaration.
#property (strong, nonatomic) PFObject *freshChow;
I also call #synthesize freshChow; under the #implementation of the FirstViewController.m.
I later initialize the object in an IBAction when a button is tapped.
- (IBAction)StartCookingProcess:(id)sender {
freshChow = [PFObject objectWithClassName:#"FoodItems"];
[self performSegueWithIdentifier:#"Perform Init Segue" sender:self];
}
And the prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"Start Cooking Process"]) {
Chow_Type_selection *vc = [segue destinationViewController];
vc.freshChow = freshChow;
}
}
This code, with the exception of the StartCookingProcess method is repeated on the subsequent views.
Thanks,
Siddharth

Why does my mutable array become NULL in the prepareForSegue method?

I want to pass an array onto another class using storyboards, and I've prepared the following code, however, the log shows that the mutable array is null, where as this is clearly not the case (in another method, the log shows it is not null, it only becomes null when prepareForSegue gets called). Why is this?
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"confirmSegue"]) {
SBBookingConfirmation *bookingConfirmed=(SBBookingConfirmation*)segue.destinationViewController;
NSLog(#"dates chosen - %#", self.dateChosen);
bookingConfirmed.confirmedTimings=self.dateChosen;
}
}
Output:
dates chosen - (null)
Clearly, you are changing self.dateChosen somewhere else. This has nothing to do with prepareForSegue:.
Go back and check what you do before the segue is invoked (through IB or by method).
Make sure, your dateChosen property is of type strong:
#property (nonatomic, strong) NSMutableArray *dateChosen;
//if you have an NSArray instead of NSMutableArray, use copy instead of strong
also in your "method" method (worst name ever!) , you should use the setter to set dateChosen:
-(void)method:(NSMutableArray *)array {
self.dateChosen=array; //not dateChosen = array;
NSLog(#"The following has been copied %#", self.dateChosen);
}
Finally, you need to initialize your array somewhere. If you never did something like
self.dateChosen = [NSMutableArray array];
or
self.dateChosen = <NON-NIL array pointer>
it is no wonder it would be nil.
On a side note: choose your method / variable names better. Don't call a method just "method".
If you use an array, it is usually better to name it in its plural form: date*s*Chosen, instead of dateChosen.

Best practice to send "reloading" signal to UITableView from one view to another

My goal is to notify a UITableView to refresh itself every time some configurations have changed. The problem is that the configuration view is "not" on the same view that produces the signal. (Yes, I used Tabbed Application.)
Currently I use a sort of global variable in AppDelegate for detecting the change in one view, and do the check in another view. This is fine but the code is not readable as it is so tightly coupling. Is there an elegant method for doing this? Do I miss something in this programming framework?
If there were such an elegant way, I suppose the refreshing process of UITableView should happen as soon as the notification occurs. In this case, I would like to know whether it's possible to delay UITableView from refreshing itself until viewDidAppear occurs.
I would use KVO (Key Value Observing) to keep track of when it changes:
- (void)viewDidLoad {
[super viewDidLoad];
// Note that you can use the options to get the new value passed when it
// changes if you want to update immediately.
[configurationObject addObserver:self forKeyPath:#"configurationItem" options:0 context:nil];
}
- (void)viewDidUnload {
[super viewDidUnload];
[configurationObject removeObserver:self forKeyPath:#"configurationItem"];
}
// Note that I would refresh in viewWillAppear instead of viewDidAppear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.needToRefreshData == YES) {
[self.tableView refreshData];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (keyPath isEqualToString:#"configurationItem") {
[self.needToRefreshData = YES];
}
}
Use Delegation Design Pattern to pass data from one View Controller to the Other.
For example, let's say one Tab shows a list of cars in a UITableViewController and you have another view that let's a user add a new car to the list. You can let the UITableViewController
Adopt AddCarViewController's protocol
Set itself as a Delegate for AddCarViewController's protocol
Implement its protocol method
Execute the protocol method when informed
You can then let the AddCarViewController
Create a Protocol
Declare object reference Delegate with getter and setter methods
Define a method under that protocol
Inform the Delegate when the Save action is performed
Take a look at the following sample code for your UITableViewController
#interface ViewController : UITableViewController <AddCarViewControllerDelegate>
:
:
// The addCar: method is invoked when the user taps the Add button created at run time.
- (void)addCar:(id)sender
{
// Perform the segue named ShowAddCar
[self performSegueWithIdentifier:#"ShowAddCar" sender:self];
}
:
:
// This method is called by the system whenever you invoke the method performSegueWithIdentifier:sender:
// You never call this method. It is invoked by the system.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString *segueIdentifier = [segue identifier];
if ([segueIdentifier isEqualToString:#"ShowAddCar"]) {
// Obtain the object reference of the destination view controller
AddCarViewController *addCarViewController = [segue destinationViewController];
// Under the Delegation Design Pattern, set the addCarViewController's delegate to be self
addCarViewController.delegate = self;
// Instantiate a Save button to invoke the save: method when tapped
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:addCarViewController action:#selector(save:)];
// Set up the Save custom button on the right of the navigation bar
addCarViewController.navigationItem.rightBarButtonItem = saveButton;
}
}
:
:
- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave: (BOOL)save {
:
:
}
Sample code for the AddCarViewController is here
#protocol AddCarViewControllerDelegate;
#interface AddCarViewController : UIViewController
#property (nonatomic, strong) IBOutlet UITextField *carMake;
#property (nonatomic, strong) IBOutlet UITextField *CarName;
#property (nonatomic, assign) id <AddCarViewControllerDelegate> delegate;
// The keyboardDone: method is invoked when the user taps Done on the keyboard
- (IBAction)keyboardDone:(id)sender;
// The save: method is invoked when the user taps the Save button created at run time.
- (void)save:(id)sender;
#end
/*
The Protocol must be specified after the Interface specification is ended.
Guidelines:
- Create a protocol name as ClassNameDelegate as we did above.
- Create a protocol method name starting with the name of the class defining the protocol.
- Make the first method parameter to be the object reference of the caller as we did below.
*/
#protocol AddCarViewControllerDelegate
- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave:(BOOL)save;
#end
Well, one approach would be to have some common class (singleton perhaps which app delegate kind of is) that keeps track of your model, when the settings viewController detects a change it can mark the model as changed, then when the view in question comes in to view, ie, viewDidAppear gets called, it can query the model to see if the changed flag has been set, if it has then you know to reload the table view, otherwise you dont...
Another way could be to use notification center for it, if your view is loaded it can sign up for the notifications of the model change, in which at point it sets a flag that it needs to reload the table view next time it comes on screen..
hope this helps
You could store the configuration in core data and use an NSFetchedResultsController with the dependant view controller set as a delegate. This way your view controller will get a callback whenever the data is changed.
Apple has some boilerplate code to handle the updates as well

Resources