I've got the following code in one of my view controllers:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case 0: // "days" section tapped
{ DayPicker *dayPicker = [[DayPicker alloc] initWithStyle:UITableViewStylePlain];
dayPicker.rowLabel = self.activeDaysLabel;
dayPicker.selectedDays = self.newRule.activeDays;
[self.navigationController pushViewController:dayPicker animated:YES];
[dayPicker release];
break;
...
Then, in the DayPicker controller, I do some stuff to the dayPicker.rowLabel property. Now, when the dayPicker is dismissed, I want the value in dayPicker.rowLabel to be used as the cell.textLabel.text property in the cell that called the controller in the first place (i.e., the cell label becomes the option that was selected within the DayPicker controller).
I thought that by using the assignment operator to set dayPicker.rowLabel = self.activeDaysLabel, the two pointed to the same object in memory, and that upon dismissing the DayPicker, my first view controller, which uses self.activeDaysLabel as the cell.textLabel.text property for the cell in question, would automagically pick up the new value of the object. But no such luck.
Have I missed something basic here, or am I going about this the wrong way? I originally passed a reference to the calling view controller to the child view controller, but several here told me that was likely to cause problems, being a circular reference. That setup worked, though; now I'm not sure how to accomplish the same thing "the right way."
As usual, thanks in advance for your help.
Turns out I needed to add a call to
[self.tableView reloadData];
in the viewWillAppear method to get the table to read the new value.
Related
i have navigation controller and i want to change label text of next controller before view is loaded. There is my code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ProductViewController *product = [[ProductViewController alloc]init];
product.myDescription.text = [[self.listOfItems objectAtIndex:indexPath.row] objectForKey:#"description"];
[self.navigationController pushViewController:product animated:YES];
}
I did try product.myDescription.text = #"123"; But it still not working.. i have no idea why, maybe i missing something? myDescription is connected UILabel of my second controller.
myDescription doesn't exist when you try to use it because the view hasn't been loaded yet. You can ensure the view is loaded by requesting it (product.view) though doing this is generally a little strange (because you aren't going to use the view). A better (but more code) approach is to pass the values required and have the controller set them when the view is loaded.
My setup is using a storyboard in which I create a login, then a home screen in which the user can press a button to display their messages.
These are displayed in a table which is instantiated programmatically.
From this table I then want to press a row to go into detail about that row, but when i do this the view displayed is blank but all methods associated with the class are being fired (view did load, and so on).
I have literally tried 10 different ways from different solutions others had had suggested on their questions but nothing works.
Code to make new view and push it:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
messageDetail *messageDetailView = [[messageDetail alloc]initWithNibName:nil bundle:nil];
messageDetailView.message = [entityObjects objectAtIndex:indexPath.row];
[self.navigationController pushViewController:messageDetailView animated:YES];
}
ViewDidLoad of DetailView:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"Message Detail:\n%#",message);
messageTitle.text = message.message_title;
messageBody.text = message.message_xml;
}
I have tried using a segueIdentifier route and then intercepting the prepareSegue: to add data to the new view, but as the tableView is created programmatically there is no segue.
I have tried instantiating the view from storyboard via:
messageDetailView = [self.storyboard instantiateViewControllerWithIdentifier:#"MDV"];
But the app crashes as there is no view with identifier, even though i set the identifiers.
You send nil as nib name, if you do not have a nib file do not allocate the message detail view with initWithNibName function.
Use raw init or initWithFrame. Whichever works for you.
Good luck
I basically did the same thing with this code here. Hope this helps
Detail_View *next=[[Detail_View alloc] initWithNibName:#"Detail_View" bundle:nil];
//These are just values that I needed to set before going to the next page.
//You can use this if you want to pass over the value of your table row
//otherwise ignore this
next.htmlContents= [[storage_array objectAtIndex:indexPath.row]objectForKey:#"title"];
next.titleText=[[storage_array objectAtIndex:indexPath.row] objectForKey:#"title"];
[self.navigationController pushViewController:next animated:YES];
I have a error in my script, it doesn't works. but I don't know how to change it.
I'm using 2 views, I'd like to use if/else for changing the text on the favoriteColorLabel on the second view from the first.
If someone know the problem, please help me.
Th
My code :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row==1) {
DetailVC.favoriteColorLabel=#"Bonjour";
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
dvController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:dvController animated:YES];
}
}
Thanks.
I think the error is on this line:
DetailVC.favoriteColorLabel=#"Bonjour";
It is most likely you actually want to set the text attribute of your label like this:
DetailVC.favoriteColorLabel.text=#"Bonjour";
This is because the text attribute is the text displayed by the label onscreen. You were setting the actual UILabel object to an NSString literal, which is probably not what you wanted to do.
You are setting Text to a label Thats why you are getting error in your code.
You need to do it as like this:
labelname.text = #"String";
Then your issue will resolve.
Hope it works.
I would consider setting a label in the second view from a method in the first bad design and error prone.
What I would do, is give your detail view controller a property that is an NSString *. After you create the detail view controller from your first controller, you pass the text #"Bonjour" to that property (by using its setter method) and then you can present the detail view controller. This second view controller can look at the value of its string and set the label accordingly.
The detail view controller is there to manage its own views, it should not need you first view controller to manage what's onscreen.
I am trying to grab the string off an object in a table row when it is clicked. I have seen tutorials about getting the string and passing it to synthesized label however I am unable to pass the same string to a synthesized string in my modal view. Is this possible? My code in the parent view is as follows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Modules *selectedModule;
selectedModule = [fixedArray objectAtIndex:indexPath.row];
NSString *moduleComponent = [selectedModule valueForKey:#"name"];
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"Detail"];
[self.navigationController pushViewController:detail animated: YES];
detail.number.text = moduleComponent;
detail.moduleLabel.text = moduleComponent;
detail.module = moduleComponent;
}
The detail.moduleLabel.text displays correctly however when I try to alert or draw a label using the detail.module I receive null. Any help is greatly appreciated.
Move [self.navigationController pushViewController:detail animated: YES]; after assigning the value to the detail controller.
EDIT:
Another point to note is when exactly do you try to alert it - viewDidLoad, viewWillAppear, viewDidAppear. Try in the different methods to see what the result is. I apologize but I am unable to test it now and give you the result itself, but have experienced similar behavior (not assigned value) depending on the specific event (viewDidLoad, viewWillAppear, viewDidAppear) where I try to access the value.
I have a DetailsViewController class and an ItemsViewController class. (Both derived from UITableViewController)
Selecting any of the items in the ItemsViewController brings up the DetailsViewController. In order to get it to show the new data on any but the first one, I currently have
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self navigationItem] setTitle:title];
[[self tableView] reloadData];
}
This works, however it feels like killing a fly with a sledgehammer. What is a better way to do this?
Thanks in advance,
Alan
Combining ideas from several comments here:
Adding BOOL needReload as a member variable to the Details Controller.
Then in the details controller:
- (void)setData:(DataClass *)value {
if (value == data)
return;
id pointer = data;
data = [value retain];
[pointer release]; // release after retain
needReload = TRUE;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(needReload){
[[self navigationItem] setTitle:title];
[[self tableView] reloadData];
needReload = FALSE;
}
}
If you know that only specific rows or sections will be changing, you can direct the bake view to reload only those rows or sections. Other than that, -reloadData is the way to go for most table views.
I assume the items on the detail table changes depending on the selected item on the items table. So, yeah, this should be alright.
Other than that, you can check if the same item is selected the last time and not call reloadData during that case.
Alan,
Your statement of "In order to get it to show the new data on any but the first one" concerns me - because it tells me that you likely have a single DetailsViewController instance.
In your first table view, ItemsViewController, you probably have a didSelectRowAtIndexPath: method that you're using to push the DetailsViewController onto the UINavigationController stack.
How I solve this issue is simply creating/destroying a new DetailsViewController every time my user taps between views. So, my didSelectRowAtIndexPath: often looks like:
- (void) didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
NSInteger selectedRow = indexPath.row;
// Create a new view controller
DetailsViewController *tmpVC = [[DetailsViewController alloc] initWithNibName:#"foo" bundle:nil];
// Tell our new view controller what data it should be using
tmpVC.tableData = [self.someArrayOfData objectAtIndex:selectedRow];
// Push view controller and release it
[self.navigationController pushViewController:tmpVC animated:YES];
[tmpVC release];
}
This example assumes that you have all the data necessary for both view controllers in your ItemsViewController - that may not be the case..?
Anyway, by doing it this way, your DetailsViewController automatically loads the data. When you tap "Back" to go back to ItemsViewController, the UINavigationController would release it, destroying it. Then, when the user taps a different cell, we run this code again, creating a brand-new controller with brand-new data - so of course when it displays, it will load the data automatically - it's never displayed before.
What it sounds like you may be doing in your code is retaining the DetailsViewController as a property of the ItemsViewController class and then reusing the object. This can also work as well if you're concerned about allocations (for example, if it is a very "heavy" allocation to make a DetailsViewController), but then I think the best place to call reloadData is not inside the class itself - but rather from the didSelectRowAtIndexPath: method of ItemsViewController.
The reason I promote the creation/destruction approach as opposed to the "flyweight pattern" approach is that it keeps your code more separate - the fewer linkages between view controllers, the better. Of course, ItemsViewController will always dependo on and know about DetailsViewController, but it shouldn't necessarily have to be the other way around - and if you add the reloadData call to viewWillAppear:animated:, you're implicitly adding a non-code dependency between the two. You know that when ItemsViewController is the "parent" in the navigation stack, that's the right behavior -- but what if you suddenly started reusing that view in other part of your app that doesn't require a reload? It's a performance hit for one, and moreover, it's the kind of hidden dependency that may end up in a nasty-to-trace bug someday. So, I'd keep Details stupid and make Items contain all the complexity, if it is indeed required to only have 1 DetailsViewController (as opposed to my first idea of recreating it each time).
I would propose the reloadData and setTitle to be in the viewDidLoad and in the setter - I assume you set a property in DetailsViewController that changes the datasource of the table. So viewDidLoad reloads and sets the title, if the property has been set, the setter reloads and sets the title if isViewLoaded and the new value is different than the old one.
- (void)setSmth:(SmthClass *)value {
if (value == smth) // if they are the same and SmthClass is immutable,
// otherwise use isEqual and [self.tableView reloadData]
// before returning...
return;
id pointer = smth; // if it's a retain property
smth = [value retain];
[pointer release]; // release after retain just to be extra safe
if ([self isViewLoaded]) {
[self.tableView reloadData];
[self setTitle:title];
}
}
- (void)viewDidLoad {
if (smth) {
[self.tableView reloadData]; // maybe redundant...
[self setTitle:title];
}
}
Or you can use Key-Value observing (NSKeyValueObserving protocol) to observe your property and reloadData on notification...