Duplicate Object when adding in NSMutableArray [iOS] - ios

Hi I have this problem that if I add more than 13 items in my uitableview the items will repeat
this is my code for
Adding:
[categoryList insertObject:inputcategory atIndex:0];
NSIndexPath *indexpath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtindexPaths#[indexpath] withRowAnimation:UITableViewRowAnimationAutomatic];
CellForRow:
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.textlabel.text = [categoryList objectAtIndex:indexPath.row];
}
NumberofRows
return [categoryList count];
the first 12 items are okay but after that it adds the same last object in my NSMutableArray again in my uitableview and when I reload it, It only records 12 items.

In cellForRowAtIndexPath: method change
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.textlabel.text = [categoryList objectAtIndex:indexPath.row];
}
to
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textlabel.text = [categoryList objectAtIndex:indexPath.row];
If you set text in if (cell == nil) it will not updated on reusing the cell. When reusing the cell will not be nil.

Akhilrajtr's answer is correct but is the old way of doing it. In 2014 for iOS 6+ you should instead be using the UITableView methods
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier
or
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
and
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath
Getting a cell in cellForRowAtIndexPath: becomes much simpler...
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
"identifier" is the unique string used to identify cells for reuse, just like the old way.
From Apple's documentation, "This method dequeues an existing cell if one is available or creates a new one based on the class or nib file you previously registered." You don't need to use initWithStyle any more, and if you think you do then consider subclassing UITableViewCell instead to more precisely control the appearance of your cells.
No more checks for the cell being nil are required. Just go ahead and set labels if your cell is a plain UITableViewCell or configure your subclassed cell (which should of course have made itself ready for reuse with the prepareForReuse: method)

Apple mention the performance this in their documentation on UITableViews.
it's should be about Reuse Cells. So if the cell != nil u can't got this below work:
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.textlabel.text = [categoryList objectAtIndex:indexPath.row];
}
Reuse cells. - Object allocation has a performance cost, especially if the allocation has to happen repeatedly over a short period—say, when the user scrolls a table view. If you reuse cells instead of allocating new ones, you greatly enhance table view performance.

Related

Label in [cell viewWithTag:] is always returned as nil

