iOS: UITableView clarifications - iphone

How can I eliminate views created upon selecting a cell in UITableView when the cell is no longer selected or other cell is on selection.
Suppose I have the code below for didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CGSize cellSize = [tableView cellForRowAtIndexPath:indexPath].frame.size;
UIView *selectionView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, (int)cellSize.width, (int)(cellSize.height + 100))];
selectionView.layer.borderWidth = 2;
[[tableView cellForRowAtIndexPath:indexPath]addSubview:selectionView];
}
Now, I want to remove that created selectionView when I focus on another cell and create it again to the cell which I am focusing.. The problem is that when I select a cell for the first time, it works perfectly but when I select another cell, the selectionView created from the previous cell still does not disappear and it duplicates the view already. How am I suppose to solve this? Need suggestion.. :( thanks..

You need to add tag for the selectionView as follows
selectionView.tag = 100;
Also , you need to have the reference of the last selected indexPath by declaring a NSIndexPath class member and retaining it.
So while selecting a new cell, get the cell with last selected indexpath and remove the view from the cell as follows
UITableViewCell *lastCell = [tableView cellForRowAtIndexPath:lastSelIndexPath];
UIView *view = [lastCell viewWithTag:100];
[view removeFromSuperview];

You really need to look into subclassing UITableViewCell if you want a custom selection UI. Then override [UITableViewCell setSelected:animated:] (or [UITableViewCell setHighlighted:animated:]) to perform your customisations.

Related

UITableview Reusability issue when drawing a line on tableview cell and moving it to the end of the table

I have to make an application where I can add N number of rows in UITableview.This is how I am using reusability.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
UITableViewCell *Cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(Cell==nil)
{
Cell=[self tableViewCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath ofTableView:tableView];
}
[Cell setSelectionStyle:UITableViewCellEditingStyleNone];
[Cell setAccessoryType:UITableViewCellAccessoryNone];
[self ConfigureCell:Cell forIndexPath:indexPath forTableView:tableView];
return Cell;
}
Then on swipe right I have to draw a line and add button on a particular cell of UITableview that I am doing in swipe gesture method after retrieving the UITableviewCell in that method and have to move cell to the bottom after adding the subviews.
CGPoint location = [recognizer locationInView:GroupedTableView];
NSIndexPath *IndexPath = [GroupedTableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.GroupedTableView cellForRowAtIndexPath:IndexPath];
UIButton *CrossButton=[UIButton buttonWithType:UIButtonTypeRoundedRect];
CrossButton.frame=CGRectMake(250, 12, 15, 15);
[CrossButton setBackgroundImage:[UIImage imageNamed:#"x.png"] forState:UIControlStateNormal];
CrossButton.tag=800+IndexPath.row;
[CrossButton addTarget:self action:#selector(CrossButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:CrossButton];
UIImageView *LineImage=[[UIImageView alloc]init];
LineImage.frame=CGRectMake(10, 19, 220, 2);
LineImage.tag=700+IndexPath.row;
LineImage.image=[UIImage imageNamed:#"line.png"];
LineImage.userInteractionEnabled=YES;
[cell.contentView addSubview:LineImage];
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:RowNum inSection:0];
[self.GroupTableContentArray insertObject:[self.GroupTableContentArray objectAtIndex:IndexPath.row] atIndex:lastIndexPath.row+1];
[self.GroupTableContentArray removeObjectAtIndex:IndexPath.row];
[self.GroupedTableView moveRowAtIndexPath:IndexPath toIndexPath:lastIndexPath];
This also works fine till the table height is small but once it is bigger than the screen size and I scroll to see the bottom content,the line I have drawn on that particular cell vanishes and sometimes its visible on other cell which was not marked.I know this is the issue of reusability but I am unable to figure out a way.
My requirement is like any.Do app where we swipe right to select a finished task and put it at the bottom of the table.Any help would be highly appreciated.
It seems to me that you don't quite understand how table view cell reuse works.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Asks the table delegate for a cell that is going to be displayed at the specified index path. You ask the table view for a reusable cell with dequeueReusableCellWithIdentifier:. Now, if the returned cell is nil it is your job to create a new cell.
When setting cell properties like text, detail text and image, you have to treat each cell as if it has unwanted data, meaning that you should overwrite everything each time before returning a cell.
To give you an example of this, imagine you have a table view with 10 visible cells and a 100 rows. Your first cell has "Hello world" in it and the other ones are empty. Now, when you start scrolling you are going to be seeing "Hello world" every 10 or so cells. This is happening because a random available cell is being returned from the tables reusable cell queue which has kept all of its changes like text, images, and subviews.
And that is also what is happening to your cell with the line and the button, so to avoid this, appropriate properties should be set for each index path. The problem is that you are adding subviews to your cell with no reference to them so they cannot be removed or hidden easily and so are always visible at random index paths. Furthermore you're going to get in a situation where you have multiple lines and buttons in the same cell.
It would be best for you to create a UITableViewCell subclass where each cell has its own line and button which can be shown/hidden as necessary for each index path.

Where to use scrollToRowAtIndexPath in iphone?

I am using [self.messageList scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO]; in cellForRowAtIndexPath and it is working fine, when a row is added in table array then it auto scroll upside but the problem is that when I try to see the top rows then it automatically come again last rows. And I am not able to see my all rows. Please let me know where I use this or any other approach to do this.
cellForRowAtIndexPath method is not called for all cell that you have. It is only called for visible cell in your TableView. When You Scroll UITableView then it ask to UITableView that you want to use reusable cell or add to new one. generally we use reusable cell so, it is use 1st no of cell and add at end of cell . it is not create another new cell at the end. so, when you scroll your UITableView it not scroll to end of cell it indexPath is similar to indexPath of first no of cell. so you can not scroll your UITableView at to Down.
use following code may be helpful for you.... :)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"S%1dR%1d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
[self.tblView scrollToRowAtIndexPath:indexPath atScrollPosition: UITableViewScrollPositionTop animated: YES];
// your code
}
// your code
return cell;
}
If you write this code in your cell for row at index path, then it will get called every time you try to scroll manually coz this message is called to draw your cell and you will b directed to the row you specified. Try checking your indexpath in a IF--Else block and scroll only when a certain action happens.
cellForRowAtIndexPath is a method for creating a cell to view .When you scroll this method is called when a cell is viewed.So each time the method called the [self.messageList scrollToRowAtIndexPath:indexPath method(which is called inside cellForRow) is also called and the table scrolls down to that row!!!

Image at 0 index overtake

I've got this nasty problem. My class, which is subclassing UITableViewController, has a method which is invoking
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
The method which is invoking what is above is this one:
- (void) insertInfo:(UIImage *)image
{
self.hotelImage = nil;
self.hotelImage = image;
numRows = numRows + 1;
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
This is how I am creating a cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
self.datacell = nil;
self.datacell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"SimpleTableIdentifier"] autorelease];
self.myImageView = nil;
self.myImageView = [[UIImageView alloc] initWithImage:self.hotelImage];
[self.myImageView setFrame:CGRectMake(5, 5, 80, 75)];
[self.datacell.contentView addSubview:self.myImageView];
[self.myImageView release];
self.tableView.userInteractionEnabled = YES;
self.tableView.separatorColor = [UIColor clearColor];
return self.datacell;
}
So, my problem is that when I scroll the table, all images in the table get replaced by a single image at index 0. My guess is that it has something to do with the fact that when cell is created, every image in the section is considered at index 0, but different image is shown when table is created. But when the table is scrolled by user, different images get overtaken by an image the is in the index 0 of the first cell.
This is exactly what happens, the image on the first cell is shown on all cells when the table begins to scroll.
I just don't know how to make every cell retain its unique image when the table is scrolled. My guess is it has something to do with placing image indexPath.section??? But I am not sure. Could somebody please help me with this problem?
Thank you,
Victor.
Effectively you're editing the contents of your table with insertInfo, but not editing its actual source, so as soon as it needs to redraw those cells, they get reverted to its original source. Try using something along the lines of:
self.myImageView=[[UIImageView alloc] initWithImage:[myImageArray objectAtIndex:indexPath.row]];
The fact is that the tableView doesn't keep your cells around. As soon as you scroll them out of view they are gone, released, deallocated as far as you know, and the table view asks the data soucre for new replacement cells. Your cellForRowAtIndexPath is always returning the same cell, independent of what the table view asks for.
You should be storing the images sent to insertInfo into an array, then in cellForRowAtIndexPath, always return the image from the array by index, looked up by indexPath.row and indexPath.section. Every time you scroll, that cellForRowAtIndexPath is called to get all the new cells that weren't on screen, this is why the cells always "change to" the most recent image sent to insertInfo.
Try adding a new file to your project, subclass of UITableViewController, and check out the default code there. You'll find dequeueReusableCellWithIdentifier is also a useful one.
The key concept here is that cellForRowAtIndexPath: is a question, and your code needs to check the values of indexPath.section and indexPath.row, and return a value that belongs to that row/section.

