prepareForSegue not setting UILabel - ios

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.

Related

How to set imageView image on prepareForSegue iOS?

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.

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

setting properties in the detail view from prepareForSegue

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.

Sending data from a UIlabel from one view controller to another

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.

DetailViewController can't set outlets?

Hi i'm having an issue where DetailViewController doesn't want to set UILabel outlets, this is a Master Detail application template, when i press a cell here's what happens
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
// here it accordingly sets the ObjectItem to my custom object.
Torrent *object = _torrents[indexPath.row];
[[segue destinationViewController] setObjectItem:object];
}
}
And here the method i call in DetailViewController viewDidLoad
- (void)configureView
{
// Since i set the objectItem earlier it works, it set the outlets
if (self.objectItem) {
theTitle.text = self.objectItem.MovieTitleClean;
genre.text = self.objectItem.Genre;
size.text = self.objectItem.Size;
quality.text = self.objectItem.Quality;
rating.text = self.objectItem.MovieRating;
year.text = self.objectItem.MovieYear;
}
if (self.detailObject) {
ShortDescription.lineBreakMode = NSLineBreakByWordWrapping;
self.details = [_detailObject objectAtIndex:0];
NSLog(#"Called"); // this is called meaning it should set the outlets but it doesn't...
resolution.text = self.details.Resolution;
framerate.text = self.details.FrameRate;
language.text = self.details.Language;
peersSeeds.text = [NSString stringWithFormat:#"%#/%#",self.details.TorrentPeers,self.details.TorrentSeeds];
downloaded.text = self.details.Downloaded;
ShortDescription.text = self.details.ShortDescription;
}
In DetailViewController viewDidLoad i call configureView which sets the UILabel outlets to my custom objectItem and it works great, but when i receive new detailObject from a delegate and set the other UILabels according to that new detailObject nothing seems to be set at all (note) the view is already loaded and a NSlog shows that i call the configureView which means it suppose to set the outlets, i have to wait like 15 sec and it automatically set them all, does anyone have any idea why the view doesn't respond? it did in the first place. thanks
Edit: here's the .h file
#import <UIKit/UIKit.h>
#import "TorrentDetail.h"
#import "YifyAPI.h"
#import "Torrent.h"
#interface DetailViewController : UIViewController <YifyAPIDelegate> {
IBOutlet UILabel *theTitle;
IBOutlet UILabel *genre;
IBOutlet UILabel *size;
IBOutlet UILabel *quality;
IBOutlet UILabel *rating;
IBOutlet UILabel *year;
IBOutlet UILabel *resolution;
IBOutlet UILabel *framerate;
IBOutlet UILabel *language;
IBOutlet UILabel *peersSeeds;
IBOutlet UILabel *downloaded;
IBOutlet UILabel *ShortDescription;
IBOutlet UIImageView *LargeImageCover;
}
#property (strong, nonatomic) Torrent *objectItem;
#property (strong, nonatomic) TorrentDetail *detailObject;
#end
this is normal because when you load the controller the object isn't set just then you load it for solve this problem you can do like that, in your detailControll, you implement the setter of your object like that:
-(void)setObjectItem:(Torrent *)object{
if(_objectItem != object){
_objectItem = object;
}
theTitle.text = self.objectItem.MovieTitleClean;
genre.text = self.objectItem.Genre;
size.text = self.objectItem.Size;
quality.text = self.objectItem.Quality;
rating.text = self.objectItem.MovieRating;
year.text = self.objectItem.MovieYear;
}
-(void)setDetailObject:(NSArray *) detailObject{
if(_detailObject != detailObject){
_detailObject = detailObject;
}
ShortDescription.lineBreakMode = NSLineBreakByWordWrapping;
self.details = [_detailObject objectAtIndex:0];
NSLog(#"Called"); // this is called meaning it should set the outlets but it doesn't...
resolution.text = self.details.Resolution;
framerate.text = self.details.FrameRate;
language.text = self.details.Language;
peersSeeds.text = [NSString stringWithFormat:#"%#/%#",self.details.TorrentPeers,self.details.TorrentSeeds];
downloaded.text = self.details.Downloaded;
ShortDescription.text = self.details.ShortDescription;
}
To configure properties in a view controller loaded from a storyboard, don't rely on viewDidLoad: to have the properties initialized. Both here and here in the documentation say to override awakeFromNib to do configuration.
// In your Detail View Controller
- (void) awakeFromNib
{
[self configureView];
// Do any other configuration here
}

Resources