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?
Related
I'm making a UITableView with expandable/collapsible cells for an iOS app. In that app I want to display an arrow down image and when click on it the image will be an up arrow. Is this possible?
Here is my code
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedIndex == indexPath.row)
{
selectedIndex = -1;
[myContestTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
return;
}
if (selectedIndex != -1)
{
NSIndexPath *prevPath = [NSIndexPath indexPathForRow:selectedIndex inSection:0];
selectedIndex = indexPath.row;
[myContestTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevPath] withRowAnimation:UITableViewRowAnimationFade];
}
selectedIndex = indexPath.row;
[myContestTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
This is my image named "rotateImage". Please help me.
Here is my expandable cell default image:
When the user click on a cell the image will be an up arrow and if the user click again on cell default image should display again.
If you're subclassing your UITableViewCell you can use: setSelected: animated:, add your configuration there.
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
YOURIMAGEVIEW.transform = CGAffineTransformMakeRotation(selected ? M_PI : 0);
// Configure the view for the selected state
}
if not, you simply use that inside -cellForRowAtIndexPath like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
YOURTABLECELL.yourImageView.tranform = CGAffineTransformMakeRotation((selectedIndex == indexPath.row) ? M_PI : 0);
...
return tableCell;
}
I would like to create cell by expand and collapse. To do this i animate the cell by reloading. When i expand it works fine. But when i collapse it crashes and the reason is Attempt to create two animations for cell. I know Ive given same array paths and it wont reload two cell at the time. Is there a way to fix this.
I am new to coding, so i would be happy to get a simple solution.
Code:
The reason am using lastSelIPath is when first cell is expanded and touch the second cell the first cell would collapse and the second cell would expand.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath* lastSelIPath = [ NSIndexPath indexPathForRow:selectedIndex_ inSection:0 ];
[ tableView deselectRowAtIndexPath:indexPath animated:YES ];
if (selectedIndex_ == indexPath.row)
{
selectedIndex_ = -1;
}
else
{
selectedIndex_ = indexPath.row;
}
NSIndexPath* ipath = [ NSIndexPath indexPathForRow:indexPath.row inSection:0 ];
NSArray* indexPathArr = [ NSArray arrayWithObjects:lastSelIPath, ipath, nil ];
[ tableView beginUpdates ];
[ tableView reloadRowsAtIndexPaths:indexPathArr withRowAnimation:UITableViewRowAnimationAutomatic ];
[ tableView endUpdates ];
}
So what i did now I set a condition and reload the one i wanted. Is it a correct method to follow please help.
Modified code:
if ( lastSelIPath.row == ipath.row )
{
[tableView reloadRowsAtIndexPaths:#[ipath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else
{
[tableView reloadRowsAtIndexPaths:#[lastSelIPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:#[ipath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
Crash is right, you are trying to attempt two animation. Your indexPathArr contains two objects. Do not create this array and try this:
[tableView reloadRowsAtIndexPaths:#[lastSelIPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView reloadRowsAtIndexPaths:#[ipath] withRowAnimation:UITableViewRowAnimationAutomatic];
Let me know, if it helps
[tableView reloadRowsAtIndexPaths:#[newIndexPath, oldIndexPath] withRowAnimation:UITableViewRowAnimationNone];
If newIndexPath is same as oldIndexPath, it will crash in iOS8, but this is fixed in iOS9 and later.
You can use third party library for expanding cell.
https://github.com/bennyguitar/CollapseClick
& implement there delegate method as
-(int)numberOfCellsForCollapseClick
{
}
-(NSString *)titleForCollapseClickAtIndex:(int)index {
}
-(UIView *)viewForCollapseClickContentViewAtIndex:(int)index {
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (selectedTag==1) {
selectedIndex=indexPath.row;
[self performSelector:#selector(Write Your Action) withObject:nil];
}
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandedIndexPath]) {
[modifiedRows addObject:self.expandedIndexPath];
self.expandedIndexPath = nil;
}
else {
if (self.expandedIndexPath)
[modifiedRows addObject:self.expandedIndexPath];
self.expandedIndexPath = indexPath;
[modifiedRows addObject:indexPath];
}
// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Using Selected Tag write the condition of your cell expand action. may be it helps.
This behavior is iOS 7.1 only, on 7.0, it works as intended.
I've got a UITableView with cells of different height. When tapping on one of them, it expands via
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row != self.selectedCellIndexPath.row) {
// Close previously opened cell
if (self.selectedCellIndexPath != nil) {
[tableView beginUpdates];
[(MyTableViewCell *) [tableView cellForRowAtIndexPath:self.selectedCellIndexPath] collapse];
[tableView endUpdates];
}
self.selectedCellIndexPath = indexPath;
[tableView beginUpdates];
[(MyTableViewCell *) [tableView cellForRowAtIndexPath:indexPath] expand];
[tableView endUpdates];
}
else if (indexPath.row == self.selectedCellIndexPath.row) {
self.selectedCellIndexPath = nil;
[tableView beginUpdates];
[(MyTableViewCell *) [tableView cellForRowAtIndexPath:indexPath] collapse];
[tableView endUpdates];
}
}
When tapping on a row at the top of the tableView, everything behaves as intended - the cell is expanded downwards, the top of the cell stays where it is. But when scrolling down and tapping on a cell more at the bottom of the tableview, the cell moves downwards and expands. The further down the cell is, the worse it is.
I'm calculating the table cells heights this way:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
self.prototypeCell = [[MyTableCell alloc] initReuseIdentifier:CellIdentifier];
[self configureCell:self.prototypeCell forIndexPath:indexPath];
float height = 38;
if ([indexPath isEqual:self.selectedCellIndexPath]) {
height += [self.prototypeCell expandedHeight] + 5;
}
return height;
}
I'm irritated that the effect is different if I tap on a cell at the top or at the bottom of the tableView. Furthermore, this effect is only the first time after scrolling down. When repeating, the cell stays at its position, as intended. Only after scrolling up and down again, it behaves wrong the first time.
I solved the problem; it was due to estimatedHeightForRowAtIndexPath, I removed the method and now the expanding/collapsing works as expected.
I have a tableView with some sections, which all have a footer, and then I have a tableViewFooter on the Tableview itself.
If I scroll down to the bottom of my tableview and delete the last item(therefore deleting the section altogether) in any sections above the last section (second last and up) it gives me this error
2014-02-21 13:19:55.066 xxxx[5436:60b] *** Assertion failure in -[UIViewAnimation initWithView:indexPath:endRect:endAlpha:startFraction:endFraction:curve:animateFromCurrentPosition:shouldDeleteAfterAnimation:editing:], /SourceCache/UIKit/UIKit-2903.23/UITableViewSupport.m:2661
Uncaught exception: Cell animation stop fraction must be greater than start fraction
at endUpdates
this is my code
[self.tableView beginUpdates];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
if(indexPath != nil){
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
NSMutableDictionary *dic =[sec.items objectAtIndex:indexPath.row];
Product* product = [dic valueForKey:PRODUCT];
//removing the item in the section
[sec.items removeObject:dic];
//deleting item from products
NSMutableArray *temp = [NSMutableArray array];
for (Product *p in self.dataCon.listPersister.products) {
if ([p.product.objId isEqualToString: product.product.objId]) {
[temp addObject:p];
}
}
for (Product *p in temp) {
[self.dataCon.listPersister.products removeObject:p];
}
//if it was the last object in section, delete the section else just delete the single row
if(sec.items.count == 0)
{
[self.sections removeObject:sec];
[self.footers removeObjectAtIndex:indexPath.section];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
USFooterView *footer = [self.footers objectAtIndex:indexPath.section];
footer.totalLabel.text = [self.dataCon.listPersister getTotalForShopId:sec.title];
self.footerView.totalLabel.text = [self.dataCon.listPersister getTotalForAllProducts];
}
}
[self.tableView endUpdates];
I had the same code earlier, just without my tableView and table sections having footers, where it worked, so I think they might be the problem, but I'm not entirely sure that's the reason it's acting up.
I have seen this post
UITableView tableFooterView may cause crash?
And the post that it links to, but that didn't help me.
Any help is appreciated :)
In the else statement you delete row from table view:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
But not from data source. Delete row from array which you use as data source and it should works.
I found a "fix", but I'm avoiding the use of sectionFooter, because that seems to be bugged.
I created an extra cell at the end of each section, with the same setup I had for my footer View before, and made that last cell not editable with
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
if (sec.items.count != indexPath.row) {
return YES;
} else
return NO;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [sec.items count] +1 ;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"normalcell";
static NSString *CellIdentifier1 = #"footercell";
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
if (indexPath.row != sec.items.count) {
//use normal type of cell
return cell;
} else{
//use footer type of cell
return cell;
}
}
So the last cell Imitates a "footer", but it's not stuck to the bottom of the frame, but I'll have to live with that. It's better than crashes.
Try using UITableViewRowAnimationLeft or UITableViewRowAnimationRight as the delete row animation(deleteRowsAtIndexPaths:withRowAnimation:).
It crashed for me when using UITableViewRowAnimationAutomatic, but not with the other two. I have not tried all of them but it seems to be a bug with the animation code for some of the options.
-(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];