Here's the deal:
I have a UICollectionView which populates itself from an array of objects. I want to be able to tap one of the views representing an object in my UICollection view and be taken to a new view displaying details of the object I selected in the UICollectionView (like the selected object's ID for example).
Unfortunately, whatever I try to set is always null!
Here is the object:
#interface obj : NSObject
#property (nonatomic, strong) NSString *objId;
...
#property (nonatomic, strong) NSString *imageURL;
#end
Here is the main view controller:
#interface ViewController : UIViewController <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
#property (weak, nonatomic) IBOutlet UICollectionView *badgesCollectionView;
#property (strong, nonatomic) NSArray *objArray;
...
#end
Here is the detail view:
#interface DetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *objId;
#end
Here is where I'm trying to populate the detail view (in my main ViewController.m)
// Implement action for object selection
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailView = [[DetailViewController alloc] init];
detailView.objIdLabel.text = [[self.objArray objectAtIndex:indexPath.row] objId];
[self.navigationController pushViewController:detailView animated:YES];
NSLog(#"Object Selection: Report");
NSLog(#"Label Text : %#", detailView.objId.text);
NSLog(#"Id of object : %#", [[self.objArray objectAtIndex:indexPath.row] objId]);
}
When I select an object in UICollectionView, I get a transition to a detailView without anything populated. Here is what's logged:
Object Selection: Report
Label Text : (null)
Id of object : f0f47920-f449-4fd0-a74e-804546905437
Any insight into this would be fantastic. Thanks!
The output of the UILabel's text field is null because the label property is not actually linked to anything until the view is loaded. In this case, it wont happen until after you push the view. In order to get around this, you can just have a property on your DetailViewController that holds the string until the view appears.
Then in viewDidLoad or viewWillAppear in DetailViewController, you can set the text on objIdLabel to the property that contains the string
The problem can come from the DetailViewController implementation.
DetailViewController *detailView = [[DetailViewController alloc] init];
only init the Controller
but you declare
#interface DetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *objId;
#end
wich means you must use a xib.
So you first have to use
DetailViewController *detailView = [[DetailViewController alloc] initWithNibName:#"DetailViewXIBName" bundle:nil];
then try to affect the value to objIdLabel once the controller is pushed:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailView = [[DetailViewController alloc] initWithNibName:#"DetailViewXIBName" bundle:nil];
[self.navigationController pushViewController:detailView animated:YES];
detailView.objIdLabel.text = [[self.objArray objectAtIndex:indexPath.row] objId];
NSLog(#"Object Selection: Report");
NSLog(#"Label Text : %#", detailView.objId.text);
NSLog(#"Id of object : %#", [[self.objArray objectAtIndex:indexPath.row] objId]);
}
You have to affect the text after [super viewDidLoad]; is called.
These are just assumptions as we only have limited information
It may push a blank view controller because you are not strongly holding onto the detailView.
Try adding:
#property (nonatomic, strong) DetailViewController *detailView;
to the main ViewController, then in the didSelectItemAtIndexPath method:
// Implement action for object selection
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
self.detailView = [[DetailViewController alloc] init];
self.detailView.objIdLabel.text = [[self.objArray objectAtIndex:indexPath.row] objId];
[self.navigationController pushViewController:self.detailView animated:YES];
}
It turns out this comes down to a xib's lifecycle. It is true, as many of you suggested, that the detailView is not actually linked with all of the items associated with it (like the objId UILabel) until it's loaded.
I ended up fixing this by creating a new singleton model object to hold onto the selected object. I was then able to set the selected object on the singleton from my main view controller and call it from the detailView to set my objId on ViewDidLoad.
Related
I have a custom UITableViewCell xib that has two buttons on it. An edit button, designed to pass the info for that cell into a new view controller is the one I am currently having issues with. I create a property for the TableViewController in my custom cell, and hook up the button to this code:
-(IBAction) editItem {
self.itemsList = [[TheItemsList alloc] init];
[self.itemsList editItem];
NSLog(#"EDIT");
}
Inside TheItemsList I have this code:
-(void)editItem {
NSLog(#"EDITAGAIN");
EditItem *detailViewController = [[EditItem alloc] initWithNibName:#"EditItem" bundle:nil];
detailViewController.selectedCountry = self.selectedCountry;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController.therow = indexPath.row;
//Pass the selected object to the new view controller.
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
The console prints my NSLog for each method, but the view does not push. This code was previously used in the didSelectForIndexPath part of the table view, so I know that the view controller it is pushing is fine, I just can't figure out what is going on here.
UPDATE:
I have it SOMEWHAT working, emphasis on the somewhat.
ItemCell.h
#import <UIKit/UIKit.h>
#import "TheItemsList.h"
#class TheItemsList;
#class ItemCell;
#protocol ItemCellDelegate <NSObject>
#required
-(void)editTheItem:(ItemCell *)theCell;
#end
#interface ItemCell : UITableViewCell
#property (assign) id <ItemCellDelegate> delegate;
#property (nonatomic, retain) IBOutlet UILabel *itemName;
#property (nonatomic, retain) IBOutlet UILabel *itemPrice;
#property (nonatomic, retain) IBOutlet UILabel *itemAisle;
#property (nonatomic, retain) IBOutlet UIImageView *thumbnail;
#property (nonatomic, retain) TheItemsList *itemsList;
#end
.m
#import "ItemCell.h"
#import "EditItem.h"
#implementation ItemCell
#synthesize itemName = _itemName;
#synthesize itemAisle = _itemAisle;
#synthesize itemPrice = _itemPrice;
#synthesize thumbnail = _thumbnail;
#synthesize itemsList, delegate;
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
-(IBAction) editItem {
[self.delegate editTheItem:self];
}
#end
Now, in the TableView
.h
#import <UIKit/UIKit.h>
#import "ItemCell.h"
#class ItemCell;
#protocol ItemCellDelegate;
#interface TheItemsList : UITableViewController <ItemCellDelegate>
.m
-(void)editTheItem:(ItemCell *)theCell {
NSLog(#"EDITAGAIN");
EditItem *detailViewController = [[EditItem alloc] initWithNibName:#"EditItem" bundle:nil];
detailViewController.selectedCountry = self.selectedCountry;
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController.therow = indexPath.row;
//Pass the selected object to the new view controller.
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
What I'm running into is two problems.
1, the first item in the tableview doesn't do anything when the button is pressed.
2, the other rows will run code, but it only does it for the first row's data.
You should use the delegate pattern inside your UITableViewCell to delegate the button press back to the UIViewController which contains the UITableView using the cell.
In cellForRowAt:IndexPath you then retrieve the cell and set its delegate like cell.delegate = self. In the view controller you implement the delegate and handle the action.
In the cell you setup a delegate variable (var delegate: MyCellDelegate?) and inside the editItem function you make a call to the delegate like self.delegate?.editItem()
Your issue is basically because of this line
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
On clicking the button on the cell, it does not select the row necessarily. The button takes the touch. A way to solve this would be to find the NSIndexPath of the cell in which the button is present and then do your operations with it. A way to do is to use the cell reference that is being supplied from the editAction delegate
-(void)editTheItem:(ItemCell *)theCell
Replace [self.tableView indexPathForSelectedRow] with [self.tableView indexPathForCell:theCell]
I have UITableViewController in my storyboard in which there are three cell with on label in the middle on each.
For instance if a user clicks on the first cell label in show a another tableview with list of Items by selecting an item it get back to the previous tableView and the item name should be printed in the place of the label.
#import <UIKit/UIKit.h>
#interface carSelectCellTableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *carMake;
#property (weak, nonatomic) IBOutlet UILabel *carModel;
#property (weak, nonatomic) IBOutlet UILabel *carRego;
#property (weak, nonatomic) IBOutlet UILabel *carYear;
//the below label are the labels in the cells.
#property (weak, nonatomic) IBOutlet UILabel *carSelected;
#property (weak, nonatomic) IBOutlet UILabel *location;
#property (weak, nonatomic) IBOutlet UILabel *service;
#end
#import "BookService.h"
#import <Parse/Parse.h>
#import "carSelectCellTableViewCell.h"
#interface BookService ()
#end
#implementation BookService
#synthesize tableView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.tableView.delegate = self;
self.tableView.dataSource = self;
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cell tabbed");
PFObject *temp = [customerCars objectAtIndex:indexPath.row];
NSLog(#"%#", temp.objectId);
NSString *car = temp.objectId;
UIStoryboard *dashboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *change = [dashboard instantiateViewControllerWithIdentifier:#"bookAservice"];
[self presentViewController:change animated:YES completion:nil];
static NSString *Identifier = #"carSelectedCell";
//here is where i'm calling the cell to change the label value when the selection is made. before dequeueReusableCellWithIdentifier there should be appropriate tableView Table.
carSelectCellTableViewCell *cell2 = [dequeueReusableCellWithIdentifier:Identifier];
cell2.carSelected.text = #"selcted";
}
How can i programmatically initiate the tableView. So that i can change the cell label value to the selected item.
Now If u suppose the First table View is names as ParentViewController and the second table view as childView u can do the Foll:
To do this make ParentController a delegate of ChildController. This allows ChildController to send a message back to ParentController enabling us to send data back.
For ParentController to be delegate of ChildController it must conform to ChildController's protocol which we have to specify. This tells ParentController which methods it must implement.
In ChildController.h, below the #import, but above #interface you specify the protocol.
#class ChildController;
#protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item;
#end
next still in the ChildController.h you need to setup a delegate property in ChildController.h
#property (nonatomic, weak) id <ChildControllerDelegate> delegate;
In ChildController we call a message on the delegate when we pop the view controller.
For this case the following will be called in didSelectRowAtIndex method
NSString *itemToPassBack = #"Pass this value back to ParentController";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
That's it for ChildController. Now in ParentController.h, tell ParentViewController to import Child and conform to its protocol.
import "ChildController.h"
#interface ParentController : UIViewController
In ParentController.m implement the following method from our protocol
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(#"This was returned from ChildController %#",item);
}
The last thing we need to do is tell ChildController that ParentController is its delegate before we push ChildController on to nav stack.
ChildController *ChildController = [[ChildController alloc] initWithNib:#"ChildController" bundle:nil];
ChildController.delegate = self
[[self navigationController] pushViewController:ChildController animated:YES];
today i have this problem:
Im doing so the title of the First cell of a tableview is show in my "Home" view controller.
This is what i have dun at the moment, but the label in homeviewcontroller is not getting the title text of the first cell.
Reminders.h:
NSMutableString *primerevento;
#property (strong, nonatomic) NSMutableString *primerevento;
Reminders.m:
#synthesize primerevento;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
....
UITableViewCell *cell = (UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
if(indexPath.row == 1){
cell.textLabel.text = primerevento;
}
HomeViewController.h
#property (weak, nonatomic) IBOutlet UILabel *promediototal;
HomeViewController.m
#synthesize promediototal
- (void)viewDidLoad
{
[super viewDidLoad];
....
Reminders *viewsiguiente = [[Reminders alloc] initWithNibName:nil bundle:nil];
promediototal.text = viewsiguiente.primerevento;
[self presentViewController:viewsiguiente animated:YES completion:NULL];
my label on homeviewcontroller appears in blanc, i dont know what im doing wrong, im really trying.
Thanks for your time guys
In the homeViewController don't forget to import the .h of the remindersViewController.
#import "reminders.h"
#property (weak, nonatomic) IBOutlet UILabel *promediototal;
Then in the homeViewController.m where you want your text - promediototal.text = reminders.primerevento.text;
#synthesize promediototal
- (void)viewDidLoad
{
[super viewDidLoad];
....
Reminders *viewsiguiente = [[Reminders alloc] initWithNibName:nil bundle:nil];
promediototal.text = reminders.primerevento.text;
[self presentViewController:viewsiguiente animated:YES completion:NULL];
We have probably still too view code to understand what you are up to.
There is one thing though:
Reminders *viewsiguiente = [[Reminders alloc] initWithNibName:nil bundle:nil];// 1)
promediototal.text = viewsiguiente.primerevento; // 2)
[self presentViewController:viewsiguiente animated:YES completion:NULL]; // 3)
1) What is the purpose of that? Firstyou create a brand new view controller object. As you do not create it from nib (which is fine) it cannot have any poperties set. so viewsiguiente.primerevento is nil at that point of time.
2) Now you take this nil value and assign it to promeditional.text which becomes nil to? What is the sense in that? Did you mean?
viewsiguiente.primerevento = promediototal.text;
3) And now you present the newly created view controller viewsiguiente. You did not make any changes to it. That may be fine if you implementation of Reminders takes care of the views. But you did not set any value from external.
I am creating an iPad application (iOS6.1) which has a master detail view concept. First view is a table view has list of items that are been loaded from Plist, when each row gets selected the second table view gets loaded with another Plist. theirs is my Detail view which has to display an UIView with a UILabel ans an UIImage. I am using didSelectRowAtIndexPath method . The first two table Views are been displayed properly and loads the row and display corresponding View but the last detail view which is supposed to display the UILabel and an image is empty, can any one help me to solve this problem
My Code for the didSelectRowAtIndexPath method is
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
TaskDetailViewController *newTaskDetailViewController = [[TaskDetailViewController alloc] init];
// pass the row to newDetailViewController
if (weekNumber == 0)
{
newTaskDetailViewController.taskdescription = [weeklist1 objectAtIndex:indexPath.row];
}
if (weekNumber == 1)
{
newTaskDetailViewController.taskdescription = [weeklist2 objectAtIndex:indexPath.row];
}
if (weekNumber == 2)
{
newTaskDetailViewController.taskdescription = [weeklist3 objectAtIndex:indexPath.row];
}
// ...... repeated for 39 times because of the list
newTaskDetailViewController.taskNumber = indexPath.row;
[self.navigationController pushViewController:newTaskDetailViewController animated:YES];
}
DetailView header
#import <UIKit/UIKit.h>
#interface TaskDetailViewController : UIViewController
#property int taskNumber;
#property(strong , nonatomic) NSString *taskdescription;
#property (nonatomic , strong) NSMutableDictionary * tasks;
#property (strong, nonatomic) IBOutlet UIImageView *questionImage;
#property (strong, nonatomic) IBOutlet UILabel *displayText;
#end
Implemetation file has
#implementation TaskDetailViewController
#synthesize taskNumber;
#synthesize taskdescription;
#synthesize tasks;
#synthesize displayText;
#synthesize questionImage;
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = taskdescription;
NSLog(#"%#", taskdescription);
}
Your problem is using alloc init to create an instance of TaskDetailViewController. You've created that controller in the storyboard so you should instantiate it from the storyboard using an identifier that you give it (DetailViewController in my example):
TaskDetailViewController *newTaskDetailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
I am using static UITableView to show profile of a customer.
It has SelectCountryCell to select country which, when clicked, opens new UITableView with country list.
When I select country from Country TableView, It should display in previous TableView's SelectCountryCell.
How do I do this?
You should bind the static cells to outlets in your UITableViewController and put the profile syncing methods to the - viewWillAppear method.
When the user changes the country in the Country list the profile updates. After that when the user moves back to the UITableViewController instance with static content the profile data will be automatically updated.
You can define a delegate in your CityTableView and then define a method in this delegate.
You should realize this method in your CountryTableView.
Then you may get what you want.
I only offer you a thought. You should find the detail by yourself.
MasterViewController.h
#import "DetailViewController.h"
#interface MasterViewController : UITableViewController <DetailProtocol> // Note this.
#property (strong, nonatomic) DetailViewController *detailViewController;
#property (strong, nonatomic, readwrite) NSString *selectedCountry;
#end
MasterViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.detailViewController) {
self.detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
self.detailViewController.delegate = self; // THIS IS IMPORTANT...
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
// Note this -- It's a delegate method implementation
- (void)setCountry:(NSString *)country
{
self.selectedCountry = country;
}
DetailViewController.h
#protocol DetailProtocol <NSObject>
-(void)setCountry:(NSString *)country;
#end
#interface DetailViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property (unsafe_unretained) id <DetailProtocol> delegate; // Note this
#end
DetailViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self.delegate setCountry:[countries objectAtIndex:indexPath.row]]; // Note this
[self.navigationController popViewControllerAnimated:YES];
}