iPhone app development : How can i get a table view in which whn i click on a cell a text view should expand just below that cell...?

How can i get a table view in which whn i click on a cell a text view should expand just below that cell it should look like clicking on a button an it will create a text view in between clicked button and its below button...
i m trying to create a pictorial view...
tabel view:
button 1 >
button 2 >
button 3 >
table view:
button 1 >
button 2 v
__________
.
.
. text view
.
----------
button 3 >
plz solve my problem i m new for iphone app development....
I dont think that this can be done the way u want it, because once the TableView is loaded it is loaded, and if even if you manage to push some extra cells in between, the Table View will not change until it is loaded again.
TableView is loaded in two scenarios i.e
1. When Loading the view
2. While scrolling within the TableView.
my advice is to show a popup window kind of thing...
- (UITableViewCell *)valueForSelectedRow {
UITableViewCell *cell = [self.AccountTable cellForRowAtIndexPath:[self.AccountTable indexPathForSelectedRow]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Get value
UITableViewCell *cell = [self valueForSelectedRow];
cell.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, cell.frame.size.height);
UITextview *txtview = [[UITextView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 25.0f)] autorelease];
[cell addSubview:txtview];
[cell sizeToFit];
//...
....
You might insert a row just under the selected one.
The steps are:
Create an instance of NSIndexPath for the new row
Update your datasource (insert a special object containing the information for your text view). Notice that the datasource object must be mutable (e.g. NSMutableArray).
Insert new row to the table
Sample implementation may look like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// 1
NSIndexPath *newRowIndexPath = [NSIndexPath indexPathForRow:(indexPath.row + 1) inSection:indexPath.section];
// 2 (datasource has to be mutable)
[self.datasource insertObject:[NSDictionary dictionaryWithObject:#"THE_TEXT_FOR_YOUR_TEXTVIEW"] atIndex:newRowIndexPath.row];
// 3
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newRowIndexPath] withRowAnimation:UITableViewRowAnimationBottom];
}
Notice that in addition to this code you will have to treat the "special" row differently in tableView:cellForRowAtIndexPath: method...
Cheers.
Michael.

