my App is kind of a FileManager like the Finder under MAC OS. I can show up the root of the filesystem. But the Navigation through the directories makes problems.
In the method didSelectRowAtIndexPath i create a new instance of the current TableViewController. I set the Delegate and DataSource toself. Now the Problem that i have: The TableViewCells of the new created instance do not have a resueIdentifier. The value is NULL. And i cant set it because its read-only. So, how can i get the new cells to have a specific reuseIdentifier?
Below is my Code from the didSelectRowAtIndexPath- Method
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if(cell.accessoryType == UITableViewCellAccessoryDisclosureIndicator)
{
NSLog(#"Entered Directory");
NSLog(#"reuseIdentifier: %#",cell.reuseIdentifier); // <- value: "fileCell"
FileTableViewController *newDir = [[FileTableViewController alloc] init];
newDir.tableView.dataSource = self;
newDir.tableView.delegate = self;
[newDir setDirectory:cell.textLabel.text];
UITableViewCell *newCell = [newDir.tableView cellForRowAtIndexPath:indexPath];
NSLog(#"reuseIdentifier NewCell: %#",newCell.reuseIdentifier); // <- is NULL
//[newDir setTitle: [fileManager currentDirectoryPath]];
/*[self setDirectory:cell.textLabel.text];
self.title = [NSString stringWithFormat:#"%#", [fileManager currentDirectoryPath]];
[self.tableView reloadData];
*/
[self.navigationController pushViewController:newDir animated:YES];
}
}
if you need more Code to help me, just let me know.
Thanks, Chris
Maybe I'm too late with this answer. But here're my thoughts.
UITableViewCell *newCell = [newDir.tableView cellForRowAtIndexPath:indexPath];
NSLog(#"reuseIdentifier NewCell: %#",newCell.reuseIdentifier); // <- is NULL
It is NULL because the tableView is not created. It will be created just after you make a push.
If you want to pass some params to your table, you should implement the variables in the FileTableViewController.h, and then in your didSelectRowAtIndexPath make use of this like:
FileTableViewController *newDir = [[FileTableViewController alloc] init];
newDir.SomeData = cell.textLabel.text;
One more important thing - if the FileTableViewController is a UITableViewController, you should not set the dataSource and delegate, especially in your current viewController. Because doing this, you set delegate to self of current viewController (not the FileTableVC that was created).
Otherwise, if FileTableVC is not UITAbleViewController Class, you should make the delegate and datasource in FileTableVC's viewDidLoad.
Related
I am new to iOS Development so please forgive me for asking silly questions or performing silly actions.
So now, here's the problem. I am developing an app, I have a tab bar controller as the initial, called [Initial Tab View]. One of the tab is a table view that displaying all the items, lets call this [Item View]. Once the user tap on the cell, it will push to another view and show the details, the [Detail View]. Another tab is also a table view but with static cells, [Static View], which I use it to select an item and return.
For a clearer picture: If I access the [Item View] from the initial tab view, just simply follow [Initial Tab View]->[Item View]->[Detail View]. However, if I access the [Static View] first and then go to the [Item View], the procedure will be like [Initial Tab View]->[Static View]->[Item View]->[Static View].
I have no idea how to implement the second one (with the [Static View] one) in [Item View], can anyone help? Or there is another better approach? Many thanks.
// Code from [Item View]
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"CustomShopCell";
ShopCustomTableCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[ShopCustomTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:identifier];
}
if (searchResult != nil) {
//cell.textLabel.text = [searchResult objectAtIndex:indexPath.row];
Shop *shop = searchResult[indexPath.row];
if ([shop.shopNameChi length] == 0) {
cell.lblName.text = shop.shopName;
} else {
cell.lblName.text = [NSString stringWithFormat:#"%# | %#", shop.shopName, shop.shopNameChi];
}
cell.lblLocation.text = shop.unit;
cell.imgImage.contentMode = UIViewContentModeScaleAspectFit;
cell.imgImage.image = [UIImage imageWithData:shop.imageData];
} else {
//cell.textLabel.text = [list objectAtIndex:indexPath.row];
Shop *shop = shopList[indexPath.row];
if ([shop.shopNameChi length] == 0) {
cell.lblName.text = shop.shopName;
} else {
cell.lblName.text = [NSString stringWithFormat:#"%# | %#", shop.shopName, shop.shopNameChi];
}
cell.lblLocation.text = shop.unit;
cell.imgImage.contentMode = UIViewContentModeScaleAspectFit;
cell.imgImage.image = [UIImage imageWithData:shop.imageData];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(![self isKindOfClass:[NavigationMainTableViewController class]]){
[self performSegueWithIdentifier:#"showShopDetail" sender:self];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
What you can do (if i'm sure i understood what you're doing) is create two segue links with differnt identifiers (with a clear name, I like this formatting "fromHereToThere"). Then in your didselect you can just call your segue with an if statement, like so :
if (your condition){
[self performSegueWithIdentifier:#"fromHomeToStatic" sender:self];
}else{
[self performSegueWithIdentifier:#"fromHomeToDetail" sender:self];
}
Then in your prepareForSegue method (the one in comment by default in every controller class) you can choose what data to pass along, with
if ([segue.identifier isEqualToString:#"fromHomeToDetail"])
{
//Manage your usual stuff here for the detail identifier
//Ask me again if you need extra help with that
}
if ([segue.identifier isEqualToString:#"fromHomeToStatic"]){
//Manage here for the static identifier
}
But i'm not really exactly sure what you meant with your question so i might be completly off topic :D
EDIT : Okay apparently you wanna know where you're coming FROM when you're loading your third controller. So here is a way to do that.
In your destination controller, add this in .h
#property (nonatomic) BOOL fromStatic;
(or whatever you want really)
Then in your origin controller, in the prepareForSegue, if you're coming from Static, set the BOOL of the next controller to YES, if not, set it to NO, like this
DetailViewController *dvc = segue.destionationViewController;
dvc.fromStatic = YES; //if you're writing this from the static controller
In the viewDidLoad of the destination controller just check if it's YES or NO and react accordingly.
Am I getting this right or am I still confused with your problem? I'm still not completly sure what you're asking haha :D
Try to use this one. You can set that method for each cell you taping.
[self performSegueWithIdentifier:#"yourIdentifierHere" sender:self];
If you are using Static Cell Prototype . Just drag from cell to the view which you want to push and select push in segue . If you are using dynamic UITableViewCell. You can performSegue in didSelectRowAtIndex Method .
I have created a custom UITableViewCell using the nib file. Then when I select the my custom UITableViewCell, I want to perform a segue and pass the data to the detail view controller. I have already set the segue identifier in the storyboard, and I put the prepareForSegueWithIdentifier in the - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath However, when I perform the segue, only the first row of the table view can be shown on the detailed page.
I have three questions:
1. How to set the segue identifier for the custom UITableViewCell?
Since I create the custom UITableViewCell in the xib file, it is difficult to connect the xib file with the main storyboards with other view controllers.
2. If I change the class name of the table view cell in the storyboard, will it be my custom UITableViewCell?
Specifically, in order to replace the default UItableViewCell in the storyboard to my custom UITableViewCell, I change the name of the class of the default UITableViewCell in the storyboard, to the name of my custom UITableViewCell. Then I Control + Drag to create a push segue and set the segue identifier. But it seems this one doesn't work either.
3. Why every time I perform segue, the destination view controller only show the specific content of the first row of my table view?
That is, whenever I select the first cell, the second cell, the third cell......, all I get after segue to the destination view controller is the content of the first cell. I am wondering whether the sender is my custom UITableViewCell? Or is the indexPath is nil? I am not sure.
My code is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
customTableViewCell *cell;
cell = (customTableViewCell *) [tableView dequeueReusableCellWithIdentifier:#"myCell"];
if (!cell) {
NSArray *nib = [[NSBundle mainBundle]loadNibNamed:#"customTableViewCell" owner:nil options:nil];
cell = [nib objectAtIndex:0];
}
NSDictionary *photo = self.recentPhotos [indexPath.row];
dispatch_queue_t queue = dispatch_queue_create("fetch photos", 0);
dispatch_async(queue, ^{
NSURL *imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatSquare];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *title = [photo valueForKeyPath:FLICKR_PHOTO_TITLE];
cell.cellLabel.text = title;
UIImageView *cellImageView = [[UIImageView alloc]initWithImage:image];
[cellImageView setFrame: CGRectMake(10, 5, 45, 45)];
[cellImageView.layer setCornerRadius:cellImageView.frame.size.width/2];
cellImageView.clipsToBounds = YES;
[cell addSubview:cellImageView];
});
});
return cell;
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"go" sender:indexPath];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSDictionary *photo = self.recentPhotos [indexPath.row];
NSURL *imageURL = [FlickrFetcher URLforPhoto:photo format:FlickrPhotoFormatLarge];
NSString *title = [photo valueForKeyPath:FLICKR_PHOTO_TITLE];
if ([segue.identifier isEqualToString:#"go"]) {
if ([segue.destinationViewController isKindOfClass:[ViewImageViewController class]]) {
ViewImageViewController *vivc = (ViewImageViewController *) segue.destinationViewController;
vivc.imageURL = imageURL;
vivc.title = title;
}
}
}
Did you try debugging the method and looking at the value of sender? You provided the indexPath in the call to performSegueWithIdentifier:sender: but the you use the sender as a cell in indexPathForCell:.
I already see problems in your tableView:cellForRowAtIndexPath: method as well. While scrolling the cells get reused, so when you perform a async call the block can and will be executed on the wrong cell. To prevent that call the dequeueReusableCellWithIdentifier: again inside the dispatch_async callback.
Also you are adding a subview directly to the cell instead of using the contentView property.
Regarding your questions, as far as I know you cannot connect a segue from a nib file. To make it work properly you need to connect the segue from the prototype cell in the storyboard. You created a manual segue and you're calling it when the user touches the cell. I think that it'll be easier for you to understand what's happening if you don't use segues at all and just call showViewController:sender: yourself.
I am stucked in a stupid problem since two days. I have got a UITableViewController pushed in Navigation Controller. When it loads, since there is no data, so empty table is visible:
But when I receive data from server, and call [self.tableView reloadData], both numberOfRowsInSection and heightForRowAtIndexPath get invoke except cellForRowAtIndexPath and my controller is shown without table:
I can't really understand that why it is happening. All datasource methods are called except for cellForRowAtIndexPath. Please someone guide me... Thanks..
ActLogController.h
#interface ActLogController : UITableViewController<ASIHTTPRequestDelegate,UITableViewDataSource,UITableViewDelegate>
#property(strong) NSMutableArray *activityKeys;
#end
ActLogController.m
- (void)viewDidLoad
{
[super viewDidLoad];
activityKeys = [[NSMutableArray alloc] init];
self.tableView.dataSource = self;
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self retrieveActivityLogFromServer];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return activityKeys.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
ActivityLogUnit *act = [activityKeys objectAtIndex:indexPath.row];
cell.textLabel.text = act.price;
return cell;
}
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50.0;
}
-(void) requestFinished:(ASIHTTPRequest *)request
{
NSArray *list = [request.responseString JSONValue];
for (int i = 0; i < list.count; i++) {
NSArray *singleTrade = [[list objectAtIndex:i] JSONValue];
ActivityLogUnit *unit = [[ActivityLogUnit alloc] init];
unit.symbol = [singleTrade objectAtIndex:0];
unit.type = [singleTrade objectAtIndex:1];
unit.price = [singleTrade objectAtIndex:2];
unit.volTraded = [singleTrade objectAtIndex:3];
unit.remVol = [singleTrade objectAtIndex:4];
unit.actualVol = [singleTrade objectAtIndex:5];
unit.recordID = [singleTrade objectAtIndex:6];
unit.orderNo = [singleTrade objectAtIndex:7];
unit.time = [singleTrade objectAtIndex:8];
[activityKeys addObject:unit];
}
if(activityKeys.count > 0)
{
[self.tableView reloadData];//it is called and I have got 6 items confirm
}
}
EDIT
I set some dummy data in my array activityKeys, Data is being displayed in table, and cellforrowatindexpath is called successfully. But as I reload data after sometime, other methods are called except this one and table disappears as shown in 2nd pic. Any ideas?
Your problem is that you probably download the data content on a background thread. Since you cannot update the UI on a background you need to call [self.tableView reloadData] on the main thread once the download is finished!
Hope it helps!
Looks like you in secondary thread, do reloadData in main thread by using following code
[self.tableView performSelectorOnMainThread#selector(reloadData) withObject:nil waitUntilDone:NO]
You can always use [NSThread isMainThread] to check whether you are in main thread or not.
you have to write in viewdidload
self.tableView.dataSource = self;
self.tableView.delegate = self;
Edit
You have no xib then where you are declared/sets your tableview's properties. Like
self.tableView.frame = CGRectMake(0, 45, 320, 500);
self.tableView.rowHeight = 34.0f;
self.tableView.separatorStyle=UITableViewCellSeparatorStyleNone;
[self.view addSubview:self.tableView];
[self.tableView setBackgroundColor:[UIColor clearColor]];
self.tableView.showsVerticalScrollIndicator=NO;
self.tableView.dataSource = self;
self.tableView.delegate = self;
Try with
#property(nonatomic,strong) NSMutableArray *activityKeys;
Firstly I strongly believe that the instance name of the tableview should not be similar to the local variable (i.e. tableView in class should not be equal to tableView in delegate and data source methods).
Second in your question posted I could not see the delegate set for the table view.
answer Posted By Samir Rathod should work if you have #property for the table view set in you .h or .m file.
You can also do this if you have a XIB file.
Press ctrl and click + drag the tableview to the files owner and set the delegate and datasource.
For me the problem was my stubbed-out code returning 0 as the number of sections (so it never asked how many rows were in the section, and never got their data). Just change that to 1 if it's your problem also. Additionally, I was working in Swift, where the issue mentioned by #shahid-rasheed is coded (slightly) differently:
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
At last I got it worked. cellForRowAtIndexPath was not being called because of a line of code I didn't mention here... which was actually removing some color background layer from view. It was causing reloading issue. After removing it, everything works fine.
Thank you all of you for your cooperation :)
I had the same symptoms too. In my case, the first time I loaded the data (from core data) in viewDidLoad, NSSortDescriptor was used to sort the data.
On the click of a button, the core data was fetched again (this time with changes) and tableView data reloaded. It initially gave me a blank table view after the button was clicked because I forgot to sort the data the second time I fetched it.
Learning points: Remember to call all methods which modify the cell (like background color mentioned by iAnum, or NSSortDescriptor in my case) if you have used them in the beginning!
I have created StoryBoard segue application containing 1st CategoryViewController and 2nd CheckListTableController. Category(buttons) wise I am opening different question list on tableview of CheckListTableController.
I have set check and uncheck button and its respective colour in tableView cell. I have taken NSMutableArray and adding indexPath to this to set the particular colour to cell.
When I am navigating between these controller I want my tableView cell colour should be reserve.
Any Idea how to do this. Thanks in advance.
Edited with query 26Aug13
Now I need same functionality by using plist file or Core data. So that user can save sessions of task and access it later. Eg. in session one he did check uncheck mark for ABC building then session saved. Session two again he did check/unchk activity for different location XYZ building then saved.
Which method would be suitable here. Plist file or Core data? Can you redirect me to specific solution.
Edit with some Code
Catergory_ViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"chkListTable"])
{
NSString *bldgArrayString = [[NSString alloc] initWithFormat:#"bldg"];
checkListTableViewController *chk= segue.destinationViewController;
chk.gotquestArrayString = bldgArrayString;
}
}
checkListTableViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
if ([gotquestArrayString isEqual:#"bldg"])
{
buildingQuest = [[NSMutableArray alloc]initWithObjects:#"Motor vehicles are not parked in the space",
#"Lightning protection system (Electronic)",
#"Lightning protection systems available on all buildings",
#"Reception facilities in order and not damaged",
}
}
//tableView button method where indexPath added to MutableArray
-(void)yesAction:(id)sender
{
arrayCheckUnchek = [[NSMutableArray alloc]init];
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
UITableView *curTableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [curTableView indexPathForCell:cell];
cell.backgroundColor = [UIColor greenColor];
[arrayCheckUnchek addObject:indexPath];
}
- (void)tableView: (UITableView*)tableView
willDisplayCell: (UITableViewCell*)cell
forRowAtIndexPath: (NSIndexPath*)indexPath
{
// UITableViewCell *Cell = [myChklist cellForRowAtIndexPath:indexPath];
if ([arrayCheckUnchek containsObject:indexPath])
{
cell.backgroundColor = [UIColor greenColor];
}
}
1.you need to save your NSMutable Array in Plist file or in UserDefaluts whenever you have changed the selection(checked/ unchecked) at that time you should update your Plist or userdefaluts
2.Read your plist file in ViewWillApper method in your tableviewcontroller. and pass this data to your "arraycheckuncheck"
hope it will work for you
happy icoding ....
NSAnant
I'm using Core Data to manage Thing(s) and then UITableViewController/UITableViewCell to present a list of Things. However, a Thing object has more data than just what I display in the text property of the UITableViewCell, and when I segue from the UITableViewController to a more detailed view controller of Thing, what I really want is a pointer to the Thing object so I can pass it on to the next view controller.
For example:
-(UITableViewCell*)tableView:(UITAbleView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ENTRY_LOG;
// Ask the NSFetchedResultsController for the NSManagedUserObject at the row in question
Thing* thing = [self.fetchedResultsController objectAtIndexPath:indexPath];
// For brevity skip dequeueReusable...
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.text = thing.name;
EXIT_LOG;
return cell;
Now, assume I assume the segue connected appropriately:
- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {
ENTRY_LOG;
UITableViewCell* cellSender = (UITableViewCell*)sender;
// I want to do this:
Thing* thingHandle = [cellSender someMessageToGetThingReference];
[segue.destinationViewController setThing:thing];
// and I don't want to do this
// setup NSFetchRequest and predicate to get thing where 'name = cellSender.text'
[segue.destinationViewController setThing:thing];
EXIT_LOG;
}
If I could get the indexPath that the cell is at I could then use my NSFetchResultsController to at least get the objectAtIndexPath again, perhaps call - (NSIndexPath *)indexPathForSelectedRow in the prepareForSegue against the UITableView and proceed from there.
Perhaps I am missing something obvious, but to get the cell's index path, assuming you are segueing from a UITableViewController, can't you use:
NSIndexPath *indexPath = [[self tableView] indexPathForCell:cellSender];