I created a Master/Detail application with xCode and changed very little about it. In prepareForSegue in the MasterViewController, I added this to try to put some text in a label in the detail view controller
MMDetailViewController *detailVC = [segue destinationViewController];
detailVC.testdetail.text = #"test";
I also added a label to the detailViewController on the storyboard and then did control/drag to the detailViewController to connect them.
#property (strong, nonatomic) IBOutlet UILabel *testdetail;
Therefore, when I set the text in prepareForSegue, I expected it to show in the label once I ran the code on the simulator. However, it didn't show. Can you explain why?
Its because you can't update the UILabel before it load...
So what you need to do is this:
Make Nsstring and update it value, then in viewDidLoad update your Label.
In your detailViewController.h add
#property (strong, nonatomic)NSString *testString;
Then in the detailViewController.m in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.testdetail.text = testString;
}
in your masterViewController:
MMDetailViewController *detailVC = [segue destinationViewController];
detailVC.testString = #"test";
That's it, it should work now :)
Happy coding
The outlets are accessible after -viewDidLoad is fired. That didn't happen yet in -prepareForSegue:, so you probably access a label with nil value.
Ensure that detailVC.testdetail is not nil in -prepareForSegue: first.
If it's nil, which I assume, the UILabel isn't loaded yet. See IBOutlet properties does not update when using prepareForSegue method
Instead, use a NSString property to pass the value around. Put the value into the label in -viewDidLoad of your destination MMDetailViewController because it's responsible for doing that itself.
Related
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.
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 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 have an object that is sent from my main VC to my MasterTabViewController(UITabBarController) in the viewDidLoad I NSLog the object and it shows the object, good. Now I need that object to go to the first tab UIViewController. I tried multiple times and cannot get it to go. I am new so forgive my ignorance,
I send the object from my main vc to my MasterTabViewController via segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showLogin"])
{
MasterTabViewController *preview = segue.destinationViewController;
preview.communityTapped = self.tempCommunity;
}
}
^This works fine!^ self.tempCommunity is an instance community object.
MasterTabViewController.h
- (void)viewDidLoad
{
[super viewDidLoad];
FirstTabViewController *firstvc;
firstvc.communityTapped = self.communityTapped;
NSLog(#"%# !!!!!!!!! ",self.communityTapped.commDescription);
// Do any additional setup after loading the view.
}
FirstTabViewController.h
#property (nonatomic, strong) IBOutlet UILabel *descriptionLabel;
#property (nonatomic, strong) Community *communityTapped;
FirstTabViewController.m
- (void)viewDidLoad
{
self.descriptionLabel.text = self.communityTapped.commDescription;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
If anyone could help that would be greatly appreciated as I have tried and failed many times.
You can't set up IBOutlets to a tab bar controllers view controllers (and I see from your project that you never hooked them up). In your viewDidLoad for the tab bar controller, you can get a reference to any of its view controllers with the viewControllers property. So do something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.firstvc = self.viewControllers[0];
self.firstvc.communityTapped = self.communityTapped;
NSLog(#"%# !!!!!!!!! ",self.communityTapped.commDescription);
}
I think the problem is in the viewDidLoad of your MasterTabViewController.h. You're creating the variable firstvc, but you're not setting the value to anything. So when you set the value of communityTapped in the next line, you're trying to set the value of communityTapped on nothing.
Option 1
If you've set up the tab in interface builder, you need to create an IBOutlet in your MasterTabViewController.h and connect it to your view controller. Something like the following:
#property (strong, nonatomic) IBOutlet FirstTabViewController *firstvc;
Then to set the value of the communityTapped, property, you would use something like this:
self.firstvc.communityTapped = self.communityTapped
Option 2
The other option would be to create the tab pragmatically, and add it to the view. I'm not sure quite what your setup is, but I imagine it would be something like this:
FirstTabViewController *firstvc = [[FirstTabViewController alloc] init];
firstvc.communityTapped = self.communityTapped;
NSArray *viewControllers = [[NSArray alloc] initWithObjects:firstvc, nil];
[self.navigationController.tabBarController setViewControllers:viewControllers animated:NO];
This is what I got so far.
mainview.m
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender(id)sender {
secondView *secView = [segue destinationViewController]
secView.ext = #".com";
}
secondView.h
#interface secondView : UIViewController {
NSString *ext;
}
#proper (nonatomic, retain) NSString *ext;
#end
secondView.m
-(void)viewDidLoad {
NSString *url = [#"www.google" stringByAppendingFormat:ext]
}
And its returning a error saying ext is null... what am I doing wrong?
Did you try to turn ext into a property instead? My understanding is that the "dot" notation essentially turns your code into
[secView setExt:#".com"]
So turn SecondView.h into
#interface secondView : UIViewController
#property (nonatomic, copy) NSString *ext;
#end
And don't forget to #synthesize it in your .m file
Check the followings:
Make sure #synthesize ext; is in SecondView.m
In Storyboard, have you linked the segue correctly?
In Stodyboard, is the viewController that represetns SecondView defined as a class of SecondView?
Make sure that you are calling the SecondView via prepareForSegue:sender method (i.e. SecondView doesn't get called by pushViewController:animated somewhere else in your code).
Put a breakpoint at the line:
secView.ext = #".com";
and make sure that the ext ivar is properly set after the assignment. If it's not, you might be specifying that the accessors use a different ivar in your #synthesize directive for ext, or you might have provided a setter -setExt: that doesn't properly set the ivar.
If ext is set properly after the assignment, perhaps you've got two different instances of secondView. That used to happen a lot when people used .xib files -- they'd create one view controller in code and have another instance of the same class in their .xib file. I wouldn't expect that to be the case here since you're getting secView straight from the segue, but it's worth thinking about.
There are two views: view1 and view2.
Add view2.h file in view1.h
init object of view2 and set their variable.