Get data from viewControllers iOS - ios

So ultra NOOB when it comes to iPhone development
I have three view controllers for a Tab Bar Controller.
What I want here is when I press a button the data from TextFields in the first view get fetched and displayed.
In my firstViewController.h I have declared the TextFields like this:
IBOutlet UITextField * driver;
IBOutlet UITextField * phone;
IBOutlet UITextField * poe;
IBOutlet UITextField * doe;
IBOutlet UITextField * coe;
IBOutlet UITextField * dod;
IBOutlet UITextField * customer;
IBOutlet UITextField * customerContact;
IBOutlet UITextField * customerTank;
IBOutlet UITextField * trailer;
IBOutlet UITextField * truck;
Then in my Main.storyboard I have connected the outlets to each TextField
Now I thought that it just was to import the firstViewController.h to my thirdViewController.h using the #import "" and somehow check when button click was trigged. But it does not seems to be that easy in terms of NOOBines.
I thought that doing something like this in the thirdViewController.h could make the magic:
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface ThirdViewController : UIViewController{
NSString *theDriver = [driver.text];
}
#end

unfortunately it isnt that easy. assuming there is a secondViewController in between your first and third, you're going to pass variables through during the segue using this method. define some variables in the .h file of your second view controller like driver, phone, etc. and assign those values from what you have right there
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"the name of your segue"]) {
secondViewController *secondVC = segue.destinationViewController;
secondVC.driver = self.driver.text;
secondVC.phone = self.phone.text;
//continue typing your values and use this to carry this same method in your secondViewController to carry them into your thirdViewController
}
EDIT 1:
Here is an example I used in an app of mine where I am looking at a list of games and I want to look at one individual game. This is the code in the MatchesViewController.m file. I have my gameObject (in your case the gameObject would be text) and I want to send it to the next view controller. I have also have this imported in the "MatchesViewController.m"
#import "EachMatchViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showOneGame"]) {
EachMatchViewController *eachVC = segue.destinationViewController;
eachVC.gameObject = self.gameObject;
}
}
This is what is defined in my "EachMatchViewController.h" which is the view controller I am going to.
#property (strong, nonatomic) PFObject *gameObject;
some in my "EachMatchViewController.m" I can use it the data that is being passed over.

Related

Segue not passing data between ViewControllers

I am making an iOS app that uses payment methods.
At the moment I am trying to work on the checkout flow, which contains an OderFormViewController and a CheckoutTableViewController as you can see below:
I connected both by dragging a segue, as highlighted in blue. I also added a segue identifier for my segue.
I called the first view controller as E_OrderFormViewController(Its title is Shipping Address), and in it I created an IBActionfor my Continue button and also used -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender to pass in the information.
I also have an Order model, with some properties in it. On my CheckoutTableViewController, I have got my labels and orderInfo as public properties, so the first view controller one can access it.
#import <Foundation/Foundation.h>
#interface Order : NSObject
#property(strong, nonatomic) NSString *shippingName;
#property (strong, nonatomic) NSString *shippingAddress;
#property (strong, nonatomic) NSString *shippingCity;
#property(strong, nonatomic) NSString *shippingState;
#property(strong, nonatomic) NSString *shippingZip;
#end
//E_OrderFormViewController.m
#import "E_OrderFormViewController.h"
#import "F_CheckoutTableViewController.h"
...
#pragma mark - My Actions
- (IBAction)storePaymentInfoProceedToConfirmation:(id)sender {
[self performSegueWithIdentifier:#"toCheckout" sender:self];
}
#pragma mark - Navigation
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"toCheckout"]) {
F_CheckoutTableViewController *checkoutView = segue.destinationViewController;
//Store Information and pass it to next view
checkoutView.orderInfo.shippingName = self.txtFieldNameOnCard.text;
NSLog(#"Shipping Name Stored: %#",checkoutView.orderInfo.shippingName);
}
}
My NSLog always returns (null) for whatever text I type inside my first textField, which is the one I am testing first.
Here is my CheckoutTableViewController, with its public property. The orderInfois listed here. I am using it to pass in the information, as I mentioned above:
//F_CheckoutTableViewController.h
#import <UIKit/UIKit.h>
#interface F_CheckoutTableViewController : UITableViewController
#property (strong, nonatomic)Order *orderInfo;
#end
On my viewDidLoad, on my destination view controller, I did:
//F_CheckoutTableViewController.m
#implementation F_CheckoutTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
//Load all information
self.labelShippingName.text = self.orderInfo.shippingName;
NSLog(#"Typed Name: %#",self.orderInfo.shippingName);
}
I just pasted the viewDidLoad as a reference, as I am aware if my NSLog in my prepareForSegue is not returning anything, the information is not being passed.
I really don't know where the error is. I searched in a couple of threads here on StackOverFlow, and none of them helped me, one which seems to be similar, is this one, but it didn't help with my issue:
Multiple segues not passing Object data between themselves
This seems to be pretty simple, but I am making some mistake that I can not find.
I really appreciate your help.
orderInfo must be null, because you didn't initialize it, therfore you cannot set its properties... so you got 2 options:
checkoutView.orderInfo = [[OrderInfo alloc]init];
checkoutView.orderInfo.shippingName = self.txtFieldNameOnCard.text;
or change your property from:
#property (strong, nonatomic)Order *orderInfo;
to:
#property (strong, nonatomic)NSString *shippingName ;
Based on the storyboard set up, you are using Textfield in Shipping Address View Controller, it may miss the delegation of Textfield to its controller.

