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.
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.
if I fill state,province and town in first view controller, how to get just town or town and state in another view controller, when I press OK button.
http://i.stack.imgur.com/jhwGq.png
First of all, you should create a property in the SecondVC.h to receive de text and a property linked to the label in screen:
#property(nonatomic, copy) NSString *townText;
#property (weak, nonatomic) IBOutlet UILabel *townLabel;
After that, in the SecondVC.m, overwrite the "viewWillAppear" method as follows:
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.townLabel.text = self.townText;
}
Then, in the FirstVC.m, overwrite the "prepareForSegue" method:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
SecondVC *secondVC = segue.destinationViewController;
secondVC.townText = self.townTextView.text; // townTextView is the TextView in screen where user writes the town name.
}
Don't forget to import the SecondVC.h in the FirstVC.m
Easy way:
Use storyboard's IBOutlet feature. One view controller class can hold a reference to another view controller
If the code just reference the other view controller
-(void)clickOK:(UIButton*)
{
NSString* town = self.anotherController;
//do something with town
}
Suggested way:
Follow the model-view-controller design pattern. Treat first view controller as a data provider
Protocal DataProviderDelegate
{
#required
-(NSString*)getTown;
-(NSString*)getState;
}
#implementation
FirstViewController:UIViewController<DataProviderDelegate>
{
}
#declaration
SecondViewController:UIViewController
#property(nonatomic, weak)DataProviderDelegate* dataProvider;
In the segue, assign first view controller to the second view controller as a delegate
I tried to push to a ViewController using prepareForSegue. When I'm pushing, I want to set an image on ImageView in pushed view controller. Here what I tried,
ViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
UIButton *btn = sender;
if (btn.tag == 50) {
if (jpegData) {
[self saveTempImage:jpegData];
}
if ([segue.identifier isEqualToString:#"HomeView"]) {
HomeViewController *vc = [segue destinationViewController];
vc.backImageView.image = capturedImage;
vc.isBackImage = true;
}
}
}
I have an ImageView in HomeViewController. I tried to set it's image using this vc.backImageView.image = capturedImage;. capturedImage is not null. But the image is not set in ImageView.
How can I fix this?
Thanks in Advance!
Your outlates are not yet set there, I believe backImageView is null at this point. Have backImage property and when backImageView is ready (added to view hierarchy, viewDidLoad is good place for that) then set its image property.
1) You should make the backImageView property (and all the other outlets of HomeViewController) private, because for this view, HomeViewController is exclusively responsible and no other class should be able to manipulate this view. This is current established convention in iOS development. You can do this by adding class extension above the #implementation keyword in HomeViewController .m file.
#interface HomeViewControler ()
#property (nonatomic, strong) IBOutlet UIImageView *backImageView;
//...other private properties ...
#end
#implementation MyViewControler
Right after that, you need to move view outlet properties from the .h interface file to the .m interface extension to have the declared privately.
2) You should create a private property called capturedImage in the extension too.
#interface HomeViewControler ()
#property (nonatomic, strong) IBOutlet UIImageView *backImageView;
#property (nonatomic, strong) UIImage *capturedImage;
#end
3) Declare a public method in h. file called configureWithImage
-(void)configureWithImage:(UIImage *)paramImage;
and implement it i m. file like this
-(void)configureWithImage:(UIImage *)paramImage
{
self.capturedImage = paramImage;
}
4) Next you need to make sure the passed image is used in the imageview, for that HomeViewController's viewDidLoad makes a lot of sense.
-(void)viewDidLoad
{
[super viewDidLoad];
self.backImageView.image = self.capturedImage;
//...other code...
}
5) Last step, in prepareForSegue you configure your view controller with the image
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIButton *btn = sender;
if (btn.tag == 50)
{
//...other code...
if ([segue.identifier isEqualToString:#"HomeView"])
{
HomeViewController *vc = [segue destinationViewController];
[vc configureWithImage:capturedImage];
//...other code...
}
}
The "morale of the story" is that by having a proper public interface that is the sole entry point for configuration (the config method) you decouple the two entities. It means the source view controller merely passes the image without having to know ANYTHING about what happens in the destination VC. The image is then processed by the responsible destination view controller.
Should you change your mind and do some layout/view content changes in the HomeViewController (possible filter and process the image for visual effects) later in time, the source view controller will not be affected at all because wheat happens in HomeViewController is nobody else's concern, and you will keep the public configuration method intact. That means the change will not require to maintain code in prepareForSegue, only in the destination VC.
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.
I have a view that through a prepareSeque method should pass whatever is written in a textView to the next view.
Before I had textField and textfield.text in the seque method worked fine. But I can not get it to work with a textView.
I have a NSString property in the .h file: #property (weak, nonatomic) NSString *textString;
I syntesize it in the .m file: #synthesize textString =_textString;
In my textViewDidEndEditing i can see (through debug) that the text in the text View is picked up and that the textString is set.
(void)textViewDidEndEditing:(UITextView *)textView {
NSString *theText = textView.text;
self.textString =theText;
}
However, when I then want to retrieve the textString in my Seque method it does not contain any text:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"go"]) {
ISecondViewController *vc = [segue destinationViewController];
vc.funnyString = self.textString;
}
If i enter: self.textString =#"Hi Hi"; in the Seque method funnyString will be passed with Hi Hi so that part works fine.
Have I just totally misunderstood the "get and set" of NSString in this case?
Your problem here is that you have been using a weak property for textString so it will be nil when the scope of your property get out of your textViewDidEndEditing method.
Why? because the principle of weak reference is that it will be set to nil as soon as the object you are referring to doesn't exist anymore. This will be the case for the "theText" object which won't exist at the end of your method. Use a strong property instead.