iOS UITableView cells getting duplicated - ios

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.

Related

Textfield data hide when scroll tableview

I am developing IOS App. Multiple textfield add on tableviewcell.when enter data on texfield than scroll tableview textfield data hide. How to manage textfield on tableview.Please Help me and thanks in advance
code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableViews{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// Return the number of rows in the section.
return 100;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"cell";
UITableViewCell *cell;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
UITextField *textFieldName = [[UITextField alloc]initWithFrame:CGRectMake(8.0f, 3.0f, 80.0f, 36.0f)];
[cell addSubview:textFieldName];
[textFieldName setDelegate:self];
textFieldName.tag = indexPath.row+1;
UILabel *line = [[UILabel alloc]initWithFrame:CGRectMake(96.0f, 0.0f, 1.0f, 50.0f)];
line.backgroundColor = [UIColor colorWithRed:214.0/255.0 green:214.0/255.0 blue:214.0/255.0 alpha:1.0];
[cell addSubview:line];
[cell setSeparatorInset:UIEdgeInsetsZero];
tableView.allowsSelection=NO;
return cell;
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:([textField tag]-1) inSection:0];
UITableViewCell *Cell = [_dairyTable cellForRowAtIndexPath:indexPath];
}
How to deal with UITextField Value in UITableView :
First of all you have to create mutable Array with count equal to the number of UITextField.
In cellForRow do cell.textField.tag = indexPath.rowsothat you can identify each UItextField.
Save every UITextFieldvalue in the array in the same sequence with each character change. So, do code in textdidChange delegate method of UITextField by identifing the UITextfield by its tag as textfield.tag.
Whenever user enters or edit the text, you get that text in array.
Now in cellForRow add below line:
cell.textfiled.text = arrDataText[index.row]

Q: Custom TableViewCell scroll to not see, and show again ,my custom line dismiss

I have two different custom cell in my tableview, In the first cell I add a custom cell line under the system cell line:
_bottom_line = [[UIView alloc] initWithFrame:CGRectMake(0, 49, kScreen_Width, 1)];
_bottom_line.backgroundColor = Back_Color_QQ;
[part3 addSubview:_bottom_line];
In the controller to setup my custom cell;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) { // my first cell
static NSString *id_cell1 = #"cell1";
AgDetailTechCell *cell = [tableView dequeueReusableCellWithIdentifier:id_cell1];
if (cell == nil) {
cell = [[AgDetailTechCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id_cell1];
}
((AgDetailTechCell *)cell).model = self.headerModel;
((AgDetailTechCell *)cell).indexPath = indexPath;
((AgDetailTechCell *)cell).delegate = self;
_head_cell = cell;
_head_cell.comemntCountlabel.text = self.headerModel.lml_commentTimes;
_head_cell.likeCountlabel.text = self.headerModel.lml_likeTimes;
return cell;
}else { // other cells
static NSString *id_cell2 = #"cell2";
AgPreOrHelpCommentCell *cell = [tableView dequeueReusableCellWithIdentifier:id_cell2];
if (cell == nil) {
//cell = [[AgPreOrHelpCommentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id_cell2];
cell = [[NSBundle mainBundle] loadNibNamed:#"AgPreOrHelpCommentCell" owner:self options:nil].firstObject;
}
cell.delegate = self;
cell.indexPath = indexPath;
[cell initCellDataWithModel:self.dataSource[indexPath.row]];
cell.refresh = ^(UITableViewCell *currentCell) {
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
};
return cell;
}
}
The first time into the controller, it shows well. But after I scroll my tableview to hide and show again, my custom bottom line dismiss, I cannot find it.And I refresh my tableView, the custom line appears again. I have token 2 pictures to explain more detail:
UPDATE
Use Harsh's suggestion, but no change for the issue:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if ([cell.reuseIdentifier isEqualToString:#"cell1"]) {
// customs bottom_line
UIView * bottom_line = [[UIView alloc] initWithFrame:CGRectMake(0, 49, kScreen_Width, 1)];
bottom_line.backgroundColor = Back_Color_QQ;
[((AgDetailTechCell *)cell).part3 addSubview:bottom_line];
}
}
If your soul purpose is to extend the default separators from end to end.
Check this post How to fix UITableView separator on iOS 7?
Maybe your bottom line is out of your cell. Make sure you return 50 or greater height for cells. Another thing is disable the system bottom line of UITableView.

