Using custom uitableviewcells - ios

I created custom cells for my table. Basically my cells have UIButton on them. I am reusing those cells.
However i have trouble with those buttons, because when cells is reused then all it's elements also is reused but i am seeking that these Button will be unique to every cells.
Maybe someone could propose a solution for this functionality ?

How about this if you have a property button on your custom cell?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath static NSString *CellIdentifier = #"My Cell Identifier";
MyCustomCellClass *cell = (MyCustomCellClass *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MyCustomCellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
UIButton *myButtonForThisCell = [MyButtonArray objectAtIndex:indexPath.row];
cell.button = myButtonForThisCell;
return cell;
}

create a buttonView UIVIEW file with two methods as
-(NSInteger) getGridIndex
{
return gridIndex;
}
-(void) setGridIndex:(NSInteger)value
{
gridIndex = value;
}
Now in cellforrowatindexpath method
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
ButtonView *buttonView=[[ButtonView alloc] initWithFrame:CGRectMake(110, 110, 160, 26)];
buttonView.tag=18;
UIButton *button=[[UIButton alloc]initWithFrame:CGRectMake(55, 0, 60, 26)];
[button addTarget:self action:#selector(ButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[buttonView addSubview:button];
[cell addSubview:buttonView];
}
ButtonView *buttonView=(ButtonView*)[cell viewWithTag:18];
NSArray *d=[buttonView subviews];
UIButton *button=[d objectAtIndex:0];
//here you can change the setting or title of button or whatever u want to do.
[buttonView setGridIndex:indexPath.row];
}
Now in button action :
-(void)ButtonAction:(UIButton*)sender{
ButtonView *view = (ButtonView *)[sender superview];
NSInteger n=[view getGridIndex];
//here n is the indexpath.row at which the button was tapped..you can write its action accordingly.
}

Related

iOS UITableView cells getting duplicated

I have a tableviewcontroller that has dynamic controls created in cells. If it's a dropdown type, I take the user to a different tableviewcontroller to select the value. Once selected, I pop back and reload the data, but when I do that it overwrites the cells on top of one another. I know this is because I'm reusing the cells, but I cannot seem to figure out how to prevent it.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
EWHInboundCustomAttribute *ca = [visibleCustomAttributes objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.tag=indexPath.row;
if (ca.CustomControlType == 1) {
cell.detailTextLabel.hidden=true;
cell.textLabel.hidden=true;
UITextField *caTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)];
caTextField.adjustsFontSizeToFitWidth = YES;
caTextField.textColor = [UIColor blackColor];
caTextField.placeholder = ca.LabelCaption;
if (ca.ReadOnly) {
[caTextField setEnabled: NO];
} else {
[caTextField setEnabled: YES];
}
caTextField.text=nil;
caTextField.text=ca.Value;
caTextField.tag=indexPath.row;
caTextField.delegate=self;
[cell.contentView addSubview:caTextField];
} else if (ca.CustomControlType == 4) {
cell.detailTextLabel.text=ca.Value;
cell.textLabel.text=ca.LabelCaption;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.detailTextLabel.hidden=true;
cell.textLabel.hidden=true;
UITextField *caTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 185, 30)];
caTextField.adjustsFontSizeToFitWidth = YES;
caTextField.textColor = [UIColor grayColor];
caTextField.placeholder = ca.LabelCaption;
[caTextField setEnabled: NO];
caTextField.text = ca.Value;
caTextField.tag=indexPath.row;
caTextField.delegate=self;
[cell.contentView addSubview:caTextField];
}
return cell;
}
Instead of creating the UITextfield each time I would suggest at least using [UIView viewWithTag:tag] to capture the same UITextField object.
I'd suggest you to create custom UITableViewCell subclass and put all subviews related logic there.
Next, in order to reset/clear cell before reuse - you should override prepeareForReuse function.
Swift:
override func prepareForReuse() {
super.prepareForReuse()
//set cell to initial state here
}
First,I suggest you to use custom cells.If not and your cells are not so many,maybe you can try unique cell identifier to avoid cell reuse:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// unique reuseID
NSString *cellReuseID = [NSString stringWithFormat:#"%ld_%ld", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellReuseID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellReuseID];
// do something
}
return cell;
}
Hope it's helpful.

How to solve this override UITextField values?

