-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//case 1
//The user is selecting the cell which is currently expanded
//we want to minimize it back
if(selectedIndex == indexPath.row)
{
selectedIndex = -1;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
return;
}
//case 2
//First we check if a cell is already expanded.
//If it is we want to minimize make sure it is reloaded to minimize it back
if(selectedIndex >= 0)
{
NSIndexPath *previousPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
selectedIndex = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:previousPath] withRowAnimation:UITableViewRowAnimationFade];
}
//case 3
//Finally set the selected index to the new selection and reload it to expand
selectedIndex = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
Notice how case 1 and case 2 are related collpasing already expanded row where as case 3 is about expanding the unexpanded row.
Both the expand and collapse use the same function of reloadRowsAtIndexPaths function.
The question for me is that a toggle button, when it's expanded, run that function again will collapse and when it's collapsed, it will expand??
What will happen is that when you call reloadRowsAtIndexPaths: the table view will reload those rows by calling your implementation of tableView:cellForRowAtIndexPath: in your UITableViewDataSource. It is up to you to return a cell there which uses the selectedIndex variable to decide if it should appear expanded or collapsed (whatever that means for your specific application). It will also call tableView:heightForRowAtIndexPath: on your UITableViewDelegate (yes, it is silly that this is in the delegate) so if your cell height changes this method too should return a value depending on selectedIndex.
Also, I would suggest you only call reloadRowsAtIndexPaths: once, like this:
NSMutableArray* rows = [NSMutableArray arrayWithCapacity:2];
// Case 2
if(selectedIndex >= 0)
{
NSIndexPath* previousPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
[rows addObject:previousPath];
}
// Case 3
selectedIndex = indexPath.row;
[rows addObject:indexPath];
[tableView reloadRowsAtIndexPaths:rows withRowAnimation:UITableViewRowAnimationFade];
Related
I have an old iOS app written in objective c. It has several UILabels that has given fixed x,y,width and height. Now what I want to do is when click on the acessoryView expand the cell and expand the UILabels inside it. Also I want to change the positions of those UILabels. So far I have done cell expanding part in this way.
- (void)expandCell :(UIButton *)sender {
NSLog(#"NSINdex Path-------%li",(long)sender.tag);
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
MetadataTableViewCell *cell = [_metadataListTVCTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:sender.tag inSection:0]];
if (newIndexPath == cellExpandedRow) {
cell.isExpanded = true;
}
else {
cell.isExpanded = false;
}
cellExpandedRow = newIndexPath;
[_metadataListTVCTableView beginUpdates];
[_metadataListTVCTableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForItem: sender.tag inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[_metadataListTVCTableView endUpdates];
[_metadataListTVCTableView reloadData];
}
But I have no idea where I can do inner label frame updatings. Please help me. Thanks
I need to add a new row under the selected row. I'm using this code in didSelectRowAtIndexPath:
[_dataSource insertObject:newDataObject atIndex:indexPath.row + 1];
NSMutableArray *indexPaths = [NSMutableArray array];
NSInteger currentCount = indexPath.row;
[indexPaths addObject:[NSIndexPath indexPathForRow:currentCount+1 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
It adds the new row in the correct position, but it also deletes the upper cell separator of the selected row. What am I doing wrong?
Seems like UITableViewCell has no separator in selected state. UITableViewCellSelectionStyleNone just disables highlighting, but cell is selected anyway. adding
[tableView beginUpdates];
// insert/delete manipulations
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[tableView endUpdates];
fixes the problem
I am a little bit confused
- (IBAction)addEmailField:(id)sender {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
UITableViewCell *Cell = [self.tableView cellForRowAtIndexPath:indexPath];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
}
this wont even compile, I read the documentation I know I have to pass it an NSarray. but I dont understand how to add the cell into that array
There are 2 possible scenarios to solve your problem:
1. In case when you have a static UITableView, i.e. all cells set up in XIB or Storyboard, you can't insert or delete cells because your whole table view is configured via Interface Builder and thus can't be modified.
A way around this is to configure all cells (including those that need to be shown later) in Interface Builder and then setup their height in runtime:
// This will show or hide the first row in first section depending on the value of
// BOOL _firstRowVisible instance variable that you set elsewhere in your code.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == 0)
return _firstRowVisible ? 44 : 0;
return 44;
}
// To show or hide the first row in first section.
// Note that you need to call both -reloadRowsAtIndexPaths:withRowAnimation:
// and -reloadData to make this work.
- (IBAction)showOrHideEmailField:(id)sender
{
_firstRowVisible = !_firstRowVisible;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadData];
}
2. In case you have dynamic cells, you can setup their appearance in Interface Builder, setting each cell type its own Reuse Identifier. Then, in code, in -tableView:cellForRowAtIndexPath: you specify what kind of cell you are going to use by passing the corresponding cell identifier to UITableView's -dequeueReusableCellWithIdentifier:forIndexPath: and set up your cell appropriately.
To make use of -insertRowsAtIndexPaths:withRowAnimation: you need to update your data source first, to keep your view in sync with your model.
So for your case the following code might work:
#property (assign, nonatomic) NSInteger numberOfEmailFields;
#pragma mark - Table View Data Source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (section) {
case 0:
return 6;
case 1:
return self.numberOfEmailFields + 1;
default:
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section) {
case 0:
{
switch (indexPath.row) {
case 0:
{
// Dequeue, set up and return a cell with corresponding reuse identifier
static NSString *CellIdentifier = #"MyCell";
return [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
}
...
default:
break;
}
break;
}
default:
break;
}
return nil;
}
- (IBAction)addEmailField:(id)sender
{
// Update data source
self.numberOfEmailFields++;
// Animate rows
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
}
I created a sample project for your scenario using the dynamic cells approach.
Note that to be able to add cells to a table view you need to use the second scenario, that is, use dynamic cells. This might seem much more work at first, but this approach is more expandable and easier to modify in future.
- (IBAction)addEmailField:(id)sender {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; // lowercase ivar name
[self.tableView insertRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationRight]; // use #[] for array literals
}
Don't need to specify begin update/end update, should only be used when there a series of actions on a table like inserts, moves, delete etc..
As the picture shows. I make a tableview which can splite by inserting one row (the gray one) when I click on the cell, click again to cancel the splite:
The code is as below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int clickCellRow = [indexPath row] + 1;
int clickCellSection = [indexPath section];
[self beginUpdates];
if (isEditFlag == NO) {
isEditFlag = YES;
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:clickCellRow inSection:clickCellSection]] withRowAnimation:UITableViewRowAnimationFade];
}
else {
isEditFlag = NO;
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:clickCellRow inSection:clickCellSection]] withRowAnimation:UITableViewRowAnimationFade];
}
[self endUpdates];
}
It works well on IOS6, but on IOS7 one bug happened. When one cell is clicked and the splited row is inserted, then the sectionView is forced to go outside of the screen:
After cancelling the splite, and scroll the hidden cell up. The table turn to be as below :
It seems that the sectionView has been loaded on the cell! How does this happened and how to fix it?
Currently I have a table view that if a cell is selected, the table is reloaded with a cell at the selected index path using the animation UITableTableViewRowAnimationFade. I don't like it very much as it is choppy and not smooth. I am not at all familiar with UIView animations and am looking to learn more about them. How would I animate this operation to be smoother and look nicer? Thanks.
Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (selectedCellIndexPath == indexPath.row) {
selectedCellIndexPath = -1;
[callsTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
return;
}
if (selectedCellIndexPath >= 0)
{
NSIndexPath *previousPath = [NSIndexPath indexPathForRow:selectedCellIndexPath inSection:0];
selectedCellIndexPath = indexPath.row;
[callsTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:previousPath] withRowAnimation:UITableViewRowAnimationFade];
}
selectedCellIndexPath = indexPath.row;
[callsTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}