Passing data from the FirstViewController to the LastViewController

I have four viewControllers in my current design and I am designing an app to sell a product.
FirstViewController gets the product image and when user clicks to the next button then it takes user to the secondviewcontroller where user describes the product and then user clicks next button which takes user to the thirdViewcontroller where price and condition are entered. In the lastviewcontolller there is a post button to send the product info to the server. I am using POST method.
The following segue approach does not fit into what I want, because it sends the firstviewcontroller object (product image) to the secondviewcontoller, and then secondviewcontroller also should forward the product image to the thirdviewcontoller and so on. I do not think it is a feasible way of doing it.
I wonder what is the best way of collection information from the first page till to the last page and send it. What is best way of handling that issue? I am using segue between the viewcontrollers.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"isSecond"])
{
// Get reference to the destination view controller
SecondViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyProductImage:productImage];
}
}
Please don't use a singleton, even if the majority of users here tells you so. It would violate the SOLID-Principles for several reasons.
Instead just pass the object from ViewController to ViewController.
If all ViewController expect the same model class, you can create a common base class that has the property for the model.
it could have this method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewControler isKindOfClass:[ProductAwareBaseViewController class]])
{
ProductAwareBaseViewController *vc = (ProductAwareBaseViewController *)segue.destinationViewControler;
vc.product = self.product;
}
}
I created an example project: https://github.com/vikingosegundo/ProductWizard
Note, that all view controller derive from ProductAwareBaseViewController
#import UIKit;
#class Product;
#interface ProductAwareBaseViewController : UIViewController
#property (nonatomic, strong) Product *product;
#end
#import "ProductAwareBaseViewController.h"
#import "Product.h"
#interface ProductAwareBaseViewController ()
#end
#implementation ProductAwareBaseViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController isKindOfClass:[ProductAwareBaseViewController class]]) {
ProductAwareBaseViewController *vc = (ProductAwareBaseViewController *)segue.destinationViewController;
vc.product = self.product;
}
}
#end
This ViewController knows how to pass the model data of class Product to other instances of ProductAwareBaseViewController and subclasses of it.
All other view controller don't deal with passing the data, just adding each portion of data (name, description, price) to the model and displaying it.
i.e:
#import "EditNameProductViewController.h"
#import "Product.h"
#interface EditNameProductViewController ()
#property (weak, nonatomic) IBOutlet UITextField *nameField;
#end
#implementation EditNameProductViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.product = [[Product alloc] init];
}
- (IBAction)continueTapped:(id)sender {
self.product.productName = self.nameField.text;
}
#end
#import "EditDescriptionProductViewController.h"
#import "Product.h"
#interface EditDescriptionProductViewController ()
#property (weak, nonatomic) IBOutlet UITextField *descriptionField;
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#end
#implementation EditDescriptionProductViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.nameLabel.text = self.product.productName;
}
- (IBAction)continueTapped:(id)sender {
self.product.productDescription = self.descriptionField.text;
}
#end
Create an object to act as your application's data model. It can be a singleton or it can be a normal object that's available from a known location...such as owned by the app delegate.
Update your model when you have new information and read from the model when you need to display something. Using prepareForSegue: and linking controllers may be acceptable for simple things but it really doesn't scale well.
One way of doing this would be that you create a mutable dictionary (or a custom object with variables) in the first view controller. Then you would pass a weak reference to second/third/fourth view controllers of the mutable dictionary/object from first view controller. Each view controller would be able to set data to the dictionary/object and the last one would be able to process the information.
Another way would be to create a simple singleton class with variables that you want to store. The first view controller would reset the singleton variables. Then let each view controller access the singleton and store their values there, last view controller would process values from singleton.
It depends how many data you are collecting and what you personally prefer.