I'm struggling with this basic thing for more than a day and it drives me crazy! Funny thing is, I have very similar thing on other screen and it works just fine! I have done this a thousand times, but never experienced something so odd. Maybe is this behavior in iOS 8 only?
On my very simple Prototype cell I have two labels with tags 102 and 103. But when I want to set text to them, they are always nil.
I have double checked that identifier is correct and that tag is the same as in Storyboard.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString * identifier = #"secondReusableIdentifier";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
for (UIView *subview in [cell subviews]) {
NSLog(#"subview: %lu", subview.tag); // prints 0
}
UILabel * label1 = (UILabel *)[cell viewWithTag:102]; // returns nil
UILabel * label2 = (UILabel *)[cell viewWithTag:103]; // returns nil
if (self.items.count) {
MyObject *obj = [self.items objectAtIndex:indexPath.row];
label1.text = obj.someProperty;
fuelPrice2.text = obj.someOtherProperty;
}
}
return cell;
Any suggestions would be appreciated.
In your code you are creating a new cell, that it's not the same than you have in Storyboard.
Change this: This is the old way, or the way you use when the cell is by code or nib, and you don't use storyboard. This code means.
NSString * identifier = #"secondReusableIdentifier";
// If I have available a cell with this identifier: secondReusableIdentifier, let's go to use it.
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil){
// If not, we create a new cell with this identifier. This methods is previous to storyboard, and this methods create a new cell, but does´t look in Storyboard if this identifier exist, or something like that.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
To this, in other hand when Apple launched storyboard the framework grow with this methods, that work in this way: If there is a cell free use it, if not it look in Storyboard for a cell with this identifier and create a new cell with this info. (You can use this methods also by code and with nib file, but you must register the class before...).
// Be sure than: "secondReusableIdentifier", it's its identifier in storyboard
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"secondReusableIdentifier" forIndexPath:indexPath];
Replacing this:
NSString * identifier = #"secondReusableIdentifier";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
BY
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"secondReusableIdentifier" forIndexPath:indexPath];
for creating a cell will solve your problem because, it always returns a cell. It either re uses existing cells or creates a new one and returns if there are no cells.
The most important difference is that the forIndexPath: version crashes if you didn't register a class or nib for the identifier. The older (non-forIndexPath:) version returns nil in that case.
You must register a class or nib for using this. But if you create your table view and your cell prototypes in a storyboard, the storyboard loader takes care of registering the cell prototypes that you defined in the storyboard.
Hope this helps.. :)
If
for (UIView *subview in [cell subviews]) {
NSLog(#"subview: %lu", subview.tag); // prints 0
}
prints 0, why do you do this??
UILabel * label1 = (UILabel *)[cell viewWithTag:102];
UILabel * label2 = (UILabel *)[cell viewWithTag:103];
So, if you created a custom cell with IB, you have to create a custom class and use it instead of a simple UITableViewCell

Prioritize the table view cells IOS

How to prfioritize the tableView cells in order to display at the top of tableView in the mothod cellForRowAtIndexPath?
I want to display the priority cells at the top of the tableView cells checking if they have priority or not, depending on the bool value of havePriority property, such that if havePriority is 1, it should be true else should be after the priority list.
I've tried by using different cell identifiers but unable to got the list at the top.
My Code:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (havePriority) // if the cell wants priority to be at top, have a value in bool
{
cell.textLabel = sOmeText; //
NSLog(#"%#",sOmeText);
}
return cell;
You shouldn't be setting the order of the cells in that method. By then it is far too late.
This is really something that should be done in the model layer. The list of items to be displayed should already be sorted according to their priority, and that way they will show up at the top.
Secondarily, this type of code isn't really necessary anymore:
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
You should be using the more modern method:
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
This will either return a cell from the reuse pool or create a new one, so it always returns a cell, and there is no need to create one yourself.

UITableViewCell - ContentView overlapping when scrolling

I have added a UILabel as the content view of my tableview. The text in the UILabel is overlapping upon scrolling. Below is the code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.chatTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Group"];
static NSString *CellIdentifier = #"Group";
UITableViewCell *cell = [self.chatTableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = self.latestTrimText;
UILabel *cellLabel = [[UILabel alloc]init];
cellLabel.text = self.dateOfLatestTrim;
cellLabel.frame = CGRectMake(15, 0, 150, 30);
[cell.contentView addSubview:cellLabel];
return cell;
}
I can fix this by changing to UITableViewCell *cell = [self.chatTableView dequeueReusableCellWithIdentifier:nil];
But then, the scrolling of the tableview won't be smooth. Is there another way to fix the issue?
The tableview cells are recycled and reused potentially an infinite amount of times. This is what the reuse identifier stands for.
Take care not to add any subview outside the if (cell==nil) block. Now you are just adding again and again a new label to the same cell instance. This is why your texts overlaps each other.
I would recommend you to read the documentation on how the cells are recycled and reused.
Your fix is not correct because by passing a nil reuse id, you are just preventing the recycling process to play. You create a new instance for each row of your tableview. This is why you have performance issues then.

Tableview subtitles on IOS7

I'm trying to add a subtitle to my tableview cells, but they are not displayed.
Where is the mistake?
Is the row
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle]
up-to-date, also with iOS 7?
Best regards
Frank
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"TestA";
cell.detailTextLabel.text = #"TestB";
return cell;
}
This code:
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
will never execute because dequeueReusableCellWithIdentifier: forIndexPath: is guaranteed to allocate a new cell.
Unfortunately, registerClass:forCellReuseIdentifier: doesn't let you specify a UITableViewCellStyle.
Change dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath to simply dequeueReusableCellWithIdentifier:CellIdentifier. This method does not guarantee a cell will be returned.* When it's not, your code will then create a new cell with the style you want.
* - (It will if you're using a storyboard, as rdelmar points out, but that's not the case here.)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
cell.textLabel.text = #"Title1";
cell.detailTextLabel.text = #"Subtitle 1";
return cell;
}
I had a similar problem and no solution on the internet worked for me. Turns out I was being an idiot. I'll post my solution just incase someone else experience a similar scenario.
I am assuming that you are using storyboard, have created a prototype and set the style to subtitle
In your storyboards document outline, make sure you select the protoptype cell and select the subtitle. See image:
Now ensure that the label font colour is of a nature that us visible on your background!
Simply subclass UITableViewCell and override
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
with the first line being
[super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
And register your cell class with the table view
[tableView registerClass:[YourCellSubclass class] forCellReuseIdentifier:#"YourCellID"];

UITableViewCell style and dequeueReusableCellWithIdentifier

So I register my cell:
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// setting up the cell
}
The issue is I can't set the cell.detailTextLabel.text property. The cell is never nil.
If called first, table view registerClass will cause dequeueReusableCellWithIdentifier to return non-nil cell if the cell reuse identifier matches.
I believe registerClass is generally used for cells that will be a custom cell derived from UITableViewCell. Your custom cell can overrite initWithStyle and set the style there.
It's not always necessary to create a custom cell.
If you want to set the cell style then don't call registerClass.
you need do followed 3 changes to achieve you goal:
remove registerClass statement.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
=> UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
use initWithStyle:UITableViewCellStyleSubtitle
usually there are two ways to create cell with subtile, firstly with custom UITableViewCell, set style in the init. secondly with followed code, which is what you wanted:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
The easiest way is to use the storyboard, and set the cell style in IB. In that case, you shouldn't register anything, nor should you have an if (cell == nil) clause. It doesn't seem to matter whether you use dequeueReusableCellWithIdentifier: or dequeueReusableCellWithIdentifier:forIndexPath. They both are guaranteed to return a cell when that cell is created in the storyboard.
Create a custom cell. Change its style in interface builder. Use table view register the cell from nib your view controller.
And the code:
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerNib:[UINib nibWithNibName:#"YourCustomCell" bundle:nil] forCellReuseIdentifier:kReuseIdentifier];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YourCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:kReuseIdentifier];
// Do things with the cell.
// The cell has no chance to be nil because you've already registered it in viewDidLoad method.
// So there's not need to write any code like if(cell==nil).
// Just use it.
return cell;
}
Try using the UITableViewCellStyleSubtitle style for the cell. Change the line in the if statement to this:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
You need to change the cell style:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
to this
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
This will work for you.
This is an old question. I just want to provide a alternative solution.
Why not try below, after you register the cell:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
then do:
[cell initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier ];
It works for me.

Resources