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.
Related
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.
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.
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.
I want to pass whatever string a user inputs into a UITextField' over to the destination view controller, but when I NSLog the value in the destination, it shows up as null. I'm also getting a warning stating:incompatible pointer types assigning to UITextField from NSString`. How can I properly make the assignment and send it over?
Originating view controller
SearchViewController.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
controller.itemSearch = self.itemSearch.text;
}
SearchViewController.h:
#property (weak, nonatomic) IBOutlet UITextField *itemSearch;
Destination view controller
UserCategoryChooserViewController.h:
#import <UIKit/UIKit.h>
#import "SearchViewController.h"
#interface UserCategoryChooserViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *itemSearch;
UserCategoryChooserViewController.m:
NSLog(#"The text is:'%#'", self.itemSearch.text); // results in (null)
The above two answer are also correct. But if you want to make the assignment properly then modified textfield variable as NSString variable like that below:
#interface UserCategoryChooserViewController:UIViewController
#property (strong, nonatomic) NSString *itemSearch;
After doing this your warning
incompatible pointer types assigning to UITextField from NSString will get removed
controller.itemSearch is a UITextField *, and you tried to set it to a NSString *, that's what that warning is for. To set text of a UITextField, try this:
[controller.itemSearch setText:self.itemSearch.text];
There might be other issues, but let's start from here.
Let's go through the error you are seeing. It says that you are assigning an NSString * to a UITextField *. Unfortunately, you cannot do that. Here, itemSearch is the text field.
To make your code run properly, you need to set the text property of the itemSearch text field. The correct code would be:
controller.itemSearch.text = self.itemSearch.text;
As a side note, you should really not be meddling so deep into iOS development until you have made a strong foundation, which you have not.
I'm trying to set the delegate for my custom protocol that has one required method allowing me to pass an array of objects back in the hierarchy of two UITableViewControllers. My delegate continues to return nil. Due to this, my required method is never called.
I'm wondering if the datasource and delegate implementations with my UITableViewControllers is causing a conflict. Also, perhaps ARC is getting in the way when declaring the delegate?
It should be noted that both UITableViewControllers were built using Storyboard and are navigated using segues within a UINavigationController (not sure if this may be causing issues or not).
The nav is --> AlarmViewController --> AlarmDetailsViewController. I create an Alarm object in my AlarmDetailsViewController that contains all the details for an alarm, place it into an array and I want to pass that array back to my AlarmViewController to be displayed in a custom cell in the table.
NOTE: I want to use the Delegate pattern here. I'm not interested in solutions that invoke NSNotifications or use my AppDelegate class.
AlarmDetailsViewController.h
#import "Alarm.h"
#protocol PassAlarmArray <NSObject>
#required
-(void) passAlarmsArray:(NSMutableArray *)theAlarmsArray;
#end
#interface AlarmDetailsViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
{
//.....
id <PassAlarmArray> passAlarmsArrayDelegate;
}
#property (nonatomic, retain) id <PassAlarmArray> passAlarmsArrayDelegate;
#end
AlarmDetailsViewController.m
#import "AlarmDetailsViewController.h"
#interface AlarmDetailsViewController ()
#end
#implementation AlarmDetailsViewController
#synthesize passAlarmsArrayDelegate;
-(void) viewWillDisappear:(BOOL)animated
{
NSLog(#"delegate = %#", self.passAlarmsArrayDelegate); // This prints nil
[[self passAlarmsArrayDelegate] passAlarmsArray:alarmsArray];
}
//....
#end
AlarmViewController.h
#interface AlarmViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, PassAlarmArray>
{
//...
AlarmDetailsViewController *alarmDetailsViewController;
}
#property (nonatomic, retain) AlarmDetailsViewController *alarmDetailsViewController;
#end
AlarmViewController.m
#import "AlarmViewController.h"
#import "AlarmDetailsViewController.h"
#import "AlarmTableViewCell.h"
#import "Alarm.h"
#interface AlarmViewController ()
#end
#implementation AlarmViewController
#synthesize alarmDetailsViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
// This is where I'm attempting to set the delegate
alarmDetailsViewController = [[AlarmDetailsViewController alloc]init];
[alarmDetailsViewController setPassAlarmsArrayDelegate:self];
}
//....
//My #required protocol method which never gets called since my delegate is nil
-(void) passAlarmsArray:(NSMutableArray *)theAlarmsArray
{
alarmsTableArray = theAlarmsArray;
NSLog(#"alarmsTableArray contains: %#", alarmsTableArray); // Never gets called due to delegate being nil
NSLog(#"theAlarmsArray contains: %#", theAlarmsArray); // Never gets called due to delegate being nil
}
#end
I've attempted to set the delegate in a method that fires when a button is pressed in AlarmViewController (as opposed to the viewDidLoad method) but that does not work either.
I'm assuming I've got a logic flow error somewhere here . . . but nearly 2 days of hunting and rebuilds haven't uncovered it. Ugh.
You're setting your delegate in the wrong place, and on a different instance of the controller than the one you will get when you do the segue. You should set the delegate in the prepareForSegue method if you're pushing AlarmDetailsViewController from AlarmViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
AlarmDetailsViewController *alarm = segue.destinationViewController;
alarm.passAlarmsArrayDelegate = self;
}
You really need to understand the life cycle of view controllers, how and when they're instantiated, and when they go away. This is the very heart of iOS programming, and Apple has extensive documentation on it. Reading up on segues would also be very useful. A segue (other then an unwind segue) always instantiates a new instance of the destination controller. So, when your segue is performed, whether directly from a button, or in code, a new (different from the one you alloc init'd directly) details controller is instantiated. Before that segue is performed, prepareForSegue: is called, and that's when you have access to the one about to be created. That's the place to set a delegate or pass any information on to the destination view controller.
Did you try replace (nonatomic, retain) with (nonatomic, strong) since you are using ARC?
Auto-synthesized properties like your alarmDetailsViewController property have backing ivars prefixed with underscores, e.g. _alarmDetailsViewController. Your alarmDetailsViewController ivar (the alarmDetailsViewController declared inside the #interface ... {} block in AlarmViewController.h) is different from the backing ivar of your alarmDetailsViewController property.
Just delete your alarmDetailsViewController ivar and use the #property, preferably through self.alarmDetailsViewController.