Labels in custom TableView cells disappearing after scrolling - ios

I have dynamically tableView with custom cell. CustomCell .h file looks like this:
#property (strong, nonatomic) IBOutlet UILabel *uslugaName; //I set retain doesn't work too
#property (strong, nonatomic) IBOutlet UILabel *howMuchPayLbl;
My CellForRowAtIndexPathMethod:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * cellIdentifier = #"Cell";
myCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
/*
if (!cell)
cell = [[myCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
*/
if (indexPath.row !=15) {
cell.uslugaName.text =serviceNameArr[indexPath.row];
//окрашиваем ячейку в зависимости от активности услуги
if ([uslugaIsActiveArr[indexPath.row] isEqual: #"1"]) {
cell.backgroundColor = [UIColor blackColor];
cell.howMuchPayLbl.enabled = YES;
}
else {
cell.backgroundColor = [UIColor grayColor];
cell.howMuchPayLbl.enabled = NO;
}
if (![amountTmpArr[indexPath.row] isEqual: #"0"])
cell.howMuchPayLbl.text = [NSString stringWithFormat:#"Оплачиваю: %# KZT", amountTmpArr[indexPath.row]];
}
else {
cell.uslugaName.font = [UIFont fontWithName:#"System Bold" size:16];
cell.uslugaName.text = [NSString stringWithFormat:#"ОБЩАЯ СУММА ОПЛАТЫ: %#", fullAmount];
cell.howMuchPayLbl.hidden = YES;
}
return cell;
}
I want that last row different than others ( for this purpose this:
if (indexPath.row !=15)
). Problem is - when scrolling cell.howMuchPayLb disappear. If delete special code for last row - all works ok, why this happening?

Your code has an if else statement where one branch can set cell.howMuchPayLbl.hidden = YES; but the other branch does not set cell.howMuchPayLbl.hidden = NO;. So, once the label is hidden it will never be un-hidden. When the cell with the hidden label is reused the label remains hidden.
Add cell.howMuchPayLbl.hidden = NO; (and any other 'inverse' configuration required) to your if statement.

Refer This link will help you..
Its because of dequeueReusableCellWithIdentifier will not recognize the cell-identifire with same name.So you can use unique cell identifire like Cell1,Cell2...for each row..

Related

UITableView after prepareForReuse - all the data in Cell disappear

I have a TableView with customCell, that has next properties:
#interface CustomTableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIImageView *image;
#property (weak, nonatomic) IBOutlet UILabel *nameOfImage;
#property (weak, nonatomic) IBOutlet UIButton *start;
#property (weak, nonatomic) IBOutlet UIButton *stop;
#property (weak, nonatomic) IBOutlet UIProgressView *progressView;
#property (weak, nonatomic) IBOutlet UILabel *realProgressStatus;
In table view - when user press start button - an image is downloaded, as well as progressView with realProgressStatus reflects current situation. At this stage everything works perfect, but when scroll table view and return back to the cell - that was already fulfilled with data - all info disappeared(except the nameOfImage, I set it separately).
I implemented next method in my CustomTableViewCell class:
-(void)prepareForReuse
{
[super prepareForReuse];
self.progressView.progress = 0.1;
self.realProgressStatus.text = #"";
self.image.image = [UIImage imageNamed:#"placeholder"];
}
I implemented new property NSMutableSet self.tagsOfCells, where I save number of cells where images where already downloaded.
I tried to make changes in TableView method but effect is the same :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* PlaceholderCellIdentifier = #"Cell";
CustomTableViewCell *customCell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier forIndexPath:indexPath];
customCell.delegate = self;
customCell.cellIndex = indexPath.row;
if (!customCell)
{
customCell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
}
else
{
NSNumber *myNum = [NSNumber numberWithInteger:indexPath.row];
if (![self.tagsOfCells containsObject:myNum])
{
NSLog(#"Row: %ld",(long)indexPath.row);
UIImage *img = [UIImage imageNamed:#"placeholder"];
customCell.realProgressStatus.text = #"";
customCell.progressView.progress = 0.1;
customCell.image.image = img;
[customCell setNeedsLayout];
}
}
if ( indexPath.row % 2 == 0 )
customCell.backgroundColor = [UIColor grayColor];
else
customCell.backgroundColor = [UIColor darkGrayColor];
customCell.nameOfImage.text = self.names[indexPath.row];
return customCell;
}
EDIT:
I populate self.tagsOfCells in one of methods during downloading images, here is the method:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.customCell.realProgressStatus.text = #"Downloaded";
UIImage *img = [UIImage imageWithData:self.imageData];
self.customCell.image.image = img;
self.customCell.tag = self.selectedCell;
[self.savedImages setObject:img forKey:self.customCell.nameOfImage.text];
NSNumber *myNum = [NSNumber numberWithInteger:self.selectedCell];
[self.tagsOfCells addObject:myNum];
}
Thanks in advance for any help or advice.
Content-related actions shouldn't occur in the prepareForReuse function.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewCell_Class/#//apple_ref/occ/instm/UITableViewCell/prepareForReuse
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.
Edited
Alfie is right, prepareForReuse isn't invoked after returning from cellForRowAtIndexPath
First thing I would try is edit the code
if (!customCell)
{
customCell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
}
else
{
NSNumber *myNum = [NSNumber numberWithInteger:indexPath.row];
if (![self.tagsOfCells containsObject:myNum])
{
NSLog(#"Row: %ld",(long)indexPath.row);
UIImage *img = [UIImage imageNamed:#"placeholder"];
customCell.realProgressStatus.text = #"";
customCell.progressView.progress = 0.1;
customCell.image.image = img;
[customCell setNeedsLayout];
}
}
to the following
if (!customCell)
{
customCell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
}
NSNumber *myNum = [NSNumber numberWithInteger:indexPath.row];
if (![self.tagsOfCells containsObject:myNum])
{
NSLog(#"Row: %ld",(long)indexPath.row);
UIImage *img = [UIImage imageNamed:#"placeholder"];
customCell.realProgressStatus.text = #"";
customCell.progressView.progress = 0.1;
customCell.image.image = img;
[customCell setNeedsLayout];
}
This really isn't an expensive operation and it's better to have this logic in a single place. Especially since you got rid of prepareForReuse, this should be changed.
The reason when you scroll away, and then come back to not find your data is cellForRowAtIndexPath is being called again, which results in the default data overriding the new data you just added.
You would want to have an underlying data structure and retrieve data from there.
What I would do:
For instances, you can keep a mutable list as such
NSMutableArray* tableData = [[NSMutableArray alloc] init];
add image data for each row to the array
[tableData add:imageData];
and then display it add cellForRowAtIndexPath
customCell.image.image = [tableData objectAtIndex:[indexPath row]];
This would be the normal practice for populating a UITableView with data. This way, even if you scroll away tableData would remain the same, resulting in consistent data.
Note
I would also call new data asynchronously using NSURLSession from within the cell if I had to implement this. Just a suggestion=)
I don't think this has anything to do with your use of prepareForReuse. A couple things look fishy.
You're setting the delegate and cellIndex properties on your cell before you know whether it's nil or not. If it's nil then these properties will never be set.
I also don't think you need the else clause in there. The logic that it wraps doesn't have anything to do with whether a cell could be dequeued. it has to do with configuring a non-nil cell.
A missing piece from your question is where do you populate the tagsOfCells object? That code is important. If you're not setting the tag in cellForRowAtIndexPath it seems like your data will get out of sync and this will cause your cell to be misconfigured.
Try the following code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* PlaceholderCellIdentifier = #"Cell";
CustomTableViewCell *customCell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier forIndexPath:indexPath];
if (!customCell)
{
customCell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
}
NSNumber *myNum = [NSNumber numberWithInteger:indexPath.row];
if (![self.tagsOfCells containsObject:myNum])
{
NSLog(#"Row: %ld",(long)indexPath.row);
UIImage *img = [UIImage imageNamed:#"placeholder"];
customCell.realProgressStatus.text = #"";
customCell.progressView.progress = 0.1;
customCell.image.image = img;
[customCell setNeedsLayout];
}
customCell.delegate = self;
customCell.cellIndex = indexPath.row;
customCell.nameOfImage.text = self.names[indexPath.row];
if (indexPath.row % 2 == 0)
{
customCell.backgroundColor = [UIColor grayColor];
}
else
{
customCell.backgroundColor = [UIColor darkGrayColor];
}
return customCell;
}
Edit
Regarding info in another answer here. prepareForReuse is not being called after you configure your cell. It's being called when you invoke dequeueReusableCell and hence before you configure your cell. I don't believe prepareForReuse is the problem.
It looks like you are not using interface builder's prototype cells so you are using the wrong dequeue reusable cell method. The correct one in your situation does not have an index path param e.g.
CustomTableViewCell *customCell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
And as the others said you need to move your 2 lines of initialization after you alloc init your custom cell because it's nil before then. Eg you need to dequeue the cell and if nil create one. Then set your params on the cell now you have a valid instance.

iOS 8 custom table view cell selection

I have some problems with my custom cells. I created New custom TableViewCell with its own xib file. The table loads and data is being populated in all the cells - that is all ok. But when i run my app in devices and simulators with iOS 8 and above the select method never fires. This problem does not happen when i run the application on devices and simulators with iOS 7.x. Here is the code for the CustomCell.m file
- (void)awakeFromNib {
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
Here is mu CustomCell.h file
#interface CustomCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIImageView *directionImage;
#property (weak, nonatomic) IBOutlet UILabel *distanceLbl;
#property (weak, nonatomic) IBOutlet UILabel *pathDescriptionLbl;
And here is the code from the ViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"row selected");
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [steps.stepsData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier = #"directionsCell";
DirectionsTableViewCell *cell = (DirectionsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil){
NSString *cellNib = [Utilities getDirectionCellNib];
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:cellNib owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
}
if(indexPath.row == 0 || (indexPath.row % 2 == 0)){
cell.backgroundColor = [UIColor colorWithRed:242.0/255.0 green:242.0/255.0 blue:242.0/255.0 alpha:1.0];
}else{
cell.backgroundColor = [UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0];
}
cell.directionImage.image = nil;
cell.distanceLbl.text = #"";
cell.pathDescriptionLbl.text = #"";
if(indexPath.row != 0){
UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.frame.size.width, 1)];
separatorLineView.backgroundColor = [UIColor colorWithRed:200.0/255.0 green:200.0/255.0 blue:200.0/255.0 alpha:1.0];
[cell.contentView addSubview:separatorLineView];
}
cell.pathDescriptionLbl.text = #"main text here";
Step *stp = [steps.stepsData objectAtIndex:indexPath.row];
NSString *txtDetails = #"Details text here";
cell.distanceLbl.text = #"Distance here";
return cell;
}
What i find weird here is that the row selection works perfectly on iOS 7 but NOT on iOS 8. The data is being populated in both cases.
Am i doing something wrong here or am i missing something.
Thanks in advance for the help folks.

Collapsable Cell

I have a UITableView with two different custom table cells. The first cell appears normal after I start the app. The second cell will appear when you click on them.
Can anybody help me or has an idea?
Thanks a lot.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"customCell2";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor grayColor];
cell.textLabel.font = [UIFont fontWithName:#"STHeitiSC-Light" size:9.0];
}
return cell;
}
Having done custom UITableViewCell in the past I usually handle the nib loading in the custom class itself.
The basic header for the custom cell.
#interface RequestsTableViewCell : UITableViewCell {
// Ivars.
}
// Properties.
- (id) initWithRequestModel: (RequestModel *) model style:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier forQueryType:(int) requestType;
// Other methods, etc.
#end
The custom cell with a designated initializer.
#implementation RequestsTableViewCell
- (id) initWithRequestModel: (RequestModel *) model style:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier forQueryType:(int) requestType {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:#"RequestsTableViewCell" owner:self options:nil];
self = [nibArray objectAtIndex:0];
requestModel = model;
queryType = requestType;
[self setRequestThumbnail];
[self setRequestCategory];
[self setRequestAddress];
[self setRequestStatusDate];
[self setRequestStatus];
[self setRequestFollowed];
[self setRequestComment];
[self setAppearance];
}
return self;
}
There would also be a custom xib for the custom UITableViewCell that corresponds and has the custom class set in the identity inspector.
In the UITableViewController.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"Cell Id";
RequestModel *request = nil;
// Other code for search, etc
request = [self.serviceRequests objectAtIndex:indexPath.row];
RequestsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if(!cell) {
cell = [[RequestsTableViewCell alloc] initWithRequestModel:request style:UITableViewCellStyleDefault reuseIdentifier:cellId forQueryType:queryTypeIndicator];
}
return cell;
}
It also sounds like you have more than one custom cell type in your question? Can you elaborate on how it is all supposed to function? You say that you have to click one cell to make another appear, can you explain that interaction?
I did something similar, but made the cell 'expand', instead of adding a new cell. Of course then you don't have two cells, but you can resize your one cell, add subframes,...
You can keep a boolean in your UITableViewCell object (BOOL cellIsExpanded), and set that on tap gesture. Then in drawRect of the TableViewCell, layout your cell accordingly.
Example code, on expand, make cell height 20-->80 and add a UIButton:
In the TableViewController, overload heightForRowAtIndexPath (this will resize your cell if 'expanded'):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
YourEntity *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (!record.cellIsExpanded)
return 20.; // cell is smaller if collapsed
else
return 80.; // bigger cell
}
In the TableViewCell, add or remove subframes:
#interface MyTableViewCell ()
#property(nonatomic) BOOL cellIsExpanded
#property(strong, nonatomic) UITextField *myTextField;
#property(strong, nonatomic) UIButton *clickMeButton;
#end
#implementation MyTableViewCell
- (void)drawRect:(CGRect)rect {
if(!self.cellIsExpanded){
// layout your collapsed cell, for example:
self.myTextField = [[UITextField alloc] initWithFrame:self.frame];
self.myTextField.text = #"Collapsed cell";
// remove button, only present in expanded view :
self.clickMeButton=nil;
}
else{
self.myTextField.text = #"Expanded cell";
// add button below textfield
self.clickMeButton = [[UIButton alloc] initWithFrame:CGRectMake(20, 20, 10, 10)];
}
}
#end

