I am trying to scroll my UITableViewCell up one UITableViewCell position when the selected cell reaches the last position in the UITableView
I have the logic correct for identifying the last visible UItableViewCell of the UITableView. However the code to make the UITableView scroll up one position is not working. This is the code I have written.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
cellSelectedBool = YES;
cell = (CustomFinishingCell *)[tableView cellForRowAtIndexPath:indexPath]; // error
[cell.widthTexField becomeFirstResponder];
// Gets an array of current visible cells in UITableView
NSArray *visibleCells = [tableView visibleCells];
NSLog(#"%#", visibleCells);
// get indexpath of last cell
UITableViewCell *lastCell = [visibleCells lastObject];
NSIndexPath *lastCellIndex = [finishingTableView indexPathForCell:lastCell];
// perform scroll when last visible cell is selected
if (indexPath.row == lastCellIndex.row) {
NSLog(#"BIGGER");
int cellHeight = 44;
[UIView animateWithDuration:.25 animations:^{
[finishingTableView setContentOffset:CGPointMake(finishingTableView.contentOffset.x,finishingTableView.contentOffset.y + cellHeight) animated:YES];
}];
}
you should use:
NSIndexPath *rowToSelect; // assume this exists and is set properly
UITableView *myTableView; // assume this exists
[myTableView selectRowAtIndexPath:rowToSelect animated:YES scrollPosition:UITableViewScrollPositionNone];
[myTableView scrollToRowAtIndexPath:rowToSelect atScrollPosition:UITableViewScrollPositionNone animated:YES];
if (indexPath.row == lastCellIndex.row) {
[finishingTableView scrollToRowAtIndexPath:indexPathatScrollPosition:
UITableViewScrollPositionNone animated:YES];
}
Related
I’m building an iOS app using storyboards.I have used UITableViewController which has 6 custom cells each of which contains three IBOutlet buttons and one IBOutlet label.
When the user clicks on any button in one particular custom cell, then the value of only that particular cell label should change.
But what happens is, values of all labels in each custom cell get changed.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
cell1.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
long row = [indexPath row];
cell1.First.tag=row;//button iboutlet in cell
cell1.Second.tag=row;//button iboutlet in cell
cell1.Third.tag=row;//button iboutlet in cell
cell1.Level.tag=row;
cell1.Level.text=Skill;//label iboutlet in cell
return cell1;
}
-(IBAction)FirstAction:(id)sender{
Skill=#"first";
[self.tableView reloadData];
}
-(IBAction)SecondAction:(id)sender{
Skill=#"Second";
[self.tableView reloadData];
}
-(IBAction)ThirdAction:(id)sender{
Skill=#"Third";
[self.tableView reloadData];
}
A couple of issues:
You should have a model that reflects what's in your table view. Specifically, right now you have a single Skill value, but you have six rows. You want to maintain a model that is an array of values, something like:
#property (nonatomic, strong) NSMutableArray *values;
And
- (void)viewDidLoad {
[super viewDidLoad];
self.values = [#[#"first", #"first", #"first", #"first", #"first", #"first"] mutableCopy];
}
And
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
NSString *value = self.values[indexPath.row];
cell.level.text = value;
return cell;
}
Note, no tag numbers needed.
When you tap on a button you must (a) identify what row in the table that corresponds to; (b) update that row in your model; and (c) reload that single row in the table (which will look up the value in the model):
- (void)updateCell:(UIView *)cell withValue:(NSString *)value {
NSParameterAssert(cell);
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell *)cell];
if (cell) {
self.values[indexPath.row] = value;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (IBAction)didTapFirstButton:(id)sender {
[self updateCell:[[sender superview] superview] withValue:#"first"];
}
- (IBAction)didTapSecondButton:(id)sender{
[self updateCell:[[sender superview] superview] withValue:#"second"];
}
- (IBAction)didTapThirdButton:(id)sender{
[self updateCell:[[sender superview] superview] withValue:#"third"];
}
Note, the sender is the button. So I get the table view cell by grabbing the superview (which is the cell's content view) and then grab its superview (the cell itself). If your view hierarchy is different, then change that as appropriate, but hopefully this illustrates the idea.
First you have to do is that use delegate method selectRowAtIndexPath when the row is selected then the indexPath selected row buttons can be used...
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
}
when the row is selected then that desired rows button functions will be performed
Try below code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Cellidentifier1 = #"List";
Cell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
cell1.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
long row = [indexPath row];
cell1.First.tag=row;//button iboutlet in cell
cell1.Second.tag=row;//button iboutlet in cell
cell1.Third.tag=row;//button iboutlet in cell
cell1.Level.tag=row;
cell1.Level.text=Skill;//label iboutlet in cell
[cell1.First addTarget:self action:#selector(FirstAction:) forControlEvents:UIControlEventTouchUpInside];
[cell1.Second addTarget:self action:#selector(SecondAction:) forControlEvents:UIControlEventTouchUpInside];
[cell1.Third addTarget:self action:#selector(ThirdAction:) forControlEvents:UIControlEventTouchUpInside];
return cell1;
}
In your IBOutlet methods add this to find the indexPath for the row
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
instead of reloading the entire tableview you only want to reload the text for that cell at that specified indexPath
To reload only one cell, do this instead of [self.tableView reloadData];
[self.tableView reloadRowsAtIndexPaths:#[hitIndex] withRowAnimation:UITableViewRowAnimationNone];
There's a few ways to do it. But, from looking at your code I would make sure you get the right indexPath from the cell you selected. Assuming you have the target action already set up, you could rewrite your IBAction method like so:
-(IBAction)firstAction:(UIButton *)sender{
int row = sender.tag;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
Skill = #"first";
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
this is going to get the right cell and index path. Update that cell's label only and then reload it.
I have a UITableView as a subview of a View. In the ViewController when the table is being populated I'm highlighting one of the rows and keeping a record of the indexPath for that row. I'm doing this in the cellforRowAtIndexPath method.
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
favouriteCellView *cell = [tableView dequeueReusableCellWithIdentifier:#"reuseID"];
QBCOCustomObject *favouriteObject = [_favouriteResults objectAtIndex:indexPath.row];
if (favouriteObject.userID == self.user.ID) {
UIView *bgColor = [[UIView alloc] init];
bgColor.backgroundColor = [UIColor colorWithRed:173.0f/255.0f green:146.0f/255.0f blue:237.0f/255.0f alpha:.5];
self.highlightedRowIndex = indexPath;
[cell setSelectedBackgroundView:bgColor];
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
return cell;
}
Then in the viewDidAppear Method I want the table to scroll to the highlighted cell.
-(void)viewDidAppear:(BOOL)animated{
[self.tableView scrollToRowAtIndexPath:self.highlightedRowIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
However I've double checked that the method is being hit with a breakpoint, but unfortunately the highlighted row is not being scrolled to the top of the table as I'd expected. AM I misunderstanding the scrollToRowAtIndexPath method? Or have I left something out of the above code.
If the row is not on screen, it will not yet be loaded by the table view. This means your cellForRowAtIndexPath for that cell will not yet be called. You'll want to choose this index in a way that does not depend on the view loading. Try this before you call scrollToRowAtIndexPath:
NSInteger row = 0;
for (QBCOCustomObject *object in _favouriteResults) {
if (object.userID == self.user.ID) break;
row++;
}
self.highlightedRowIndex = [NSIndexPath indexPathForRow:row inSection:0];
I know there is a method called [tableView cellForRowAtIndexPath:indexPath]; which we can use to get cell at given indexPath.But i want to expand row when the row tapped.using this code-
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Deselect cell
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
// Toggle 'selected' state
BOOL isSelected = ![self cellIsSelected:indexPath];
// Store cell 'selected' state keyed on indexPath
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
int sec = indexPath.section;
int row = indexPath.row;
NSString* indexpathStr = [NSString stringWithFormat:#"%d-%d", sec, row];
[selectedIndexes setObject:selectedIndex forKey:indexpathStr];
// This is where magic happens...
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 0.0;
if([self cellIsSelected:indexPath]) {
//problem here
CalEventCell* cell = (CalEventCell*)[tableView cellForRowAtIndexPath:indexPath];
[self setDescriptionFrame:cell.descriptionLbl];
height = cell.descriptionLbl.frame.size.height+cell.eventLbl.frame.size.height +20;
}
else
height = 60;
return height;
}
when a row tapped i call beginUpdates and endUpdates methods on tableview and change row height in heightForAtIndexPath method.The problem is that as you can see i call cellForAtIndexPath from heightForAtIndexPath and in cellForRow there is beginUpdates method call which turn into a call to heightForRow and this loop never ends and program crash with EXC-BAD-EXCEP.
So any other method to get cell from indexPath or any other way to do so? Any help would be appreciated.
Take an array to store the selected indices and then reload that tableview.
If you tap that cell height will expand and again if you will tap it will decrease its height.
Check out the below code.
// Add a mutable array to your class
NSMutableArray *selectedCellArray;
// Initialise it in `viewDidLoad`
-(void)viewDidLoad
{
[super viewDidLoad];
selectedCellArray = [[NSMutableArray alloc] init];
}
// Add selected indices in selectedCellArray
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Deselect cell
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
[tableView reloadData];
if (![selectedCellArray containsObject:indexPath])
[selectedCellArray addObject:indexPath];
else
[selectedCellArray removeObject:indexPath];
[tableView beginUpdates]; // Animate the height change
[tableView endUpdates];
}
// Write the UI Updation in HeightForRowAtIndexPath Method
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 0.0;
if (selectedCellArray.count>0) {
if ([selectedCellArray containsObject:indexPath]) {
CalEventCell* cell = (CalEventCell*)[tableView cellForRowAtIndexPath:indexPath];
[self setDescriptionFrame:cell.descriptionLbl];
height = cell.descriptionLbl.frame.size.height+cell.eventLbl.frame.size.height+20;
return height;
}else{
height = 60.f;
return height;
}
}
return height;
}
Hope This Helps
You should call below lines of code on your didSelectRowAtIndexPath method
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:row inSection:sec]] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
When row will be reloaded cellForRowAtIndexPath:indexPath and heightForRowAtIndexPath methods will be called where u can expand cell's height
You can't use cellForRowAtIndexPath in heightForRowAtIndexPath if you are using reusable cells. You need to calculate the height in some other way. The simplest way that would likely work for you would be to keep a prototype cell in your class that you only use for sizing purposes.
Try this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[your_tableView reloadData];
}
I have more than 20 cells in my custom table view, in execution time 6 cells will be visible. Now i select the 4 th cell means, that 4th cell have to come in first position and 5th cell in 2nd position and so on. how to do this process, i tried like this.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MACalendarCustomCell *cell = (MACalendarCustomCell*) [tableView dequeueReusableCellWithIdentifier:[MACalendarCustomCell reuseIdentifier]];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"MACalendarCustomCell" owner:self options:nil];
cell = customCell;
customCell = nil;
}
cell.lbl_CalendarTitle.text = [arr_CalendarTitle objectAtIndex:indexPath.row];
cell.lbl_CalendarSubTitle.text = [arr_CalendarSubTitle objectAtIndex:indexPath.row];
cell.lbl_calendarEventTime.text = [arr_CalendarEventTime objectAtIndex:indexPath.row];
cell.lbl_calendarDate.text = [arr_CalendarDate objectAtIndex:indexPath.row];
cell.lbl_CalendarMonth.text = [arr_CalendarMonth objectAtIndex:indexPath.row];
cell.lbl_CalendarYear.text = [arr_CalendarYear objectAtIndex:indexPath.row];
cell.img_BackGround.image = [UIImage imageNamed:#"calendar_Cell_Up.png"];
//here click event is occurred.
cell.btn_CollapseExpand.tag = indexPath.row;
[cell.btn_CollapseExpand addTarget:self action:#selector(method_Expand:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
ButtonPressed event calls
- (void)method_Expand:(UIButton*)sender
{
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tbl_CalendarList indexPathForCell:cell];
[tbl_CalendarList moveRowAtIndexPath:[NSIndexPath indexPathForItem:indexpath.row inSection:indexpath.section] toIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexpath.section]];
int_SelectedIndex = sender.tag;
NSLog(#"Selected Button : %ld",(long)int_SelectedIndex);
if ( int_TempSelectedIndex != int_SelectedIndex)
{
int_TempSelectedIndex = int_SelectedIndex;
}
else
{
int_TempSelectedIndex = -1;
}
[tbl_CalendarList reloadData];
}
Resizing the cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == int_TempSelectedIndex )
{
cellSize = 300;
isRowSelected[indexPath.row] = YES;
}
else
{
cellSize = 100;
isRowSelected[indexPath.row] = NO;
}
return cellSize;
}
Now i got Like this in simulator.
when i pressed it comes like this.
This selected cell should come to first position.
You can scroll your table view to that cell and you can specify that you want to scroll it on top when you select the cell:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
Hope this is what you are after.
In the method_Expand method after fetching the selected row you have to remove the object at the selected index and insert the removed object at 0 index.
Also you want to move the move next item to the second position
For that you have to increment the selected index and check if that index is with in the array bounds then remove that item and add it to the index 1;
- (void)method_Expand:(UIButton*)sender
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tbl_CalendarList indexPathForCell:cell];
int nextIndex=indexpath.row+1;
// first remove the object
NSString *str=[arr_CalendarTitle objectAtIndex];
[arr_CalendarTitle removeObjectAtIndex:indexpath.row];
[arr_CalendarTitle insertObject:str atIndex:0];
//do the same to arr_CalendarEventTime,arr_CalendarDate,arr_CalendarMont etc
if([arr_CalendarTitle count]-1<nextIndex)// check if nextIndex within bounds to avoid crash
{
// remove next object and addit to the index 1 to all array
}
[tbl_CalendarList reloadData];
}
As i wrote in the comments:
Upon selection, move selected arr_CalendarTitle entry to the top of array and call reloadData() on tableView. Table view displays data as is sorted in arr_CalendarTitle.
moveRowAtIndexPath is not enough, must resort the array too.
So, before reloadData() call (in button click method), do this:
id object = [[[arr_CalendarTitle objectAtIndex:int_SelectedIndex] retain] autorelease];
[arr_CalendarTitle removeObjectAtIndex:int_SelectedIndex];
[arr_CalendarTitle insertObject:object atIndex:0];
For ARC you can use :
__autoreleasing id object = [arr_CalendarTitle objectAtIndex:int_SelectedIndex];
[arr_CalendarTitle removeObjectAtIndex:int_SelectedIndex];
[arr_CalendarTitle insertObject:object atIndex:0];
Since you have more than one array that holds data (only noticed that now) you must do this for every array thats holds data for tableView cells.
Use below method to scroll your tableview.
[tableview setContentOffset:CGPointMake(0, button.tag*tableview.rowHeight) animated:YES];
This method will make tableview scroll to specified point.
Change value of Y in CGPointMake according to your code.
I have a horizontal scrolling table view. Each cell takes the whole screen to show full pictures. I'm doing lazy loading of the pictures. The problem I'm having is that when the pictures finish downloading, the UIImageview.image from the cell is not updated automatically. I need to scroll to another cell and back for it to show.
Here is the method called when the picture is finished downloading:
- (void)imageDownloaderDidFinish:(PictureDownloadOperation *)downloader {
NSLog(#"image download finished");
NSIndexPath *indexPath = downloader.indexPathInTableView;
PictureRecord *theRecord = downloader.photoRecord;
[self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.pendingOperations.downloadsInProgress removeObjectForKey:indexPath];
}
This is the cellforwoeatindexpath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"HorizontalCell";
HorizontalTableCell *cell = [self.myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[HorizontalTableCell alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width)];
}
cell.contentView.transform = CGAffineTransformMakeRotation(-M_PI/-2);
PictureRecord *aRecord = [self.photos objectAtIndex:indexPath.section];
[cell.indicator startAnimating];
if (aRecord.hasImage) {
[cell.indicator stopAnimating];
[cell.pic setImage:aRecord.image];
[cell setNeedsDisplay];
cell.username.text = aRecord.username;
}
else if (aRecord.isFailed) {
[cell.indicator stopAnimating];
cell.pic.image = [UIImage imageNamed:#"first.png"];
}
else {
[cell.indicator startAnimating];
if (!myTableView.dragging && !myTableView.decelerating) {
[self startOperationsForPhotoRecord:aRecord atIndexPath:indexPath];
}
}
return cell;
}
Thanks
UPDATE:
One more information about this problem. After troubleshooting I found that the problem is with reloadRowsAtIndexPaths. It does not call cellForRowAtIndexPath for the image to load. However I'm still not sure it does not call cellForRowAtIndexPath. I have also tried [tableview reloadData] and no luck.
FIXED IT:
The problem was fixed by adding another tableview inside this existing table view. The way I had before was a UITableView inside a UIView. I modified it so I had one UITableView with one cell. Created a custom cell and added another table in that cell so I could scroll it horizontally and added another custom cell for the horizontal tableview with the imageview. That did the trick.