Xcode 6 - How to hide textfield in one viewcontroller from af switch in another viewcontroller

I have a view controller (firstViewController) with a switch button. The switch button should show / hide a text field in the secondViewController. How can I do it?
Your app should be structured according to the Model-View-Controller design pattern. You need to do this:
Add a BOOL hideText variable to your model class
Set model.hideText to the value of the switch in the switch event handler
Check model.hideText in the viewWillAppear handler of the view that has the text field
If model.hideText is set, hide the text field; otherwise, show it
Very Simple it is.You just have to use delegate design pattern.Go through this link delegation.
In your code you have to do something like this. I suppose your switch is in secondViewCOntroller and you have to make textField hide on SwitchON in FirstViewController
Step 1) Create Protocol in second View controller like this and create property of that delegate.Pass the value of your switch in switchButtonPressed Method and call that delegate method there.
#protocol secondViewControllerDelegate <NSObject>
-(void)switchButtonPressedOn:(BOOL)switchOn;
#end
#interface secondViewController : UIViewController
#property (weak, nonatomic) IBOutlet UISwitch *switchOutlet;
#property(weak,nonatomic)id<secondViewControllerDelegate>delegate;
- (IBAction)switchButtonPressed:(id)sender;
#end
In switchButtonPressed,
- (IBAction)switchButtonPressed:(id)sender {
if(self.switchOutlet.on==YES)
[self.delegate switchButtonPressedOn:YES];
}
Step 2)
In FirstViewController,
#import "swiftViewController.h"
#interface ViewController : UIViewController<secondViewControllerDelegate,UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property BOOL mySwitchState;
#end
Step 3) set delegate=self in prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
secondViewController *destViewController = segue.destinationViewController;
destViewController.delegate = self;
}
-(void)switchButtonPressedOn:(BOOL)switchOn{
NSLog(#"Method Called");
if (switchOn==YES) {
self.textField.hidden=YES;
}else{
self.textField.hidden=NO;
}
}
If you wanted to hide textField in nextViewController then pass the switchState to nextViewController in prepareForSegueMethod,
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
thirdViewController *thirdVC=segue.destinationViewController;
if (self.switchOutlet.on==YES) {
thirdVC.isSomethingEnabled=YES;
}
}
and in nextViewController, in viewDidLoad, hide your textField.
if (self.isSomethingEnabled==YES) {
self.textField.hidden=YES;
}
Working perfectly. This is very important concept in iOS. Go through the above link.

Passing data from some TextFields in one ViewController to a TextView in another ViewController

