In my Storyboard I've got a table view.
I'm filling that table view using data loaded from a JSON file (loaded in viewDidLoad).
In my UITableView I did set "Prototype Cells" to 1 so I can easily select a Accessory.
My Prototype Cell has a Seque to a new View which needs to show the details of the selected item.
I'm filling my Cells programmatically using this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *videoTableIdentifier = #"VideoCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:videoTableIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:videoTableIdentifier];
UILabel * title = [[UILabel alloc] initWithFrame:CGRectMake(40,5, 240,20)];
[cell addSubview:title];
[title setText:[[videos objectAtIndex:indexPath.row] objectForKey:#"title"]];
UILabel * detail = [[UILabel alloc] initWithFrame:CGRectMake(40,28, 100,10)];
[cell addSubview:detail];
[detail setText:[NSString stringWithFormat:#"Year: %#", [[videos objectAtIndex:indexPath.row] objectForKey:#"year"]]];
[cell addSubview:[[UIImageView alloc] initWithImage:[UIImage imageNamed:[[videos objectAtIndex:indexPath.row] objectForKey:#"image"]]]];
return cell;
}
When I start scrolling the following happens:
So I started searching for a solution and found that I had to set: dequeueReusableCellWithIdentifier:videoTableIdentifier to "nil".
While I did that, this solved my problem, BUT now me Prototype Cell Accessory and Seque are gone so I can't navigate to the next view anymore.
I couldn't find a solution for this on the internets so I decided to ask this.
Would be awesome if someone could help.
Your problem is that every time you re-use a cell, you are adding new subviews to it, so if you have a cell with some labels, and you reuse it and add more labels, all of them appear overlaped.
I suggest you to make your own custom UITableViewCell with labels on it, and just change the value of the labels every time.
if (!title){
title = [[UILabel alloc] initWithFrame:CGRectMake(40,5, 240,20)];
[cell addSubview:title];
}
[title setText:[[videos objectAtIndex:indexPath.row] objectForKey:#"title"]];
Previous answer author is right. It could be better to use your own subclass of UITableViewCell.
I don't see any memory release. Are you using automatic reference counting?
Related
Say i’m loading UITableview with each UITextView inside each cell as subview.And i’ve assigned indexPath.row as tags for each textview.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"userDetails";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UITextView *textView=[[UITextView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, 60)];
NSString * myString = [contentArray1 objectAtIndex:indexPath.row];
textView.text= myString;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
[tapRecognizer setNumberOfTouchesRequired:1];
[tapRecognizer setDelegate:self];
textView.userInteractionEnabled = YES;
textView.tag = indexPath.row;//assign tags to textview
[textView addGestureRecognizer:tapRecognizer];
[cell addSubview:textView];
return cell;
}
Below method gets called once the user taps on any textview.I’m seeing proper tag values printed when i tap on any textviews.
-(void) action:(id)sender
{
//NSLog(#"TESTING TAP");
UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer *)sender;
NSLog (#"%d",[tapRecognizer.view tag]);
}
Now i would like to insert row in my tableview,say at index 3.
What i did is simple,
[contentArray1 insertObject:[NSString stringWithFormat:#"added cell”] atIndex:3];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:3 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
Now when i try to tap on any textviews after the inserted cell,i’m able to see the old tag values.Meaning,after row gets inserted to tableview at index=3,when i tap on textview i can see tag=2,then again when i tap on next cell’s textview i can see tag=2,it should be 3.
My question is,once we insert any row/cell in tableview,the tableview will not refresh other cell tags/index?….
I can fix it by calling reloadVisibleCells method.But i’m looking out for better solution.I don’t want to refresh whole screen just for inserting a row.Any solutions would be greatly appreciated.
Try to do it like this:
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
Your problem is that inserting/deleting rows doesn't cause reloading of other rows, and this is the expected and correct behavior. However, since -tableView:cellForRowAtIndexPath: is not called for those other rows, they are still configured with old (now obsolete) tags.
You can fix it in a number of ways (off the top of my head):
subclass UITableViewCell and store the represented object itself as its property (instead of the object's index)
associate the object with the UITableViewCell with objc_setAssociatedObject()
use -[UITableView indexPathForCell:] instead of tags to figure out a cell's real index path.
I am having a table view. in that I am displaying an array. when I scroll the tableview its not moving up to the bottom of a last table cell. what should I do for that.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return tableResourceArray.count;}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellIdentifier=[NSString stringWithFormat:#"CustomCell:%d",indexPath.row];
customcell *cell = (customcell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[customcell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if(tableView==table) {
table.backgroundColor = [UIColor clearColor];
table.separatorColor=[UIColor clearColor];
NSDictionary *slug = [category objectAtIndex:indexPath.row];
NSLog(#"gggg:%#",slug);
cell.ctitle.text = [NSString stringWithFormat:#"%#", [slug objectForKey:#"title"]];
cell.ctitle.font=[UIFont fontWithName:#"Arial" size:12];
cell.cdate.text = [NSString stringWithFormat:#"Date:%#", [slug objectForKey:#"date"]];
cell.cdate.textColor=[UIColor colorWithRed:0.820 green:0.776 blue:0.526 alpha:1.000];
cell.ccontent.text = [NSString stringWithFormat:#"%#", [slug objectForKey:#"content"]];
}
Solution 1 :
You have declared Identifier as non-static variable. This might cause scroll issue.
NSString *cellIdentifier=[NSString stringWithFormat:#"CustomCell:%d",indexPath.row];
Instead use below :
NSString *cellIdentifier= #"CustomCellIdentifier";
Static variable are used to construct only once and it avoid memory creation all the time whenever “CellForRowAtIndexPath” method is called. If non-static variable is used lots of memory are created that may lead to scrolling issue.
Solution 2 : // Not sure. But might help
Allocate dictionary before usage.
NSDictionary *slug = [[NSDictionary alloc] init];
slug = [category objectAtIndex:indexPath.row];
I solved the problem by changing my table view frame.
Assuming that your screen size is 320x480, the bottom part of your table view frame is out of screen by 130 because of your table view's frame (origin y 130 + height 480). Or even more off if you have more subviews at the bottom of screen that cover tableview. Change your table view frame so that the bottom of your table view ends at the bottom of the screen.
I'm using the following code to just make a single UILabel on my cell. (I know this example is contrived; I can't even get this to work much less my ultimate design goal.)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Photo";
UILabel *username;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
username = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 20.0)];
username.tag = USERNAME_TAG;
username.font = [UIFont systemFontOfSize:14.0];
username.textAlignment = UITextAlignmentLeft;
username.textColor = [UIColor blackColor];
username.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:username];
} else {
username = (UILabel *)[cell.contentView viewWithTag:USERNAME_TAG];
}
//NSDictionary *photos = [self.photos objectAtIndex:indexPath.row];
username.text = #"Testing!";
return cell;
}
I got this template code from here
However, when I run the code, there is no text label in the cell, but the accessory does show up.
I find it much easier to use a custom xib for table cells that don't lend themselves easily to one of the normal types. The basic steps are: create a xib with your row, create an outlet property, then in your cellForRowAtIndexPath method, call loadNibNamed and assign it to a local variable (that you return), and set the outlet back to nil.
The secret is that loading a nib with a nil outlet causes the nil outlet to be init'd and inflated with the contents of the nib.
Described here (scroll to Loading Custom Table-View Cells From Nib Files):
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7
If you need a lot of user interaction, you are probably better off sub-classing UITableViewCell and supplying it with its own xib. If you have a lot of controls on a cell, you will end up using tags to determine the source of events, which gets messy with more than a few tags.
Apple changed the behavior of dequeueReusableCellWithIdentifier when they released 5.0.
In 5.0+ it is guaranteed to always return a copy of your cell (if it exists in your xib/storyboard), even if you haven't even loaded a cell yet. Prior to this, it would return a cell only if there was one that was ready to be recycled.
Because of this, some older code which assumes that you will receive nil unless you have already initialized it will break.
So, get rid of the identifier (or the entire cell) in your xib/storyboard or modify your logic so that it initializes it when needed (perhaps based on whether or not there is a view with the specified tag).
It looks like you're just trying to add a "standard" UILabel to the UITableViewCell.
Here's what you do: every UITableViewCell of the UITableViewCellStyleDefault has a property called textLabel. In your code, do the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Photo";
UILabel *username;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
cell.textLabel.text = #"Testing!";
return cell;
}
PS: Looks like you're trying to add an image at some point? There's a property for that as well. Take a look at the documentation for the imageView property.
i'm working on a tableview and i am newbie in dynamically creating cells (i used to create them with IB and then link them to their tableviewcell controller etc..).
Everything works great and recpected arrays are updated properly but when i fire [self.tableview reloadData] the program just redraws new values over old cells. for example if there "TEST CELL" value in a uilabel inside a cell, when i update the data to "CELL TEST" and fire the reloadData, the uilabel looks like there are two labels on top of each other and both values are visible. (its like creating two uilabel with exact same location and same size and setting their values)
and this event happens everytime i fire reloadData, with each reload, the program looks like its adding another uilabel on top of the older one. heres my code:
- (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];
}
UILabel *lblText1 = [[UILabel alloc] initWithFrame:CGRectMake(30, 5, 130, 30)];
lblText1.adjustsFontSizeToFitWidth = YES;
lblText1.backgroundColor = [UIColor clearColor];
lblText1.text = [lblText1Array objectAtIndex:indexPath.row];
[cell addSubview:lblText1];
[lblText1 release];
if(indexPath.row<3)
{
UILabel *lblText2 = [[UILabel alloc] initWithFrame:CGRectMake(170, 5, 130, 30)];
lblText2.adjustsFontSizeToFitWidth = YES;
lblText2.backgroundColor = [UIColor clearColor];
lblText2.text = nil;
lblText2.text = [spParameters objectAtIndex:indexPath.row];
[cell addSubview:lblText2];
[lblText2 release];
}
else if(indexPath.row==3){
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(170, 7, 130, 30)];
textField.adjustsFontSizeToFitWidth = YES;
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.placeholder = #"please insert value";
textField.textAlignment = UITextAlignmentCenter;
textField.delegate = self;
textField.text = [spParameters objectAtIndex:indexPath.row];
[cell addSubview:textField];
[textField release];
}
else if(indexPath.row == 4)
{
UISwitch *gSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(170, 7, 130, 30)];
[gSwitch setOn:FALSE];
[gSwitch addTarget: self action: #selector(switchValueChanged:) forControlEvents:UIControlEventValueChanged];
[cell addSubview:gSwitch];
[gSwitch release];
}
// Configure the cell...
return cell;
}
im releasing the components after i adding them to subviews, and i am thinking about if theres something wrong with the reuseidentifier...
Thanx for helping.
It looks as though you're populating a detail view with a fixed number of cells, so you should consider creating the instances statically, for example in viewDidLoad or in Interface Builder. You could store each cell in a separate instance variable, and just return the one that corresponds the current row each time tableView:cellForRowAtIndexPath: is called.
If you create the cells programmatically, add whatever subviews you need at that time. Otherwise, as I mentioned, you could do that in Interface Builder, which often makes it easier to set up the details of controls such as text fields. Note though that UITableViewCell already contains an instance of UILabel, so adding one yourself is redundant. Instead, just access the cell's textLabel property to get its label.
Hey Simple little app for educational purposes, its a simple uitableview and I want users to be able to use a uislider to adjust the font size as needed. The code I have now works to change the font but only when the view is updated, ie when i pull up or down the table view to reveal other cells. I'd like the font change to be reflected immediately as a user moves the uislider if possible, here's the code that I have working half way:
-(UITableViewCell *) tableView: (UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: SimpleTableIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier: SimpleTableIdentifier] autorelease];
NSUInteger row = [indexPath row];
cell.text = [listData objectAtIndex:row];
UIImage *image = [UIImage imageNamed:#"star.png"];
cell.font = [UIFont systemFontOfSize:[fontSlider value]];
cell.image = image;
return cell;
}
You could make an IBAction for the fontSlider for the "Value Changed" option as follows:
-(IBAction) refreshResize
{
[self.view setNeedsDisplay];
}
That should work. setNeedsDisplay refreshes the screen.