I've got an UITableView with 30 objects.
Controller shows correctly first 13 rows, on 14th row use a "joker" row which changes his content scrolling, then start again with first thirteen row and "joker" row until the end.
That's code of cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: #"cell" forIndexPath:indexPath];
UIImageView * flagImageView = (UIImageView *) [self.view viewWithTag:1];
UILabel * nationLabel = (UILabel *) [self.view viewWithTag:2];
nationLabel.text = [_nationsArray objectAtIndex:indexPath.row];
nationLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
NSLog(#"%i", indexPath.row);
return cell;
}
Strange thing is that configuring cell in if (cell == nil) { ... } it doesn't work...
The problem seems to be with the nationLabel. When I replaced
UILabel * nationLabel = (UILabel *) [self.view viewWithTag:2];
nationLabel.text = [_nationsArray objectAtIndex:indexPath.row];
with
cell.textLabel.text = [_nationsArray objectAtIndex:indexPath.row];
it's working fine (at least the text). So I think you should try to make a UITableViewCell subclass, make property for that label and see if it's ok then.
Don't use this
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: #"cell" forIndexPath:indexPath];
Change your code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: #"cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
UIImageView * flagImageView = (UIImageView *) [self.view viewWithTag:1];
UILabel * nationLabel = (UILabel *) [self.view viewWithTag:2];
nationLabel.text = [_nationsArray objectAtIndex:indexPath.row];
nationLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
NSLog(#"%i", indexPath.row);
return cell;
}
Related
I created UITableView inside my UIViewController and I built a prototype cell in the storyboard. I assigned tags for all the elements in the cell I want to change and I am changing them in the code.
When the (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is called, my data is processed and I can see a cell appear with the red background colour I set in storyboard, however the images or text is missing. I think the problem is with the implementation but I can't figure out what I am doing wrong.
This is what the storyboard looks like:
This is the relevant code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
if(tableView == youLikeTableView)
{
UIView* youLikeImageFrame = (UIView *)[cell.contentView viewWithTag:100];
UIImageView* youLikeImageView = (UIImageView *)[cell.contentView viewWithTag:101];
UILabel *youLikeNameLabel = (UILabel *)[cell.contentView viewWithTag:102];
for(int i = 0; i < [youLike count]; i++)
{
youLikeImageFrame.layer.cornerRadius = youLikeImageFrame.frame.size.width / 2;
[youLikeImageView setImage:[youLikeImages objectAtIndex:i]];
youLikeImageView.layer.cornerRadius = youLikeImageView.frame.size.width / 2;
youLikeNameLabel.text = [youLikeName objectAtIndex:i];
}
}
return cell;
}
And this is the cell appearing without the text or images
you forgot to add them as subview
[cell addSubView:youLikeImageFrame];
[cell addSubView: youLikeImageView]; and
[cell addSubView: youLikeNameLabel];
just before
return cell;
heres my code in didSelectRowAtIndexPath. any suggestion or help on how to fix this please.. the problem is when i clicked the cell many cell will have checkmark ....My plan is when i clicked one cell only that cell will have Checkmark
- (void)tableView:(UITableView *)thetableView
didSelectRowAtIndexPath:(NSIndexPath *)newindexPath {
[thetableView deselectRowAtIndexPath:[thetableView indexPathForSelectedRow]
animated:true];
UITableViewCell *cell = [thetableView cellForRowAtIndexPath:newindexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
and heres the code in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(nullable PFObject *)object {
static NSString *cellIdentifier = #"attendance";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
UILabel *student_lastname = (UILabel *)[cell viewWithTag:1];
UILabel *student_firstname = (UILabel *)[cell viewWithTag:2];
UILabel *student_midname = (UILabel *)[cell viewWithTag:3];
student_lastname.text = object[#"Last_Name"];
student_firstname.text = object[#"First_Name"];
student_midname.text = object[#"Middle_Name"];
UILabel *studentNum = (UILabel *)[cell viewWithTag:5];
studentNum.text = [NSString stringWithFormat:#"%d", indexPath.row];
return cell;
}
So, I am assuming that you only want to show a checkmark if the cell is selected. Recommend you to update your question please.
The problem happens because your cells are being re-used when you scroll and you need to first set the accessory type accordingly in your cellForRowAtIndexPath method like this:
if ([cell isSelected])
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;
This is to ensure that whenever a cell is reused, you make sure that it does not have any checkmark if its not selected, and show a checkmark if selected.
Next use the following two methods to set the checkmark and remove it when the cell is unselected/selected like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
Also, you should not call [thetableView deselectRowAtIndexPath:[thetableView indexPathForSelectedRow] animated:true]; This method is automatically called in your case.
Hope this solves your problem.
Not a fan of either answer provided. Apple's UITableView implementation forces you into MVC (model-view-controller). Cell views, since they can be reused, should never store state, which is what you're doing relying on cell.selected. All answers above will fail if there are more cells than fit on screen, since they will be reused.
Given that you already have an object to store data such as "Last_Name", etc. you should also store #"Selected" as well.
Here's what your code should look like:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(nullable PFObject *)object {
static NSString *cellIdentifier = #"attendance";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
UILabel *student_lastname = (UILabel *)[cell viewWithTag:1];
UILabel *student_firstname = (UILabel *)[cell viewWithTag:2];
UILabel *student_midname = (UILabel *)[cell viewWithTag:3];
student_lastname.text = object[#"Last_Name"];
student_firstname.text = object[#"First_Name"];
student_midname.text = object[#"Middle_Name"];
NSNumber *selectedVal = object[#"Selected"];
if (selectedVal && [selectedVal boolValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
UILabel *studentNum = (UILabel *)[cell viewWithTag:5];
studentNum.text = [NSString stringWithFormat:#"%d", indexPath.row];
return cell;
}
Then, in didSelect, you change the state of the cell and force a reload (of that cell only, if appropriate).
- (void)tableView:(UITableView *)thetableView
didSelectRowAtIndexPath:(NSIndexPath *)newindexPath {
NSNumber *selectedVal = object[#"Selected"];
BOOL selected = selectedVal ? ![selectedVal boolValue] : YES;
object[#"Selected"] = #(selected);
[thetableView reloadRowsAtIndexPaths:#[newindexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
I have a cell. Whenever the text in cell row is equal to "(null)" I want the label to be on the right hand side of the the cell.
Here is my code at the moment, but it isn't doing anything. No errors, it just doesn't align to the right hand side of the cell. Any ideas?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"ChatListItem";
NSDictionary *itemAtIndex = (NSDictionary *)[messages objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if([[itemAtIndex objectForKey:#"user"] isEqualToString:#"(null)"]){
cell.textLabel.textAlignment=UITextAlignmentRight;
cell.detailTextLabel.textAlignment=UITextAlignmentRight;
}
cell.textLabel.text = [itemAtIndex objectForKey:#"text"];
cell.detailTextLabel.text = [itemAtIndex objectForKey:#"user"];
return cell;
}
First, did you step through the code and check the contents of the value for keys "user" and "text"?
If all is as expected, you should do the following:
Replace UITextAlignmentRight with NSTextAlignmentRight to silence compiler warnings.
Explicitly set NSTextAlignmentRight and NSTextAlignmentLeft, otherwise you will not get the correct update in recycled cells.
Finally, make sure the label's width is fixed. Otherwise, the width of the label will be based on its content, so that the alignment (within the label) loses its effect.
The only working solution for your case (without subclassing of the UITableViewCell of course)
is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ChatListItem";
NSDictionary *dict = [_tableData objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = dict[#"text"];
cell.detailTextLabel.text = dict[#"user"];
if ([dict[#"user"] isEqualToString:#"(null)"]) {
[self performSelector:#selector(alignText:) withObject:cell afterDelay:0.0];
}
return cell;
}
- (void)alignText:(UITableViewCell*)cell
{
CGRect frame = cell.textLabel.frame;
frame.origin.x = cell.frame.size.width - (frame.size.width + 10.0);
cell.textLabel.frame = frame;
frame = cell.detailTextLabel.frame;
frame.origin.x = cell.frame.size.width - (frame.size.width + 10.0);
cell.detailTextLabel.frame = frame;
[cell setNeedsDisplay];
}
As for me, I would better make a subclass.
I would to create a custom cell without create a custom cell class. I know that this is possible. I've created in storyboard a tableviewcontroller with a prototype cell with a label. In the attibutes I've set the cell name "mycell". And the code that I used is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"mycell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];}
UILabel *title = (UILabel*) [cell viewWithTag:1];
title.text = #"Hi";
return cell;
}
But when my app run I see only a empty table without a cell with my label.
In the tableView methods I've used:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1;}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5; }
Thank you.
If you are adding UILabel programmatically it needs to be alloc init, should be given a frame and added to the cell as sub view.
UILabel *title = (UILabel*) [cell viewWithTag:1];
title.text = #"Hi";
return cell;
Should be
UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(50, 10, 50, 20)];
title.text = #"Hi";
[cell addSubview:title];
return cell;
I've subclassed the UITableViewCell to add custom appearance to it. At the init level of the MYTableViewCell I added 4 subviews: UIImageView, and three UILabel(s). All 4 subviews have a different Tag assigned to them.
Inside the cellForRowAtIndexPath method I either create a new cell if it wasn't available at first, or reuse available one and assign the proper text to the ui labels.
The problem I am having is that if I try to scroll super fast, then the data gets messed up, however if I scroll up and down more slowly, then everything works fine.
Any thoughts??
Below is the code:
- (MyTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"itemListTableViewCell";
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
DisplayableEntity *displayableEntity = [self.fetchedResultsController objectAtIndexPath:indexPath];
if( ! cell ) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[self tableView:tableView appearanceForCell:cell withEntity:displayableEntity];
} else {
UIImageView *imageView = (UIImageView *) [cell viewWithTag:IMAGEVIEW_TAG];
imageView.image = [UIImage imageNamed:displayableEntity.displayImageName];
UILabel *titleLabel = (UILabel *) [cell viewWithTag:TITLEVIEW_TAG];
titleLabel.text = displayableEntity.entityName;
UILabel *itemDescription = (UILabel *) [cell viewWithTag:DESCRIPTION_TAG];
itemDescription.text = displayableEntity.entityDesctiption;
}
}
// some code removed to make it brief
- (void)tableView:(UITableView *)tableView appearanceForCell:(MyTableViewCell *)cell withEntity:(DisplayableEntity *)entity {
// cell image view
UIImageView *cellImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[entity displayImageName]]];
[cellImageView setTag:IMAGEVIEW_TAG];
[cell addSubview:cellImageView];
// adding entity name label
UILabel *itemTitleName = [self itemTitleNameLabelWithFrame:itemNameLabelRect itemName:[entity entityName]];
[itemTitleName setTag:TITLEVIEW_TAG];
[cell addSubview:itemTitleName];
// adding 'assigned to' label right under the item name label
UILabel *itemDescriptionLabel = [self itemDescriptionLabelWithFrame:descriptionLabelFrame itemDescription:[entity entityDesctiption]];
[itemDescriptionLabel setTag:DESCRIPTION_TAG];
[cell addSubview:itemDescriptionLabel];
}
I see some troubles in tableView:cellForRowAtIndexPath: logic
It should be:
Dequeue cell
If cell cannot be dequeued - create the new one
Set all cell properties
I mean something like this:
- (MyTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"itemListTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
} // <-- Note there is no else, we should reset properties in both cases
NSManagedObject *managedObject = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [managedObject valueForKey:#"text"];
cell.imageView.image = [managedObject valueForKey:#"image"];
return cell;
}