I am very new to iOS. I am having some difficulty with a project I am working on for iOS. I believe that this problem should have a simple solution, but I have spent hours looking at tutorials and other submissions on this website, but so far nothing has worked.
My issue is that I have 5 TextFields in ViewControllerS, and I want to take the data from those TextFields, and combine them in a TextView in ViewControllerView. Here is my code.
ViewControllerS.h:
#import <UIKit/UIKit.h>
#interface ViewControllerS : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *txtfDay;
#property (weak, nonatomic) IBOutlet UITextField *txtfMonth;
#property (weak, nonatomic) IBOutlet UITextField *txtfName;
#property (weak, nonatomic) IBOutlet UITextField *txtfStart;
#property (weak, nonatomic) IBOutlet UITextField *txtfEnd;
#property (weak, nonatomic) IBOutlet UIButton *btnFinish;
#end
ViewControllerS.m:
#import "ViewControllerS.h"
#interface ViewControllerS ()
#end
#implementation ViewControllerS
#synthesize txtfDay;
#synthesize txtfMonth;
#synthesize txtfName;
#synthesize txtfStart;
#synthesize txtfEnd;
#synthesize btnFinish;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
}
#end
ViewControllerView.h:
#import <UIKit/UIKit.h>
#interface ViewControllerView : UIViewController
#property (weak, nonatomic) IBOutlet UITextView *txtViewFinal;
#end
ViewControllerView.m:
#import "ViewControllerView.h"
#interface ViewControllerView ()
#end
#implementation ViewControllerView
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
}
#end
Well, as you can see, this is basically just the skeleton of my program. I have made so many changes to this code as I followed tutorials, tried ideas from here, and listened to what my classmates had to say. Nothing has worked so far, but I know it's a simple fix. I am liking the use of NSUserDefaults, but if anyone has any other ideas, I will try them as well. Again, I'm just trying to figure out how to pass data from one ViewController to another with TextFields and a TextView.
I do not need directions for all 5, but rather just 1. I can figure out the formatting and the other 4 if I just get a nudge in the right direction.
THANK YOU FOR ANY ASSISTANCE I MAY RECEIVE! I really do enjoy programming with Xcode, but it's been very frustrating too. Thanks!
It's not working because at create point, your ViewControllerView instance still have not loaded UI components, so even if you pass it directly to the textView, it will be simply ignored at this point.
Create a dictionary at ViewControllerView.h to pass data from ViewControllerS like this:
#property (nonatomic, strong) NSDictionary *dicTextData;
and set the data into it. After doing this, just load this data from within your ViewControllerView's -(void)viewDidLoad and voila !
Update
ViewControllerS.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([[segue identifier] isEqualToString:#"ViewControllerView"]) {
ViewControllerView *destination = segue.destinationViewController;
destination.dicTextData = #{#"day":self.txtfDay.text,
#"Month":self.txtfMonth.text,
#"Name":self.txtfName.text}; //and so on...
}
}
ViewControllerView.m
- (void)viewDidLoad {
self.txtViewFinal.text = [NSString stringWithFormat:#"My name is %#", [self.dicTextData objectForKey:#"Name"]];
//and so on...
}
Note that I did't use all fields on my example, but I'm sure you got the point. Good coding!
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([[segue identifier] isEqualToString:#"ViewControllerView"]){
ViewControllerView *destination = segue.destinationViewController;
destination.finalText = self.txtfDay.text;
}
Please make sure you set the identifier of the segue correctly.
Edit:
Thanks #rmaddy point out the problem:
For uncoupling purpose, it would be a better way to store the data that you will pass with a property and let the view controller handle that data. In this case let's say there is a NSString type property called finalText. The prepareForSegue method stored the data and now you can handle this data freely.
There are multiple ways for doing this.An alternative way is to use AppDelegate object.Follow the steps-
Write this line in AppDelegate.h file-
#property (nonatomic, strong) NSString *combinedText;
Import the AppDelegate file in your ViewControllerViewS.m file.Such as-
#import "AppDelegate.h"
Write the line in viewDidLoad of ViewControllerViewS.m -
appDelegate=(AppDelegate *)[[UIApplication sharedApplication]delegate];
Now write the line in the body of the btnFinish button-
appDelegate.combinedText=[NSString stringWithFormat:#"fDay:%#,fMonth:%#,fName:%#,fStart:%#,fEnd:%#",
self.txtfDay.text, self.txtfMonth.text, self.txtfName.text, self.txtfStart.text, self.txtfEnd.text];
Now in destination ViewController.m file repeat step 2 and 3.
Now assign the combined text value to the textView.Like-
UITextView *textView=[[UITextView alloc]init];
textView.text=appDelegate.combinedText;
Let me know if it works for you.Thank you.

Can I change a segue identifier depending on another segue identifier, in code?

Can I change a segue identifier depending on what another segue identifier is in code? I'm not even sure if that is the correct question for what I am looking to do.
I have a ViewController which has a number of buttons on it. Each button has a segue to another ViewController which contains some labels, images and buttons. The text/images/titles of these labels, images and buttons are determined by which segue (which button on the previous ViewController) is pressed. This is done by giving each property a variable in the initial ViewController and assigning that variable in the 2nd ViewController.
eg. (I will not individually put the .h and .m but just under one heading!)
1st ViewController:
-(void) prepareForSegue: (UIStoryboardSegue*)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"someSegIdentifier"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.TitleString = #"A Title";
aVC.FImg = [UIImage imageNamed:#"someimage.jpg";
aVC.Button1String = #"Button 1 Text";
aVC.Button2String = #"Button 2 Text";
etc...
} }
2nd View Controller:
#property (strong, nonatomic) UIImage *FImg;
#property (strong, nonatomic) NSString *TitleString;
#property (strong, nonatomic) NSString *Button1String;
#property (strong, nonatomic) NSString *Button2String;
#property (strong, nonatomic) IBOutlet UILabel *TitleLabel;
#property (strong, nonatomic) IBOutlet UIImageView *FImageView;
#property (strong, nonatomic) IBOutlet UIButton *Button1;
#property (strong, nonatomic) IBOutlet UIButton *Button2;
-(void)viewDidLoad
{
self.TitleLabel.text = self.TitleString;
self.FImageView.image = self.FImg;
[self.Button1 setTitle:self.Button1String forState:UIControlStateNormal];
[self.Button2 setTitle:self.Button2String forState:UIControlStateNormal];
}
From this ViewController, each button links to another ViewController each (which have NavControllers Embeded), these 2 ViewControllers also have 2 buttons which individually link to a tableViewController. The data displayed in the tableView is determined by the segue ID from the previous 2 viewControllers.
I believe I need to repeat the first process (the code above) on the second set of viewControllers but at the same time change the segue ID for proceeding/destination viewController, so that the tableView can load the correct data. How can I change this Identifier?
I think a hierarchy may be and easier way of explaining it.
How would I go about changing the specific segue Identifiers depending on what the previous ViewController shows. Keeping in mind, that for each level their are many different 'links' or specified properties.
Yes you are definitely asking the wrong question. You are creating a segue for each button?
That is the wrong approach as segue are meant to be between 2 view controllers.
if you had 100 button would you create 100 segues?
In your case you have 2 controllers so it should be only one segue.
If you need to know which button was pressed I suggest you to give the sender a tag number and check it when preparing to segue.
You could, instead of sending the information from previous controllers through the segue identifier, create properties on the destination view controllers and change those properties at the time of segue TO that view controller according to the button pressed ( or segue identifier). Then, when there is a segue FROM the view controller, you use those properties and the segue identifier as parameters, instead of just the segue identifier.
e.g.:
if you go are going from controller A to B to C, and there are 3 possible segues from A to B and 3 possible segues from B to C, you could have a NSString property in B which would go like
in A:
-(void) prepareForSegue: (UIStoryboardSegue*)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"identifier1"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.propertyIWasTalkingAbout = "segue1"
}
else if ([segue.identifier isEqualToString:#"identifier2"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.propertyIWasTalkingAbout = "segue2"
}
else if ([segue.identifier isEqualToString:#"identifier3"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.propertyIWasTalkingAbout = "segue3"
}
}
in B:
-(void) prepareForSegue: (UIStoryboardSegue*)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"identifier4"]){
switch (self.propertyIWasTalkingAbout){
case("segue1"):
//...
break;
case("segue2"):
//...
break;
case("segue3"):
//...
break;
}
}
else if ([segue.identifier isEqualToString:#"identifier5"]){
switch (self.propertyIWasTalkingAbout){
case("segue1"):
//...
break;
case("segue2"):
//...
break;
case("segue3"):
//...
break;
}
}
else if ([segue.identifier isEqualToString:#"identifier6"]){
switch (self.propertyIWasTalkingAbout){
case("segue1"):
//...
break;
case("segue2"):
//...
break;
case("segue3"):
//...
break;
}
}
}
I wrote all this code here, not on XCode, so there might be some errors, but I hope you get the idea, and I also hope I got what you meant :p

Resources