I see this problem all over the net, and none of the solutions listed work for me!
I have added UIButton to a UITableViewCell in IB. I have assigned a custom UITableViewCell class to the UITableViewCell. My custom UITableViewCell class has an IBAction that im connecting to the Touch Up Inside event (I have tried other events they all don't work) but the IBAction function is never called!
There is nothing else in the UITableViewCell, I have added the UIButton directly into the Content View. And everything has user interaction enabled! It is as simple as that I have nothing complex going on!
What is is about a UITableViewCell that stops buttons working?
EDIT: By request the code where I initialize my UITableViewCells the custom class is called DownloadCell
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DownloadCell";
DownloadCell *cell = (DownloadCell *)[aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
UIViewController * cellController = [[UIViewController alloc] initWithNibName:#"DownloadCell" bundle:nil];
cell = (DownloadCell *)cellController.view;
[cellController release];
}
SoundLibrarianIPhoneAppDelegate * del = (SoundLibrarianIPhoneAppDelegate *)[UIApplication sharedApplication].delegate;
DownloadQueue * dq = del.downloadQueue;
DownloadJob * job = [dq getDownloadJob: indexPath.row];
[job setDelegate:self];
SoundInfo * info = [job sound];
NSArray * seperated = [info.fileName componentsSeparatedByString: #"."];
NSString * displayName = [seperated objectAtIndex:0];
displayName = [displayName stringByReplacingOccurrencesOfString:#"_" withString:#" "];
displayName = [displayName capitalizedString];
[cell.titleLabel setText: displayName];
cell.progressBar.progress = job.percentCompleted;
[cell.progressLabel setText: [job getProgessText]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = YES;
[cell setDelegate:self];
return cell;
}
Seems the problem was to do with me updating the table cells too often, so this was interrupting any interaction with the cells themselves
have U placed this code int the table view.?
tableView.delegate=self;
tableView.dataSource=self;
Thanks,
bharath
Related
As title says I would like to have 3 labels in my cell (in tableView). As can be seen in the code below I currently only have 2 labels which are name, as textLabel and book as detailTextLabel. But what if I also would like chapter as a label (own row in the tabelView cell)? What would be the best way to implement this?
The output should look like this in the tableView:
Name
Book
Chapter
/Thanks in regards!
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"BookmarkCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Bookmark *item = [self.items objectAtIndex:indexPath.row];
NSArray *chunks = [item.name componentsSeparatedByString: #","];
NSString *name;
NSString *book;
NSString *chapter;
if ([chunks count] > 0)
{
name = [chunks objectAtIndex:0];
if ([chunks count] > 1)
{
book = [chunks objectAtIndex:1];
if ([chunks count] > 2)
{
chapter = [chunks objectAtIndex:2];
}
}
}
cell.textLabel.text = name;
cell.detailTextLabel.text = book;
Sounds like your going to want a custom UITableViewCell and with that you can add anything you would like into it. From there just name the labels that you put into it and write the code accordingly to fill them with the cellForRowAtIndexPath method.
Create a custom UIView and add it as a subview to the cell's contentView. In this custom view add as many UILalels as you would like. You don't actually have to create a custom view to do this, but it allows for greater versatility.
Here is some basic code to achieve three labels.
UIView * pNewContentView= [[UIView alloc] initWithFrame:cell.contentView.bounds];
CGRect labelFrame= pNewContentView.bounds;
labelFrame.size.width= labelFrame.size.width * 0.33;
UILabel* pLabel1=[[UILabel alloc] initWithFrame:labelFrame];
[pNewContentView addSubview:pLabel1];
labelFrame.origin.x= labelFrame.size.width;
UILabel* pLabel2=[[UILabel alloc] initWithFrame:labelFrame];
[pNewContentView addSubview:pLabel2];
labelFrame.origin.x= labelFrame.origin.x + labelFrame.size.width;
UILabel* pLabel3=[[UILabel alloc] initWithFrame:labelFrame];
[pNewContentView addSubview:pLabel3];
[cell.contentView addSubview:pNewContentView];
If what you need is simply another label, then you can do what mark says, create a subview and set it to your cell.
If you want to have something can do more, what you need is a custom UITableViewCell. You can define buttons and other controls. Take a look at this document apple provide. This document can really help to understand how UITableViewCell works so it's worth reading.
In this screen shot you can see that I have added UITableView in UIViewController then customized the UITableViewCell by adding some labels in it. But the issue is when I run the application. All of the cells are empty. There are no labels at all.
I am not getting what can be the issue. I have searched the web and read tutorials but couldn't resolve it.
I resolved this issue by myself, just after little effort.
Actually when you create a custom cell you have to do these things:
Setup the labels, images etc on storyboard cell.
Create a custom cell class (inheriting from UITableViewCell)(CustomCell.h & .m), CustomCell.h having all of the properties as iboutlet for labels, images etc. and synthesize them all in implementation.
After creating this custom class go back to storyboard, select the custom cell, change its class to the CustomCell and give it some identifier like "MyCustomCell", then right click on the custom cell and connect the IBOutlets with labels etc.
Now import CustomCell class in the class where you are implementing the UITableView and use the properties you defined in CustomCell class.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyCustomCell";
CustomCell*cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the provided setImageWithURL: method to load the web image
// Ensure you use a placeholder image otherwise cells will be initialized with no image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://example.com/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
cell.myCustomLabel.text = #"My Text";
return cell;
}
I did this all and my issue was resolved and please don't forget to connect the delegates & datasource and table.
Hope this will help others.
It is little bit late but you can solve your problem by setting the background color of your view as Clear Color from your storyboard.
in the tableview's delegate method , cellforrowatindexpath has a uitableviewcell inside it , there should be an identifier in the initialization of this cell. possibly it is "cell" or "cellIdentifier" .
you just need to select your cell from storyboard and enter this identifier string to the storyboard , where uitableviewcell's attribute inspector stays.
hope this helps.. :)
First Set cell identifier in storyboard #"Cell"
then set tag of label
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Create
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
UILabel *lblDate = (UILabel*)[cell viewWithTag:101];
UILabel *lblDistance = (UILabel*)[cell viewWithTag:102];
UILabel *lbltype = (UILabel*)[cell viewWithTag:103];
lblDate.text = [temp objectAtIndex:indexPath.row] valueForKey:#"date"] stringByReplacingOccurrencesOfString:#"-" withString:#"/"]];
lblDistance.text = [NSString stringWithFormat:#"%# KM",[[temp objectAtIndex:indexPath.row] valueForKey:#"distance"]];
NSString *type = [NSString stringWithFormat:#"%#",[[temp objectAtIndex:indexPath.row] valueForKey:#"distance"]];
if([type isEqualToString:#"0"])
{
lbltype.text = #"Personal";
}
else
{
lbltype.text = #"Bussiness";
}
// Configure
return cell;
}
I have no idea what's wrong with my program...
I think something bad with the memory management, cos every time I try to execute the app i have a different result from the simulator.
When I run the app everything works fine. The date formatter works fine! I can see in the table all the cell formatted in the right way!
The interface is a tabController whit 2 tableView to show the content of a database and a tab with a view used to add element to the db.
If I go in the "Add Tab" i can add all the element I want, but when i switch back to the others tab the program crash with an "Exe_Bad_Access" (in the code below).
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MovieTableCell" owner:self options:NULL];
cell = nibLoadedCell;
}
// Configure the cell.
UILabel *itemLabel = (UILabel *) [cell viewWithTag:1];
UILabel *priceLabel = (UILabel *) [cell viewWithTag:2];
UILabel *groupLabel = (UILabel *) [cell viewWithTag:3];
UILabel *dateLabel = (UILabel *) [cell viewWithTag:4];
NSDictionary *rowVals = (NSDictionary *) [shoppingListItems objectAtIndex:indexPath.row];
NSString *itemName = (NSString *) [rowVals objectForKey:#"item"];
itemLabel.text = itemName;
int groupid = [(NSNumber *) [rowVals objectForKey:#"groupid"] intValue];
groupLabel.text = Group[groupid];
NSNumber *price = (NSNumber *) [rowVals objectForKey:#"price"];
priceLabel.text = [priceFormatter stringFromNumber: price];
NSDate *dateValue = (NSDate *) [rowVals objectForKey:#"dateadded"];
NSString *str = [dateFormatter stringFromDate:dateValue]; //-->Here I got the Bad Access
[dateLabel setText:str];
return cell;
[itemLabel release];
[groupLabel release];
[priceLabel release];
[dateLabel release];
}
Here is the entire program, if someone want to have a look: http://cl.ly/A1yk
3 things:
1) Anything after your return statement will not run. The 4 lines after that will never get run.
return cell;
[itemLabel release];
[groupLabel release];
[priceLabel release];
[dateLabel release];
2) If those release statements did run, the next time you access those labels you will get a bad access error, because those UILabels will get deallocated. Don't call 'release' on any object you haven't called 'retain' on.
3) To understand if anything is wrong with dateFormatter, we'd have to see every piece of code that touches that variable.
Look at the memory management of dateFormatter. It's may be being over released. You can check by adding a
NSLog(#"Date formatter: %#", dateFormatter);
before the string call and see what shows up.
By they way, remove the [itemLabel release] etc. lines. (a) they are not being executed as they follow your return cell and (b) if they were called, they'd cause problems.
Your casting NSDate to the objectAtIndex, are you sure the object is not some other class is it? Also, is the dateFormatter variable initialised as it could be nil?
Try removing the release lines at the end. As you are not allocating the UILabel its better not to release those. Hope this helps!
I cant be sure if this might be the reason, coz exc_bad_access can occur for anything. But it seems like when you are reusing the same cell, you are never allocating it urself but instead getting it as nibloaded cell from the interface builder.
Try using the default code where u alloc/init a cell:
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"MyCell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell =
[[[UITableViewCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:cellIdentifier]
autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.text = label;
return cell;
}
I have a custom cell in a table View, see below.
I have two textFields txt1 and txt2 in my custom cell, as seen below
How can i access the values i enter in each of the text fields so that i can add the values in to seperate Arrays...
the "Addset" button will increment the no of sections of the grouped table View there by incrementing the one more set.
my table view code is as follows.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID= #"catogoryCell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:cellID];
if(cell==nil)
{
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:nil options:nil];
for(id currentObject in nibObjects)
{
if([currentObject isKindOfClass: [CustomCell class]])
{
cell = (CustomCell *)currentObject;
}
}
}
//cell.txt1.text = #"0";
return cell;
}
Thanks all..
Cells are reusable, so they need a more persistent way to keep their info.
Method 1:
You could hold some UITextField objects into an array in case you don't want to reuse the textfields as well, and at cellForRowAtIndexPath you'd only need to set the textfields to their cells such as:
cell.txt1 = [textFieldsArray objectAtindex:indexPath.section*2];
cell.txt2 = [textFieldsArray objectAtindex:indexPath.section*2+1]; //txt1 and txt2 properties should have assign
Method 2:
If you want to reuse the textfields as well I suggest using an array with mutable dictionaries, each dictionary holding the 'settings' for a cell. The textfields will be fully managed by the custom cell (e.g: at the UIControlEventValueChanged event update #"txt1" or #"txt2" values from the dictionary attached to the cell).
///somewhere in the initialization (in the class holding the tableview)
contentArray = [[NSMutableArray alloc] init];
///when adding a new cell (e.g: inside the 'Add set' action)
[contentArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:#"", #"txt1", #"", #"txt2", nil]];
//add a new cell to the table (the same way you do now when tapping 'Add set')
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
...
[cell attachDictionary:[contentArray objectAtIndex:indexPath.section]];
return cell;
}
///anywhere where you'd like to access the values inserted inside a cell
NSMutableDictionary *cell3Content = [contentArray objectAtIndex:3];
NSString *text1 = [cell3Content valueForKey:#"txt1"];
NSString *text2 = [cell3Content valueForKey:#"txt2"];
///CustomCell.m
-(id)initWithCoder:(NSCoder *)decoder{
self = [super initWithCoder:decoder];
if(!self) return nil;
[txt1 addTarget:self action:#selector(txt1Changed:) forControlEvents:UIControlEventValueChanged];
[txt2 addTarget:self action:#selector(txt2Changed:) forControlEvents:UIControlEventValueChanged];
return self;
}
-(void)attachDictionary:(NSMutableDictionary *)dic{
contentDictionary = dic;
txt1.text = [contentDictionary valueForKey:#"txt1"];
txt2.text = [contentDictionary valueForKey:#"txt2"];
}
-(void)txt1Changed:(UITextField *)sender{
[contentDictionary setValue:txt1.text forKey:#"txt1"];
}
When you make the IBOutlet connections in your UITableViewCell subclass, connect them to properties in the File Owner (the viewController), instead of the view itself. That way you'll be able to access them from your viewController (the UItableViewDataSource)
When i scroll down my UITableView, it starts showing me the same cells that i've already seen, and scrolling around a bit continues to put cells in the wrong place.
Here's the code i'm using. If anything additional is needed then let me know:
.h
#interface HomeViewController : UITableViewController {
int numberOfRows;
NSArray *allVaults;
}
#property (nonatomic, assign) int numberOfRows;
#property (nonatomic, retain) NSArray *allVaults;
#end
.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSFileManager *fileManager = [NSFileManager defaultManager];
self.allVaults = [fileManager contentsOfDirectoryAtPath:vaultsPath error:nil];
numberOfRows = [self.allVaults count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return numberOfRows;
}
- (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] autorelease];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.allVaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell.backgroundView = [AHCellCreation backgroundView];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.selectedBackgroundView = [AHCellCreation selectedBackgroundView];
cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
}
return cell;
}
Any help is appreciated!
EDIT 1: Image to show what happens when i move most code outside the (cell == nil) if statement:
Before:
After:
EDIT 2:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 82;
}
It looks as if you are only setting the cell content when the you're getting a nil back from dequeueReusableCellWithIdentifier. You need to set the cell contents every time, not just when you need to create a new cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
AHCell *cell = (AHCell*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// create a new cell if there isn't one available to recycle
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell = [AHCell blankCell];
}
// set the contents of the cell (whether it's a new one OR a recycled one)
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.allVaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell.backgroundView = [AHCellCreation backgroundView];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.selectedBackgroundView = [AHCellCreation selectedBackgroundView];
// cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
[cell populateAHCellWithDictionary: dictionary];
return cell;
}
Update updated code to address second issue. Rework AHCell so that the class method, e.g. blankCell returns a new cell with the subviews set up and the instance method, e.g. populateAHCellWithDictionary: sets the content.
In that case AHCellCreation class must add the subviews to the cell and then set the text in one go? You need to layout the cell inside the if statement (add the subviews, UILabels, UIImageView etc, and set their frames etc). And set the content outside the if statement.
Basically whatever doesn't change in each row put inside the if statement, but what changes from row to row put outside the if statement.
This is because the code inside the if statement is only reached when the cell is created, almost always its the cells that are visible on the screen when the Table view loads are created.
When you scroll down the cells that disappear off the top of the screen are reused, and put at the bottom. This means that is you have 100 rows, it won't create 100 cells (it only creates the number of cells that can be visible on the screen at a time and the reuses those) , as this would consume a lot of memory, and the scrolling wouldn't be as smooth.