iPhone SDK: App crash on textField resignFirstResponder

I an creating app that needs expand tableview feature like below screen. when i click the row the textbox and button will appear.when i click on textbox and press return from keyboard, application crashed.
When I tried to resign the text box showing in screenshot, application crashed,
I write Following Code...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"AffiliationCell"];
cell.backgroundColor = [UIColor clearColor];
UILabel *lblTitle = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, 130, 44)];
lblTitle.tag = indexPath.row+1;
lblTitle.font = [UIFont fontWithName:Oxygen_Regular size:13];
lblTitle.text = [NSString stringWithFormat:#"Affiliation %d",indexPath.row+1];
lblTitle.backgroundColor = [UIColor clearColor];
lblTitle.textColor = [UIColor blackColor];
[cell.contentView addSubview:lblTitle];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell* checkCell = [tableView cellForRowAtIndexPath:indexPath];
[tableView beginUpdates];
if ([indexPath compare:self.checkedIndexPath] == NSOrderedSame) {
self.checkedIndexPath = nil;
[viewScreenName removeFromSuperview];
} else {
//add view
[txtScreenName setClearsOnInsertion:YES];
viewScreenName.frame = CGRectMake(0, 45, viewScreenName.frame.size.width, viewScreenName.frame.size.height);
[checkCell.contentView addSubview:viewScreenName];
self.checkedIndexPath = indexPath;
}
[tableView endUpdates];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if ([indexPath compare:self.checkedIndexPath] == NSOrderedSame) {
return expandedCellHeight; // Expanded height
}
return 44 ;
}
#pragma mark - TextField Delegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
tblAffiliations.frame = updatedFrame;
return TRUE;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
#try {
if (textField) {
[textField resignFirstResponder];
}
}
#catch (NSException *exception) {
NSLog(#"Exception %#",exception);
}
tblAffiliations.frame = currentFrame;
return TRUE;
}
Please Help.
It seems that you are using a textfield that is being deallocated. I think the best way to proceed is adding the textfield in each cell like you added your label and setting it to be hidden. On the didSelectRow delegate, you should set the label as hidden and the textfield not hidden. It is better to work with hidden flag that to remove and add from superview.
Wish it helps.

UIButton gets created twice on cell reload