What is a good way to animate UIViews inside UITableViewCell after a callback?

We couldn't find a way to animate UIViews inside a UITableCell as an action from a callback.
Suppose in a scenario where you click on a button on the UITableViewCell and it fires off an asynchronous action to download a picture. Suppose further that when the picture is downloaded we want a UIView in the cell to animate the picture to give user a visual feedback that something new is about to be presented.
We couldn't find a way to track down the UIVIew to invoke beginAnimation on because the original cell that the user clicked on might now be used for another row due to the nature of cells being reused when you scroll up and down in the table. In other words we can't keep a pointer to that UITableViewCell. We need to find another way to target the cell and animate it if that row is visible and don't animate if the row is scrolled out of range.
Keep the cell object different from the object being animated so the cell holds a UIView. When the animation callback occurs check to make sure that the UIView still exists and, if it does, animate the changes.
When the cell object gets bumped off the screen and recycled, release the UIView that would have been animated and create a new one. When the animation callback occurs it will have nothing to do because the UIView no longer exists.
A modification of the above is to keep some sort of object in the UIView that your callback can check to see if the animation is still appropriate. This could be some sort of unique identifier for the picture being downloaded. If the identifier changes, no animation is needed. If it matches, do the animation.
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
} else {
UIView *oldViewToAnimate = [cell.contentView viewWithTag:1];
[oldViewToAnimate removeFromSuperview];
}
UIView *viewToAnimate = [[UIView alloc] initWithFrame:CGRectZero]; //replace with appropriate frame
viewToAnimate.tag = 1;
[cell.contentView addSubview:viewToAnimate];
return cell;
}
When you spawn your download process you pass in [cell.contentView viewWithTag:1]. When the download is done, it will update the appropriate view. If the table cell was reused the view will no longer have a superview and will not update the wrong cell.
There are things you can do to make this more efficient but this is the basic idea. If you have a custom UITableViewCell than this will probably look a bit different.
EDIT 2:
To reuse the viewToAnimate objects to make sure that they get updated if their parent cells were recycled, do something like the following:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
} else {
UIView *oldViewToAnimate = [cell.contentView viewWithTag:1];
[oldViewToAnimate removeFromSuperview];
}
UIView *viewToAnimate = [self viewToAnimateForIndexPath:indexPath];
viewToAnimate.tag = 1;
[cell.contentView addSubview:viewToAnimate];
return cell;
}
viewToAnimateForIndexPath will need to:
Check to see if a viewToAnimate has been created for this indexPath
Create a viewToAnimate if there isn't one
Save a reference to the view that can be looked up by indexPath
Return the viewToAnimate so the table cell can use it
I don't know enough about your data structure to do this for you. Once the download process completes it can call this same method to get the view and animate it.