I am having some odd behaviour with my UITableView. When I scroll up and down sometimes the bottom row will vanish and disappear again. Not sure why this is happening. Each of my UITableCells contains 4 buttons like so :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
int x,y;
x=5;
y=0;
for(int i = (int)indexPath.row*4; i<(int)indexPath.row*4+4; i++) {
//make sure we don't hit a blank in the array
if(i<[self.buttons count]) {
UIButton *button = [[UIButton alloc] init];
button = self.buttons[i];
button.layer.cornerRadius = button.frame.size.width / 2;
button.clipsToBounds = YES;
[button setFrame:CGRectMake(x, y, 70, 70)];
[cell.contentView addSubview:button];
x+=80;
}
}
return cell;
}
Could this be because I have the same cell reuse identifier for each cell? Does it need to be unique?
In this code:
UIButton *button = [[UIButton alloc] init];
button = self.buttons[i];
You allocate and initialize a button and then reassign the variable to entry i of a buttons array. Was this meant to be something else?
Also you are adding buttons to the cell content as a subview. Cells are reused when you scroll, so you could easily be adding more than 4 buttons to a given cell which will be causing you display issues. When you dequeue a cell it could already have 4 buttons in it.
Can you not just have 4 buttons in the cell prototype, saving you all this effort in the code.
No, cell reuse identifier need to be constant for a tableView. However you should use is as a static variable.
Change to static NSString *cellID = [NSString stringWithFormat:#"cellID",indexPath.row]; instead of
NSString *cellID = [NSString stringWithFormat:#"cell%d",indexPath.row];
Also are you making any changes in willDisplayCell or didEndDisplayingCell delegate methods. If yes Plz post that code too.
Re-write like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
if (cell.contentView.subviews.count == 0) {
int x,y;
x=5;
y=0;
for (int i = 0; i < 4; i++) {
UIButton *button = [[UIButton alloc] init];
button = self.buttons[i];
button.layer.cornerRadius = button.frame.size.width / 2;
button.clipsToBounds = YES;
[button setFrame:CGRectMake(x, y, 70, 70)];
[cell.contentView addSubview:button];
x+=80;
}
}
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
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;
I added UIButton in the last cell of UITableView, and I want to show more cells with click..
- (void)viewDidLoad {
[super viewDidLoad];
rowCount = 15;
dataArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 100; i++) {
NSString *value = [NSString stringWithFormat:#"the value of row is: %d", i +1];
[dataArray addObject:value];
}
}
...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return rowCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *idintify = #"Cell";
UITableViewCell *cell = [self.myTable dequeueReusableCellWithIdentifier:idintify];
[cell.textLabel setText:dataArray[indexPath.row]];
if (indexPath.row == rowCount -1) {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, self.myTable.frame.size.width, 44)];
[button addTarget:self action:#selector(cellButtonClicked) forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundColor:[UIColor yellowColor]];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:#"Show more..." forState:UIControlStateNormal];
[cell addSubview:button];
}
return cell;
}
...
- (void)cellButtonClicked
{
if (rowCount +10 >= dataArray.count) {
rowCount = dataArray.count;
} else {
rowCount += 10;
}
[self.myTable reloadData];
NSLog(#"%ld", (long)rowCount);
}
At beginning its work properly, But when I scrolled the table the cell did not changed!
I want to show the button at last cell
Cells in a table view are reused when the user scrolls. When you add a button to an instance of your cell prototype and don't remove it, the button remains, even if the cell is used at another index later. This results in what you have on your screenshots.
You should create two cell prototypes in the interface builder and your cellForRow:atIndexPath: should look something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return cell with data
if (indexPath.row < dataArray.count) {
UITableViewCell *cell = [self.myTable dequeueReusableCellWithIdentifier:#"cell_prototype_1"];
[cell.textLabel setText:dataArray[indexPath.row]];
return cell;
}
// If it's the last index, return cell with button
else {
UITableViewCell *cell = [self.myTable dequeueReusableCellWithIdentifier:#"cell_prototype_2"];
return cell;
}
// This won't get called
return [UITableViewCell new];
}
That will be better if you use two different cells otherwise replace the code in your cellForRowAtIndexPath
if (indexPath.row == rowCount -1){}
with
if ( (indexPath.row-15)%10 == 0 ){}
and Put
[cell.textLabel setText:dataArray[indexPath.row]];
in the else part.
and place code just below UITableViewCell *cell....
for(UIView *view in cell.view.subviews){
if([view isKindOfClass: [UIButton class]]){
[view removeFromSuperView];
}
}
I think you need to refresh visible cells, doing:
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
Also better way to do it would be that you have 2 prototype cells, one for data, one with button. let's sey you give the second one identifier "buttonCell", than you just do this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *idintify = (indexPath.row < rowCount -) ? #"Cell" : #"buttonCell";
UITableViewCell *cell = [self.myTable dequeueReusableCellWithIdentifier:idintify];
if (cell == nil) //initialize
if (indexPath.row < rowCount -1) {
[cell.textLabel setText:dataArray[indexPath.row]];
}
return cell;
}
and in didSelectRowAtIndexPath you increase rowCount and reload data if indexPath.row == rowCount - 1
I am fairly new to Objective C programming, and have a UITableView setup with a custom cell. I want to make it so a user can touch a button that will add another cell, and this button will appear in the last cell only. Currently, it is not showing up. Here is the code that I am using. I have created the button within the custom cell, and used "setHidden:YES" to hide it within the cell itself. I am trying "setHidden:NO" to make the button appear in the TableView code, but it is not working. I thought maybe it had something to do with reloading the cell, but I am not sure if I am going in the right direction with this or not. I would appreciate any help on this, thanks.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{workoutTableViewCell *cell = (workoutTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
[cell.addButton setTitle:(NSString *)indexPath forState:UIControlStateApplication];
[cell.textLabel setText:[NSString stringWithFormat:#"Row %i in Section %i", [indexPath row], [indexPath section]]];
NSInteger sectionsAmount = [tableView numberOfSections];
NSInteger rowsAmount = [tableView numberOfRowsInSection:[indexPath section]];
if ([indexPath section] == sectionsAmount - 1 && [indexPath row] == rowsAmount - 1) {
NSLog(#"Reached last cell");
[cell.addButton setHidden:NO];
if (lc == NO)
{[[self tableView] reloadData];
lc = YES;
}
}
return cell;
}
Following UITableViewDataSource method will help you to return exact number of rows available in section. Here you need to return additional as you want to have last as your button.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return yourRowCount + 1;
}
Now in folowing method you will check row number using indexpath.row as
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *lastCellIdentifier = #"LastCellIdentifier";
static NSString *workoutCellIdentifier = #"WorkoutCellIdentifier";
if(indexPath.row==(yourRowCount+1)){ //This is last cell so create normal cell
UITableViewCell *lastcell = [tableView dequeueReusableCellWithIdentifier:lastCellIdentifier];
if(!lastcell){
lastcell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:lastCellIdentifier];
CGRect frame = CGRectMake(0,0,320,40);
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeCustom];
[aButton addTarget:self action:#selector(btnAddRowTapped:) forControlEvents:UIControlEventTouchUpInside];
aButton.frame = frame;
[lastcell addSubview:aButton];
}
return lastcell;
} else { //This is normal cells so create your worktouttablecell
workoutTableViewCell *cell = (workoutTableViewCell *)[tableView dequeueReusableCellWithIdentifier:workoutCellIdentifier];
//Configure your cell
}
}
Or you can do like create UIView programatically and set it as FooterView as suggested by #student in comment code would look like,
CGRect frame = CGRectMake(0,0,320,40);
UIView *footerView = [[UIView alloc] initWithFrame:frame];
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeCustom];
[aButton addTarget:self action:#selector(btnAddRowTapped:) forControlEvents:UIControlEventTouchUpInside];
aButton.frame = frame;
[footerView addSubView:aButton];
[yourTableNmae setTableFooterView:footerView];
Declare method as follow
-(IBAction)btnAddRowTapped:(id)sender{
NSLog(#"Your button tapped");
}
if ([indexPath section] == sectionsAmount - 1 && [indexPath row] == rowsAmount - 1) {
NSLog(#"Reached last cell");
[cell.addButton setHidden:NO];
} else {
[cell.addButton setHidden:YES];
}
Replace this code in your program.
If you know your number of cells in the uitable and you wish to just know when the last row will appear, you could implement the following delegate method
(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
this method tells the delegate table view is about to draw cell for particular row, simple compare your row with table rowcount.
my problem is I want to add custom designed seperator line between my UITableview cells except first cell of table(indexPath.row=0). Following code seems fine when I reload my table first time. However when I scroll down and scroll up it appears custom seperator line on the top of the first cell of table. I printed indexpath.row value and discovered that if I scrolls up first cell of table is reconstructed at indexpath.row=7. Any solution? Thanks for reply :) My code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCellIdentifier";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (CustomCell *)[CustomCell cellFromNibNamed:#"CustomTwitterCell"];
}
if(indexPath.row!=0)
{
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 2.5)];
lineView.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"line.png"]];
[cell.contentView addSubview:lineView];
[lineView release];
}
NSDictionary *tweet;
tweet= [twitterTableArray objectAtIndex:indexPath.row];
cell.twitterTextLabel.text=[tweet objectForKey:#"text"];
cell.customSubLabel.text=[NSString stringWithFormat:#"%d",indexpath.row];
}
That because the table uses a reused cell that was build with the separator line, you can use two CellIdentifier one for your first row, and another for all the rest..
Try something like (didn't test the code but it should work):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *FirstCellIdentifier = #"FirstCellIdentifier";
static NSString *OthersCellIdentifier = #"OthersCellIdentifier";
NSString *cellIndentitier = indexPath.row == 0 ? FirstCellIdentifier : OthersCellIdentifier;
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentitier];
if (cell == nil) {
cell = (CustomCell *)[CustomCell cellFromNibNamed:cellIndentitier];
if(indexPath.row!=0) {
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 2.5)];
lineView.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"line.png"]];
[cell.contentView addSubview:lineView];
[lineView release];
}
}
NSDictionary *tweet;
NSDictionary *tweet= [twitterTableArray objectAtIndex:indexPath.row];
cell.twitterTextLabel.text = [tweet objectForKey:#"text"];
cell.customSubLabel.text = [NSString stringWithFormat:#"%d",indexpath.row];
}
I am using FontLabel in the view cells of the table.
- (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];
}
FontLabel *author_name = [[FontLabel alloc] initWithFrame:CGRectMake(58, 10, 217, 16) fontName:#"MyriadPro-Bold" pointSize:12.0f];
author_name.numberOfLines = 1;
author_name.text = [dummyArray objectAtIndex:indexPath.row];
author_name.textColor = [UIColor colorWithRed:0.580 green:0.776 blue:0.329 alpha:1.000];
author_name.backgroundColor = [UIColor clearColor];
[cell addSubview:author_name];
return cell;
}
But the label is loaded multiple times. I can see that it is getting bolder and bolder. How can I fix it?
When you call addSubview, it's not removing the old view. remember that cells are reused so the view you added in the last iteration is still there.
You should instead subclass UITableViewCell so that you can add a UILabel to it.
Edit:
Or you can do
author_name.tag = 10;
and then
FontLabel *author_name = (FontLabel *)[cell.contentView viewWithTag:10];
when you want to retrieve it.