I have created iOS new app. UITextField is added in each row of UITableView. first row UITextField enter values then add new row the first row UITextField values show in second row UITextField. how to solve this issues. please help me . thanks in advance.
Here is my sample code add row functionality
- (IBAction)btnAddRow:(id)sender {
[self loadData:[self.dataArray count]];
[self.tblview reloadData];
}
-(void ) loadData:(NSInteger) Indexvalue
{
newSheet = [NSDictionary dictionaryWithObjectsAndKeys:strEntryID,entryID, nil];
[self.dataArray insertObject:newSheet atIndex:Indexvalue];
}
- (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];
}
UITextField *txtComment = [[UITextField alloc] initWithFrame:CGRectMake(225.0f,5.0f,50.0f,20.0f)];
[txtComment setBorderStyle:UITextBorderStyleLine];
[cell addSubview:txtComment];
[txtComment setTag:indexPath.row];
[txtComment addTarget:self action:#selector(txtCommentChange:) forControlEvents:UIControlEventEditingChanged];
return cell;
}
- (IBAction)btnAddRow:(id)sender {
[self loadData:[self.dataArray count]];
[self.tblview reloadData];
}
-(void ) loadData:(NSInteger) Indexvalue
{
newSheet = [NSDictionary dictionaryWithObjectsAndKeys:strEntryID,entryID, nil];
[self.dataArray insertObject:newSheet atIndex:Indexvalue];
}
- (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];
}
for (UIView *view in cell.subviews) {
if ([view isKindOfClass:[UITextField class]]) {
[view removeFromSuperview];
}
}
UITextField *txtComment = [[UITextField alloc] initWithFrame:CGRectMake(225.0f,5.0f,50.0f,20.0f)];
[txtComment setBorderStyle:UITextBorderStyleLine];
[cell addSubview:txtComment];
[txtComment setTag:indexPath.row];
[txtComment addTarget:self action:#selector(txtCommentChange:) forControlEvents:UIControlEventEditingChanged];
return cell;
}
You write just one for loop after your cell allocation.
txtComment.text = yourText
You missed it
You are adding a new UITextField every time that cellForRowAtIndexPath is called. Cells are re-used as you scroll, so imagine that as a cell goes off the top of the screen, its quickly taken and put at the bottom of the screen to be the new cell that is appearing. This also means that if you added a textfield first time round, then a second one will be added on top of the first.
If you add your UITextField within the same if statement where you initialize your cell, this will prevent more than one textfield appearing. Eg :
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UITextField *txtComment = [[UITextField alloc] initWithFrame:CGRectMake(225.0f,5.0f,50.0f,20.0f)];
[txtComment setBorderStyle:UITextBorderStyleLine];
[cell addSubview:txtComment];
[txtComment setTag:indexPath.row];
[txtComment addTarget:self action:#selector(txtCommentChange:) forControlEvents:UIControlEventEditingChanged];
}
Theres also a method you can use that is called when a cell is about to be recycled : -prepareForReuse. This is called on the UITableViewCell itself though, so you would need a subclass of UITableViewCell to access it.
- (void) prepareForReuse {
//set text fields to #"" for eg
}

iOS TableView Reuse would load a lot of subviews to cell

