I'm very new to iOS development and I know there have been questions like this asked, but I can't seem to pull out of those examples what I think I need.
I'm trying to teach this to myself by coding a very simple mortgage calculator. I have two views, MortgageCalculatorViewController where the user enters the loan value, term and interest; and the ResultsViewController where this information is redisplayed, with the monthly payment, in labels. My current issue is I can't seem to figure out how to relay the calculated monthly payment value to the ResultsViewController.
I named the segue showResultsSegue.
In MortgageCalculatorViewController:
#import "MortgageCalculatorViewController.h"
#import "ResultsViewController.h"
#interface MortgageCalculatorViewController ()
#end
#implementation MortgageCalculatorViewController
NSString *paymentText;
-(IBAction)calculateMonthlyPayment
{
float rate = (self.interestRate.text.floatValue / 12) / 100;
long term = self.termInYears.text.integerValue;
float principle = self.loanAmount.text.floatValue;
float termedRate = pow((1 + rate), term);
float payment;
payment = (principle * rate * termedRate) / (termedRate - 1);
paymentText = [NSString stringWithFormat:#"%f", payment];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"showResultsSegue"]){
ResultsViewController *controller = (ResultsViewController *)segue.destinationViewController;
controller.paymentLabel.text = paymentText;
}
}
#end
And in ResultsViewController:
#interface ResultsViewController : UIViewController
#property (nonatomic) IBOutlet UILabel *loanAmountLabel;
#property (nonatomic) IBOutlet UILabel *termInYearsLabel;
#property (nonatomic) IBOutlet UILabel *interestRateLabel;
#property (nonatomic) IBOutlet UILabel *paymentLabel;
-(IBAction)close;
#end
I've guided this approach on the information I found in Passing Data between View Controllers, but I'm still seeing no change after the segue.
Any help would be appreciated. Thanks.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"showResultsSegue"]){
ResultsViewController *controller = (ResultsViewController *)segue.destinationViewController;
controller.paymentLabel.text = paymentText;
}
}
When preparing for a segue, the view has probably not been loaded, so paymentLabel is nil. Instead, declare a paymentText property on ResultsViewController and assign that value to paymentLabel in viewDidLoad.
New prepareForSegue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"showResultsSegue"]){
ResultsViewController *controller = (ResultsViewController *)segue.destinationViewController;
controller.paymentText = paymentText;
}
}
Implementation for ResultsViewController:
#interface ResultsViewController : UIViewController
#property(copy, nonatomic) NSString *paymentText;
#end
#implementation ResultsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.paymentLabel.text = self.paymentText;
}
#end
It's likely that your ResultsViewController has not initialized it's view elements, including the paymentLabel yet. Try explicitly calling view before setting the label text like so:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"showResultsSegue"]){
ResultsViewController *controller = (ResultsViewController *)segue.destinationViewController;
[controller view];
controller.paymentLabel.text = paymentText;
}
}
Calling view will cause the view controller to load it's view elements, hence letting you set the label's text properly.
By far the easiest why to do this without using delegates, singletons or as mentioned above using the prepareForSegue, is using NSUserDefaults.
Save string:
[[NSUserDefaults standardUserDefaults] setObject:paymentText forKey:#"payment"];
[[NSUserDefaults standardUserDefaults]synchronize];
Read string:
NSString *readPaymentString = [[NSUserDefaults standardUserDefaults] stringForKey:#"payment"];
What you basically need to do here is to use the save code I provided in one view and the read code in the second view, simple.
I hope it makes sense.
Related
I have a prepareForSegue method setup in that sends everything I want to the destinationViewController except the value for a UILabel on the destinationVC. I threw a NSLog statement in to see what value it prints and it prints what I want.
It doesn't crash, but it doesn't set. I know I'm missing something very basic here, but it's not jumping out at me.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UIButton *)sender {
if ([[segue identifier] isEqualToString:#"directionsSegue"]) {
// Set destination view controller
DirectionsViewController *destinationVC = segue.destinationViewController;
// Pick out the "thing" you want to send to destinationVC
CGPoint point = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point];
// Set the "thing" on the destinationVC
PointOfInterest *poi = [self.fetchedResultsController objectAtIndexPath:indexPath];
destinationVC.destinationLabel.text = poi.name;
NSLog(#"destinationVC.destinationLabel.text = %#", poi.name);
destinationVC.destinationLatitude = poi.latitude;
destinationVC.destinationLongitude = poi.longitude;
}
}
My property declared in the header of my destinationVC:
#property (strong, nonatomic) IBOutlet UILabel *destinationLabel;
Solution from answers below:
Mystery solved! Here's what I did:
on my destinationVC, I added this to my header:
#property (nonatomic, strong) NSString *destinationName;
I put this back in the implementation:
#property (strong, nonatomic) IBOutlet UILabel *destinationLabel;
In destinationVC, I added this to my viewDidLoad:
self.destinationLabel.text = self.destinationName;
Your label will be nil in prepareForSegue because it won't be instantiate at this time. In fact, IBOutlet are initialised yet once your view is loaded. That's why it's not working.
The best way to solve your issue is to create another property in your DirectionsViewController where will be stored your text. This one is available directly after your controller initialisation, and then you can set your label directly wherever in your controller.
IBOutlet objects are not initialized until the view controller's view loads. That happens after the segue. Create a custom property and update that during prepare and then copy it to your label during viewDidLoad.
I have two view controllers
DetailViewController
BlogViewController
I have a 'push' set-up on the storyboard with the identifier 'ShowBlog'
i need to send the title of the blog from the UILabel below on the DetailViewController:
#property (strong, nonatomic) IBOutlet UILabel *TitleLabel;
to a UILabel on the BlogViewController called BlogTitleLabel:
#property (strong, nonatomic) IBOutlet UILabel *BlogTitleLabel;
i know i need to use:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"ShowBlog"]) {
// Im struggling with the code
}
}
But im struggling with the code to go in it
Use NSUserDefaults the code goes something like this:
Store the title:
[[NSUserDefaults standardUserDefaults] setObject:label.title forKey:#"nameForStoredVariableHere"];
[[NSUserDefaults standardUserDefaults] synchronize];
Retrieve the stored title:
NSString *storedTitle = [[NSUserDefaults standardUserDefaults] objectForKey:#"nameForStoredVariableHere"];
After that you're free to use the title as you please
prepareForSegue is called before destination VC's viewDidLoad so don't try to access any view object of destination view controller in prepareForSegue. Its better you create a string property in destination and set that in prepareForSegue method. In destination viewController's viewDidLoad set it to label.
You should not try to send data from label to label. Labels are view objects. They display information and collect input from the user. They do not store information.
Also, you should never, ever try to manipulate another view controller's views directly. That violates the other view controller's encapsulation.
Both view controllers should have NSString properties for this. Let's call it blogTitle on both VCs.
Your DetailViewController should set it's blogTitle somewhere during it's setup, and then in viewWillAppear:animated, display that value to it's titleLabel outlet:
- (void) viewWillAppear: animated;
{
[super viewWillAppear: animated];
self.titleLabel.text = self.blogTitle;
//your other code here
}
Then, in your prepareForSegue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowBlog"])
{
BlogViewController *theBlogController = [segue destinationViewController];
theBlogController.blogTitle = self.blogTitle; //Pass the blog title to the other VC
}
}
And then in the BlogViewController's viewWillAppear, copy the blogTitle to it's title label:
- (void) viewWillAppear: animated;
{
[super viewWillAppear: animated];
self.titleLabel.text = self.blogTitle;
//your other code here
}
Note that Cocoa/iOS programming has strong naming conventions that you should follow. Only filenames and class names should start with a capital letter. Method names, instance variable names, and property names should start with a lower-case letter. So your TitleLabel should be titleLabel, and BlogTitleLabel should be blogTitleLabel.
I have a simple app which connects to a web server and the web server returns a JSON value.
I am trying to send the JSON value from ViewController1 to "linkController", however when I send the sender over to ViewController2 the sender value is null?
Here is my code:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"linkControllerSegue"]) {
linkController *lc = [segue destinationViewController];
lc.introString = sender;
}
}
This is part of my linkController.h file:
#interface linkController : UIViewController
{
// succes view outlets
IBOutlet UILabel *short_url;
IBOutlet UILabel *full_url;
UIDataDetectorTypes *json;
}
#property (weak, nonatomic) NSDictionary *introString;
And my linkController.m file:
#synthesize introString;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"%#", introString);
}
The segue is being called through the code:
[self performSegueWithIdentifier:#"linkControllerSegue" sender:nil];
The NSLog in the viewDidLoad method returns: (null).
Is there anything I am doing wrong?
Many Thanks,
Peter
Try:
lc.introString = short_url.text;
instead of:
lc.introString = sender;
I am doing the following:
#ViewController1.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Transferring");
guestViewController *controller = [segue destinationViewController];
[controller.label isEqualToString:self.num_value.text];
}
However when the view is loaded, the text does not run through or show up in the label. I have done this in past programs and it has worked on Xcode 4. However, for some reason in Xcode 5 it is not working for me. Is there something else that I need to implement to make this work in Xcode?
Add this to your guestViewController's header file :
#property (nonatomic, strong) NSString *value;
The your prepareForSegue method becomes something like this :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Transferring");
guestViewController *controller = [segue destinationViewController];
[controller setValue : self.num_value.txt];
}
isEqualToString is for comparison! Not to be used for assigning values.
Then in your viewDidLoad method, add this :
[textLabel setText : value];
[controller.string isEqualToString:self.num_value.text];
isEquelToString only check is controler.string is the same or not as self.num.value.text.
- (BOOL)isEqualToString:(NSString *)aString
Returns a Boolean value that indicates whether a given string is equal
to the receiver using a literal Unicode-based comparison.
Read the apple documentation
your can pass your string to the another controller like this:
In guestViewController's h file :
#property (nonatomic, strong) NSString *theStringWhatYouWantToGet;
and in prepareForSegue:
controller.theStringWhatYouWantToGet = self.num_value.text;
I am using the following to create a reference to a view controller in my code like this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"detailSegue"]){
NSIndexPath *indexPath=[self.tableView indexPathForSelectedRow];
TRDetailViewController *vc=segue.destinationViewController;
vc.item=self.items[indexPath.row];
[vc setValue:self forKeyPath:#"delegate"];
[vc setValue:indexPath.row forKeyPath:#"itemIndex"];
}
}
and this is declared as in TRDetailViewController
#interface TRDetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *detailLabel;
#property NSString *item;
#property (nonatomic, weak)id delegate;
#property NSInteger itemIndex;
- (IBAction)deleteItem:(id)sender;
#end
but I get the following error
and I'm not sure why. I am trying to pass the location in an index of the cell selected in a UITableView. If a better way, please let me know. Why am I getting this error?
thx
setValue:forKey: is expecting an object for its value argument and you are passing it a primitive (in this case, an integer). You can work around this by amending your method to read as follows:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"detailSegue"]){
NSIndexPath *indexPath=[self.tableView indexPathForSelectedRow];
TRDetailViewController *vc=segue.destinationViewController;
vc.item=self.items[indexPath.row];
[vc setValue:self forKeyPath:#"delegate"];
[vc setValue:[NSNumber numberWithInt:indexPath.row] forKeyPath:#"itemIndex"];
}
}
And then access it like:
[[vc valueForKey:#"itemIndex"] intValue];
setValue:forKeyPath: requires an object (i.e. id) as first argument.
You are instead passing a NSInteger.
You will have to use an object type, which means to turn your itemIndex into a NSNumber.
Then you can store it using
[vc setValue:#(indexPath.row) forKeyPath:#"itemIndex"];
and retrieve it by
NSInteger myIndex = [vc valueForKey:#"itemIndex"].integerValue;