I have a UIButton inside a UITableViewCell. When the app is first launched, it works as expected, where I created it's frame.
When I scroll pass the cell which holds the button, it creates a second instance of the button slightly below the button.
Here's a video to illustrate my problem: http://pixori.al/DJ1k
Here's the code for the UITableViewCell and also how I populate the cells.
Not sure why it's behaving like this.
#pragma mark - UITableViewDataSource
// 3 sections, (1 = mistarOverview) (2 = hourlyForecast) (3 = dailyForecast)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0) {
return MAX(6,6) + 1; //TODO add getNumberOfClasses for people with 7 or 8 classes
} else if (section == 1) {
return MIN([[MAManager sharedManager].hourlyForecast count], 6) + 1;
} else {
return MIN([[MAManager sharedManager].dailyForecast count], 6) + 1;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Redefine layout variables in method from `viewDidLoad`
CGFloat inset = 20; // For padding
if (! cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
// Sets up attributes of each cell
cell.selectionStyle = UITableViewCellSelectionStyleNone; //TODO none
cell.backgroundColor = [UIColor colorWithWhite:0 alpha:0.2];
cell.textLabel.textColor = [UIColor whiteColor];
cell.detailTextLabel.textColor = [UIColor whiteColor];
QBFlatButton* loginButton = nil;
if (indexPath.section == 0) {
if (indexPath.row == 0) {
[self configureHeaderCell:cell title:#"Grades"];
if ([cell.textLabel.text isEqual: #"Grades"] && (!loginButton) && (indexPath.row == 0) && (indexPath.section == 0)) {
UIView *cellView = cell.contentView;
CGRect loginButtonFrame = CGRectMake((cellView.frame.size.width - (80 + inset)), 18, 80, (cellView.frame.size.height));
loginButton = [[QBFlatButton alloc] initWithFrame:loginButtonFrame];
[loginButton addTarget:self action:#selector(loginButtonWasPressed)forControlEvents:UIControlEventTouchUpInside];
loginButton.faceColor = [UIColor grayColor];
loginButton.sideColor = [UIColor clearColor];
loginButton.radius = 6.0;
loginButton.margin = 4.0;
loginButton.depth = 3.0;
loginButton.alpha = 0.3;
loginButton.titleLabel.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20];
[loginButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[loginButton setTitle:#"Login" forState:UIControlStateNormal];
[cellView addSubview:loginButton];
}
} else {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.textLabel.text = [NSString stringWithFormat:#"Period %ld A+", (long)indexPath.row];
cell.detailTextLabel.text = #"Class name";
//TODO get grades and config using method (TB Created)
}
} else if (indexPath.section == 1) {
if (indexPath.row == 0) {
[self configureHeaderCell:cell title:#"Hourly Forecast"];
}
else {
// Get hourly weather and configure using method
MACondition *weather = [MAManager sharedManager].hourlyForecast[indexPath.row - 1];
[self configureHourlyCell:cell weather:weather];
}
}
else if (indexPath.section == 2) {
if (indexPath.row == 0) {
[self configureHeaderCell:cell title:#"Daily Forecast"];
}
else if (indexPath.section == 2) {
// Get daily weather and configure using method
MACondition *weather = [MAManager sharedManager].dailyForecast[indexPath.row - 1];
[self configureDailyCell:cell weather:weather];
}
}
return cell;
}
Implement the following UITableView Delegate Method
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
//In here, check the index path. When you have the cell that contains the button, pop it out from there by using [button removeFromSuperView];
}
Your problem occurs when you dequeue the cell. Since the cell is being reused, it already has the button and you're simply re-adding it again. This will solve your issue. However, I'd recommend you create a subclass for the UITableViewCell, and in it's prepareForReuse method, pop the button out. Up to you. Both will work.
Table view cells are not just deallocated then they move out of visible area. They are stored for reusing and then returned in tableView dequeueReusableCellWithIdentifier:CellIdentifier];
So you need to clean your cells after using or before reusing. There are several ways:
1.Add tag to your button when you create it
loginButton.tag = SOME_TAG;
just after
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
search for view with this tag
loginButton = [cell viewWithTag:SOME_TAG];
if loginButton != nil you can reuse it or remove from cell and then create a new one.
2.Implement UITableViewDelegate method
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
and erase login button inside it.
3.Create custom UITableViewCellclass and implement prepareForReuse method.
You're adding the button every time you return a cell in this method. If you scroll the cell off the screen and back on, this method is called again for the same index path, and you will add the button again.
You declare the variable, do nothing with it, then check if it is nil. It will always be nil, so you always add the button.
A quick and dirty solution is to give the button a tag, then check for its existence using viewWithTag:.
A better solution is to make a custom cell subclass, and set one-time properties like this in the init method. Your cell contents seem very different for each section as well, so use different reuse identifiers for each section, and possibly a different cell subclass. Clearing out sub views is expensive and could hurt your scrolling performance.
When you run your project first time then cellForRowAtIndexPath is called.....
Then whenever you scroll tableView it again calls cellForRowAtIndexPath and reload data automatically..
So you have to take CellIdentifier as unique for each cell.
you have to remove static keyword from
static NSString *CellIdentifier = #"CellIdentifier";
now you have
NSString *CellIdentifier = #"CellIdentifier";
only this things
Now you have to write like below
NSString *CellIdentifier = [NSString stringWithFormat:#"%#",indexPath];
Now Enjoy.....

Add xib as a view to cover a custom UITableViewCell in editing mode

I'm trying to to something like apple's alarm clock, when tap the edit button, a custom view cover the custom UITableViewCell.
The code above:
// CGRect frame = [tableView rectForRowAtIndexPath:indexPath];
// CGPoint yOffset = self.tableViewBlock.contentOffset;
// CGRect newFrame = CGRectMake(frame.origin.x, (frame.origin.y - yOffset.y + 45), frame.size.width, frame.size.height);
CallBlock_Date_EditMode *viewController = [[CallBlock_Date_EditMode alloc] initWithNibName:#"CallBlock_Date_EditMode" bundle:nil];
// self.view.frame = newFrame;
// [self.view addSubview:viewController.view];
// [self addChildViewController:viewController];
UITableViewCell *cell = (UITableViewCell*)[self.tableViewBlock cellForRowAtIndexPath:indexPath];
[cell.contentView addSubview:viewController.view];
Cover the specific cell when I put in under:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Just to make sure the size is ok (although when I tap a button in that xib the app crash without even a single error).
But I want to do like apple's alarm clock (actually, mimic it), tap my edit button and my custom UITableViewCell will get cover with this xib as a view.
Maybe there is a better approach to do it?
EDIT:
My updated code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CallBlock_TableCell *cell = (CallBlock_TableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[CallBlock_TableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(CallBlock_TableCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
cell.accessoryType = self.isEditing ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
CallBlock_ByDate *callBlock = (CallBlock_ByDate*)[fetchedObjects objectAtIndex:indexPath.row];
cell.labelTime.text = callBlock.startDate;
cell.labelRepeat.text = callBlock.repeat;
cell.labelTextLabel.text = callBlock.label;
cell.switchCallBlock.on = YES;
cell.switchCallBlock.tag = (NSInteger)indexPath.row +1;
[cell.switchCallBlock addTarget:self action:#selector(handleSwitch:) forControlEvents:UIControlEventValueChanged];
cell.switchCallBlock.hidden = self.isEditing ? YES : NO;
if (self.isEditing)
{
cell.switchCallBlock.hidden = YES;
UIButton *btnArrow = [[UIButton alloc] init];
btnArrow.frame = CGRectMake(282.0, 31.0, 18.0, 21.0);
[btnArrow setBackgroundImage:[UIImage imageNamed:#"arrow_FWR_off"] forState:UIControlStateNormal];
[btnArrow setBackgroundImage:[UIImage imageNamed:#"arrow_FWR_on"] forState:UIControlStateHighlighted];
btnArrow = [UIButton buttonWithType:UIButtonTypeCustom];
[btnArrow addTarget:self action:#selector(handleTapToEdit:) forControlEvents:UIControlEventTouchUpInside];
btnArrow.tag = indexPath.row + 1;
[cell.contentView addSubview:btnArrow];
[cell.contentView bringSubviewToFront:btnArrow];
}
}
But I cannot get the btnArrow appear on the UTableView.
The reason you are getting a crash is because nothing is retaining your CallBlock_Date_EditMode view controller. You add its view to your cell as a subview, but nothing maintains a reference to the view controller, so it is deallocated and then, when pressing a button that is supposed to pass a message to your view controller, it is sent to a deallocated object and you get a crash.
There are two possible solutions to this. First, you could store that view controller in one of your properties to maintain a reference to it so that it does not deallocated. This is, for the most part, probably not what you want.
Instead, what I would suggest doing is do not make your CallBlock_Date_EditMode a UIViewController, but instead make it a UIView. You may be wondering "But how can I use a xib without a UIViewController?". I would do something like the following:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"CallBlock_Date_EditMode" owner:self options:nil] objectAtIndex:0];
self.myEditButton = (UIButton *)[view viewWithTag:2];
[self addSubview:view];
}
return self;
}
This would be code inside your custom UIView that would load in a xib file and add it as a subview. In order to get access to your subviews, you have to use tags inside interface builder, so you do lose the convenience of drawing/connecting IBOutlets... But in the end, it is much better than allocating/storing a bunch of unnecessary UIViewControllers.
If I understand you right and you want to mimic the functionality of the alarm clock that comes pre-installed from Apple, your solution is much simpler than creating a custom view. It looks like all they do is set the On-Off switches to hidden and add a disclosure indicator to the cell. This is what I would do...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
bool hide = (tableView.editingStyle == UITableViewCellEditingStyleDelete); // set to true or false depending on if the table is in editing mode
for (UIView *sv in [cell subviews] ) {
if([sv isKindOfClass:[UISwitch class]]) { // find the on-off switch
[sv setHidden:hide]; // hide the switch depending on the t/f value of hide
break;
}
}
if(hide) { // adds the arrow like in apple's alarm clock table if the cell is in edit mode
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
else {
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
return cell;
}

Resources