I used the following code to implement cell for every row at index path:
But the problem is when I scroll the tableView, the cell will load a lot of UIImageView *itemimageview in one cell in one line, I tried use
for (UIImageView *sView in cell.subviews) {
[sView removeFromSuperview];
}
but it would remove all subviews of one cell. How to solve this problem?...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
NSUInteger oldRow = [lastIndexPath row];
static NSString *CheckMarkCellIdentifier = #"CheckMarkCellIdentifier";
//dequeueReusableCellWithIdentifier --
// Returns a reusable table-view cell object located by its identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CheckMarkCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CheckMarkCellIdentifier] autorelease];
}
/*
for (UIImageView *sView in cell.subviews) {
[sView removeFromSuperview];
}
*/
UIImageView *itemimageview=[[UIImageView alloc]initWithFrame:CGRectMake(5, 5, 232, 54)];
itemimageview.image=[UIImage imageNamed:[tabsImageArray objectAtIndex:row]];
itemimageview.userInteractionEnabled = YES;
[cell.contentView addSubview:itemimageview];
[itemimageview release];
UIImageView *dictIcon=[[UIImageView alloc]initWithFrame:CGRectMake(30, 18, 30, 30)];
dictIcon.image=[UIImage imageNamed:#"dictionary_icon.png"];
dictIcon.userInteractionEnabled = YES;
[cell.contentView addSubview:dictIcon];
[dictIcon release];
UILabel *dictNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(80, 23, 100, 21)];
dictNameLabel.text = dictName;
dictNameLabel.textColor = [UIColor whiteColor];
dictNameLabel.shadowColor = [UIColor blackColor];
dictNameLabel.backgroundColor = [UIColor clearColor];
dictNameLabel.userInteractionEnabled = YES;
[cell.contentView addSubview:dictNameLabel];
[dictNameLabel release];
//cell.textLabel.text = [tabsImageArray objectAtIndex:row];
cell.accessoryType = (row == oldRow && lastIndexPath != nil) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
Consider running through and checking [view isKindOfClass:[UIImageView class]] to check whether the view is the right type of view to remove.
Also consider tagging the views with the UIView tag property, so you can add the subviews once, and then won't have to recreate them with reuse.
Here is how you would do this:
#define ImageViewOneTag 1001
#define ImageViewTwoTag 1002
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)path {
static NSString *CellID = #"CellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellID];
UIImageView *imageViewOne = nil;
UIImageView *imageViewTwo = nil;
if (cell) {
// You've caught a reusable cell. Fetch the image views by their tag.
imageViewOne = (UIImageView *)[cell viewWithTag:ImageViewOneTag];
imageViewTwo = (UIImageView *)[cell viewWithTag:ImageViewTwoTag];
} else {
// You haven't got a reusable cell. Make one, and make and add the image views to the contentView.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellID];
imageViewOne = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 20.0, 20.0)];
[imageViewOne setTag:ImageViewOneTag];
imageViewTwo = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 20.0, 20.0, 20.0)];
[imageViewTwo setTag:ImageViewTwoTag];
UIView *contentView = cell.contentView;
[contentView addSubview:imageViewOne];
[contentView addSubview:imageViewTwo];
}
// By this stage, you've either retrieved a reusable cell, or you've made a new one. Either way, imageViewOne and imageViewTwo now have a reference to the views you mean.
imageViewOne.image = *imageOneForRow*;
imageViewTwo.image = *imageTwoForRow*;
return cell;
}
I suggest you using custom table view cell, just create a cocoa touch class which inherits uitableviewcell.

Actions for UIButtons in UITableViewCells

I implemented a UIButton in every cell of my UITableView and set an action for this button. Now I'm trying to find out which of these buttons was tapped:
- (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];
}
// Configure the cell...
<...>
UIButton *but=[UIButton buttonWithType:UIButtonTypeRoundedRect];
but.frame= CGRectMake(275, 5, 40, cell.frame.size.height-10);
[but setTitle:#"Map" forState:UIControlStateNormal];
[but addTarget:self action:#selector(Map:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:but];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
- (IBAction) Map: (id) sender {
NSIndexPath *indexPath = [[NSIndexPath alloc] init];
indexPath = [tableView indexPathForCell:(UITableViewCell*)[[sender superview]superview]];
[delegate callMap: [[[data objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row+1]
objectAtIndex:kNameInArray]
: [[[[data objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row+1]
objectAtIndex:kXcoordinateInArray] doubleValue]
: [[[[data objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row+1]
objectAtIndex:kYcoordinateInArray] doubleValue]
];
}
But it always sends data[0][1][*] elements to the delegate. I mean indexPath always has 0 raw and 0 section. Where is my mistake?
Do the following:
add the button inside your if (cell == nil) { ... } method or else you will have lots of buttons inside the same cell when your cell is reused.
Set cell.contentViow.tag = SomeValueIdentifyingYourData (some int value representing your model.. maybe just the index of your object array..) before returning the cell.
Inside your Map: method you can access the value again with ((UIButton)sender).superview.tag and make your decisions based on that value

Add a label to the first UITableViewCell row while reusing cells

Is it possible to add a label to a UITableViewCell while reusing cells? This is the code I have been trying but when I scroll down and the cells get reused my label moves around. I just want the label in the very first row at all times, but I cannot figure this out for the life of me. I know I can not reuse the cells and it will work but I would like to reuse the cells. Please help me figure this out. Thank you very much...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SimpleTableIdentifier] autorelease];
}
if ([indexPath row] == 0) {
UILabel* Label = [[UILabel alloc] initWithFrame:CGRectMake(2,2, 62, 23)];
[Label setText:#"Text"];
[cell addSubview:Label];
[Label release];
}
return cell;
}
I think I figured it out. I made the 1st row not reusable.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
static NSString *CellsToBeReused = #"CellsToBeReused";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellsToBeReused];
if (cell == nil){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellsToBeReused] autorelease];
}
if ([indexPath row] == 0) {
UITableViewCell *first_Cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
UILabel* Label = [[UILabel alloc] initWithFrame:CGRectMake(2,2, 62, 23)];
[Label setText:#"Text"];
Label.backgroundColor = [UIColor whiteColor];
[first_Cell.contentView addSubview:Label];
[Label release];
return first_Cell;
} else {
return cell;
}
}

Resources