viewWithTag returning nil inside cellForRowAtIndexPath method

I have a UITableViewController with a custom prototype cell. Inside the prototype cell I have 2 labels and an image. These are tagged with 100 for the image, 101 and 102 for the label. I am trying to access that tags inside this method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath row] == 0) {
static NSString *CellIdentifier = #"InfoCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UIImageView *albumArtworkImageView = (UIImageView *)[cell viewWithTag:100];
albumArtworkImageView.image = [self getAlbumArtworkWithSize:albumArtworkImageView.frame.size];
UILabel *albumArtistLabel = (UILabel *)[cell viewWithTag:101];
albumArtistLabel.text = [self getAlbumArtist];
UILabel *albumInfoLabel = (UILabel *)[cell viewWithTag:102];
albumInfoLabel.text = [self getAlbumInfo];
return cell;
} else {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MPMediaQuery *audiobookQuery = [MPMediaQuery audiobooksQuery];
MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue: audiobookTitle forProperty: MPMediaItemPropertyAlbumTitle];
[audiobookQuery addFilterPredicate:albumPredicate];
NSArray *albumTracks = [audiobookQuery items];
NSUInteger trackNumber = [[[albumTracks objectAtIndex:(indexPath.row-1)] valueForProperty:MPMediaItemPropertyAlbumTrackNumber] unsignedIntegerValue];
if (trackNumber) {
cell.textLabel.text = [NSString stringWithFormat:#"%i. %#", trackNumber, [[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyTitle]];
} else {
cell.textLabel.text = [[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyTitle];
}
if ([self sameArtists]) {
cell.detailTextLabel.text = #"";
} else {
if ([[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyArtist]) {
cell.detailTextLabel.text = [[[albumTracks objectAtIndex:(indexPath.row-1)] representativeItem] valueForProperty:MPMediaItemPropertyArtist];
} else {
cell.detailTextLabel.text = #"";
}
}
return cell;
}
}
heres a look at the storyboard
The issue I am having is the lines where I look up the view from the tags are returning nil. I have done this type of lookup before and I can't figure out why they are returning nil. Any help would be very appreciated. I'm not even sure of a good way to debug. I am a C# developer trying to learn objective-c/ios programming. Thanks!
I give you an alternative. Make a custom class for UITableViewCell and declare its views there; then connect each cell's subviews with these properties and access them directly, without calling viewWithTag.
For example, in your custom cell:
#interface MyCustomCell : UITableViewCell
#property (strong, nonatomic) UIImageView *albumArtworkImageView;
#property (strong, nonatomic) UILabel *albumArtistLabel;
#property (strong, nonatomic) UILabel *albumInfoLabel;
and in your cellForRowAtIndexPath method:
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.albumArtworkImageView.image = [self getAlbumArtworkWithSize:albumArtworkImageView.frame.size];
cell.albumArtistLabel.text = [self getAlbumArtist];
cell.albumInfoLabel.text = [self getAlbumInfo];
Remember to set MyCustomCell in your cell in the storyboard.
Hope this helps!

UILabel in TableCell never changes font or colour

I've been trying to find a solution for this for quite some time now but with no luck. I've got a TableViewController with a number of Cells, all of which have a UILabel with some text. I'm trying to change the font and colour with the following code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [menuItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.font = [UIFont fontWithName:#"Times New Roman" size:12.0f];
cell.textLabel.textColor = [UIColor redColor];
return cell;
}
This code should be correct to set the font and colour to a UILabel but it just never changes. Maybe there's a setting I need to change from the Interface Builder? Maybe an attribute setting the I need to disable?
The odd part is that even from the Interface Builder the fonts and colours simply never change.
Try the following code:-
#interface TestTableViewController ()<UITableViewDataSource,UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation TestTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate=self;
self.tableView.dataSource=self;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 5;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * reuseIdentifier = nil ;
UITableViewCell* cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
}
cell.textLabel.font = [UIFont fontWithName:#"Times New Roman" size:12.0f];
cell.textLabel.textColor = [UIColor redColor];
cell.textLabel.text = [NSString stringWithFormat:#"Number: %d",indexPath.row];
return cell;
}

Resources