I'm having serious troubles with the animation of a UICollectionView, similar to the problem mentioned here: UICollectionView crashes when rearranging items between sections
Let's say I have a UICollectionView with 5 different sections, each with 10 cells.
I would like to update the CollectionView with an animation, so the cells will be reordered into 10 sections. No cells with new content will be added nor existing cells removed.
Therefore I'm performing a batch update:
[_resultCollectionView performBatchUpdates:^{
[_resultCollectionView insertSections:insertSections];
[_resultCollectionView deleteSections:deleteSections];
//[_resultCollectionView insertItemsAtIndexPaths:insertIndexPaths];
//[_resultCollectionView deleteItemsAtIndexPaths:deleteIndexPaths];
for (all items which need to be moved...) {
[_resultCollectionView moveItemAtIndexPath:sourcePath toIndexPath:destinationPath];
}
} completion:^(BOOL finished) {
//[_resultCollectionView reloadData];
nil;
}];
If I perform insertSections, deleteSections, insertItemsAtIndexPath, deleteItemsAtIndexPath and reloadData once the block was performed everything works fine which means my dataSource and delegates work properly in theory. Except it's not animated yet ...
If I perform insertSections, deleteSections and moveItemAtIndexPath (which should work since the cells only get rearranged) i get this little bug:
cannot move a row into a newly inserted section (5)
If I perform insertSections, deleteSections and moveItemAtIndexPath but exclude any movement into a newly inserted section I obviously get invalid number of items in the sections.
Does anybody have a solution for this problem?
I followed john1034's link and figured out a quite tricky way to do it. You have to split up the work in three steps:
Adding new sections if necessary. The new sections will be empty first.
Adding, removing and moving cells within the new and existing sections.
Deleting sections if necessary.
For sure there are many lines which can be further improved. Especially the search functions are quite inefficient...
static BOOL addingSections = NO;
static BOOL updatingCollectionView = NO;
static BOOL removingSections = NO;
- (void)reloadResultCollectionView
//IndexUnloadedArray: a link to my CollectionViewData before the update
//IndexArray: a link to my CollectionViewData after the update
//start only if something changed
if (![IndexArray isEqual:IndexUnloadedArray]) {
NSMutableIndexSet *deleteSections = [[NSMutableIndexSet alloc] init];
NSMutableIndexSet *insertSections = [[NSMutableIndexSet alloc] init];
NSMutableArray *deleteIndexPaths = [[NSMutableArray alloc] init];
NSMutableArray *insertIndexPaths = [[NSMutableArray alloc] init];
//step 1 - add collectionView sections
for (int i = 0; i < IndexArray.count; i++) {
if (i >= IndexUnloadedArray.count) {
[insertSections addIndex:i];
}
}
NSLog(#"insert sections:%#", insertSections);
_sectionAmount = (int)_distanceUnloadedArray.count + (int)insertSections.count;
addingSections = YES;
[_resultCollectionView performBatchUpdates:^{
[_resultCollectionView insertSections:insertSections];
} completion:^(BOOL finished) {
nil;
}];
addingSections = NO;
//step 2 - update collectionView
//adding cells if there are not enough
for (int i = 0; i < IndexArray.count; i++) {
for (int j = 0; j < (int)[[IndexArray objectAtIndex:i] count]; j++) {
NSNumber *searchIndex = [[IndexArray objectAtIndex:i] objectAtIndex:j];
bool found = NO;
for (int k = 0; k < IndexUnloadedArray.count; k++) {
if ([[IndexUnloadedArray objectAtIndex:k] containsObject:searchIndex]) {
found = YES;
k = (int)IndexUnloadedArray.count;
}
}
if (!found) {
[insertIndexPaths addObject:[NSIndexPath indexPathForRow:j inSection:i]];
}
}
}
NSLog(#"insert cells:%#", insertIndexPaths);
//deleting cells if there are too many
for (int i = 0; i < IndexUnloadedArray.count; i++) {
if (![deleteSections containsIndex:i]) {
for (int j = 0; j < (int)[[IndexUnloadedArray objectAtIndex:i] count]; j++) {
NSNumber *searchIndex = [[IndexUnloadedArray objectAtIndex:i] objectAtIndex:j];
bool found = NO;
for (int k = 0; k < IndexArray.count; k++) {
if ([[IndexArray objectAtIndex:k] containsObject:searchIndex]) {
found = YES;
k = (int)IndexArray.count;
}
}
if (!found) {
[deleteIndexPaths addObject:[NSIndexPath indexPathForRow:j inSection:i]];
}
}
}
}
NSLog(#"deleting cells:%#", deleteIndexPaths);
updatingCollectionView = YES;
[_resultCollectionView performBatchUpdates:^{
[_resultCollectionView insertItemsAtIndexPaths:insertIndexPaths];
[_resultCollectionView deleteItemsAtIndexPaths:deleteIndexPaths];
for (int i = 0; i < IndexUnloadedArray.count; i++) {
for (int j = 0; j < [[IndexUnloadedArray objectAtIndex:i] count]; j++) {
NSIndexPath *sourcePath = [NSIndexPath indexPathForRow:(j) inSection:i];
NSNumber *searchIndex = [[IndexUnloadedArray objectAtIndex:i] objectAtIndex:j];
NSIndexPath *destinationPath;
for (int k = 0; k < IndexArray.count; k++) {
if ([[IndexArray objectAtIndex:k] containsObject:searchIndex]) {
NSInteger *row = [[IndexArray objectAtIndex:k] indexOfObject:searchIndex];
destinationPath = [NSIndexPath indexPathForItem:row inSection:k];
if (sourcePath != destinationPath) {
NSLog(#"moving cell from %ld.%ld to %ld.%ld (%#)", (long)sourcePath.section, (long)sourcePath.row, (long)destinationPath.section, (long)destinationPath.row);
[_resultCollectionView moveItemAtIndexPath:sourcePath toIndexPath:destinationPath];
} else {
NSLog(#"object %ld.%ld stays in position", (long)sourcePath.section, (long)sourcePath.row);
}
}
}
}
}
} completion:^(BOOL finished) {
nil;
}];
updatingCollectionView = NO;
//step 3 - deleting sections if there are too many
for (int i = 0; i < IndexUnloadedArray.count; i++) {
if (i >= IndexArray.count) {
[deleteSections addIndex:i];
}
}
NSLog(#"delete sections:%#", deleteSections);
_sectionAmount = (int)_distanceArray.count;
removingSections = YES;
[_resultCollectionView performBatchUpdates:^{
[_resultCollectionView deleteSections:deleteSections];
} completion:^(BOOL finished) {
//update table header and footer
[_resultCollectionView reloadData];
}];
removingSections = NO;
}
}
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView
{
return _sectionAmount;
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
{
if (addingSections) {
if (section < _distanceUnloadedArray.count) {
return [[_distanceUnloadedArray objectAtIndex:section] count];
} else {
return 0;
}
}
if (updatingCollectionView) {
if (section < _distanceArray.count) {
return [[_distanceArray objectAtIndex:section] count];
} else {
return 0;
}
}
if (removingSections) {
return [[_distanceArray objectAtIndex:section] count];
}
return [[_distanceArray objectAtIndex:section] count];
}
Related
i have a products catalogue that is shown in collection view, User may change the category or sub category for new products and they are populating in collection view perfectly.
now user may choose the quantity of any specific product in numbers as 1, 2, 3 or more to buy.
for this i set UIButton action in collection view cell to take users input in UITextfield inside Cell.
all works perfectly, actually i have different number of products as per Category wise, and most of the time i have to scroll to see products in collection view.
when ever the quantity of any product or more than one products are set they are just perfect as i want in each Cell.
** Updated Problem:**
if i change the category or sub category to see other products in collection view now this condition never satisfies in both uibutton action method and in cellForItemAtIndexPath method.
here is problem:
if ([code objectAtIndex:indexPath.row] == [updatedCodes objectAtIndex:i])
{} // this condition is creating problem on category or sub category change.
that how i am working:
Custom CollectionView class
#interface MyCell : UICollectionViewCell
#property (retain, nonatomic) UIButton *btn1;
#property (retain, nonatomic) UITextField *txt1;
#end
implementation
#implementation MyCell
#synthesize btn1, txt1;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
CGRect btn1Rect = CGRectMake(190, 230 , 35 , 35);
btn1 = [[UIButton alloc] initWithFrame:btn1Rect];
btn1.tag=11;
[btn1 setBackgroundImage:[UIImage imageNamed:#"BtnPlus.png"] forState:UIControlStateNormal];
//btn1.layer.borderWidth = 1.0f;
CGRect txt1Rect = CGRectMake(143, 230 , 45 , 30);
txt1 = [[UITextField alloc] initWithFrame:txt1Rect];
txt1.tag=13;
txt1.font=[UIFont fontWithName:#"Superclarendon" size:18];
txt1.textAlignment = NSTextAlignmentCenter;
txt1.textColor = [UIColor blueColor];
//txt1.layer.borderWidth = 1.0f;
}
return self;
}
in ViewController implementation
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CellID" forIndexPath:indexPath];
[[cell btn1] addTarget:self action:#selector( btnPlus:event:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:cell.btn1];
cell.txt1.text = #"0";
for (i = 0; i < [updatedCodes count]; i++)
{
if ([code objectAtIndex:indexPath.row] == [updatedCodes objectAtIndex:i])
{
cell.txt1.text = [updatedQty objectAtIndex:i];
}
}
[cell.contentView addSubview:cell.txt1];
return cell;
}
the UIButton Action Method is
-(void)btnPlus:(id)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:myCollection];
btnIndex = [myCollection indexPathForItemAtPoint: currentTouchPosition];
MyCell *cell = (MyCell*)[myCollection cellForItemAtIndexPath:btnIndex];
NSString *newCode = [code objectAtIndex:btnIndex.row];
NSString *newQty = cell.txt1.text;
if ([updatedCodes containsObject:newCode])
{
for (i = 0; i < [updatedCodes count]; i ++)
{
if ([updatedCodes objectAtIndex:i] == newCode)
{
qnty = [newQty integerValue];
qnty = qnty + 1;
cell.txt1.text = [NSString stringWithFormat:#"%d", qnty];
newQty = cell.txt1.text;
[updatedQty replaceObjectAtIndex:i withObject:newQty];
}
}
if (![indexPaths containsObject:btnIndex])
{
[indexPaths addObject:btnIndex];
}
}
else
{
[updatedCodes addObject:newCode];
qnty = [newQty integerValue];
qnty = qnty + 1;
cell.txt1.text = [NSString stringWithFormat:#"%d", qnty];
newQty = cell.txt1.text;
[updatedQty addObject:newQty];
if (![indexPaths containsObject:btnIndex])
{
[indexPaths addObject:btnIndex];
}
}
[myCollection reloadItemsAtIndexPaths:indexPaths];
}
NOTE:
Code, indexPaths ,updatedCodes and updatedQty are NSMutableArrays
Any suggestion / Help about this problem will be greatly appreciated.
waiting for swift help….. :(
The problem is here
if ([indexPaths count] > 0)
{
for (i = 0; i < [updatedCodes count]; i++)
{
if ([code objectAtIndex:indexPath.row] == [updatedCodes objectAtIndex:i])
{
cell.txt1.text = [updatedQty objectAtIndex:i];
}
}
}
As when this array has records it will go in within this IF statement and will skip the else statement where you are actually setting your cell.txt1.text = #"0". Easiest fix would be to put next line of code just before the for loop
cell.txt1.text = #"0";
Like this
if ([indexPaths count] > 0)
{
cell.txt1.text = #"0";
for (i = 0; i < [updatedCodes count]; i++)
{
if ([code objectAtIndex:indexPath.row] == [updatedCodes objectAtIndex:i])
{
cell.txt1.text = [updatedQty objectAtIndex:i];
}
}
}
answering your own question may be not good but i figured out my problem and resolve it myself
so posting here the issue for some one else who is suffering in same problem….
this is all i change in my code
in view controller implemention
if ([code objectAtIndex:indexPath.row] == [updatedCodes objectAtIndex:i])
{
cell.txt1.text = [updatedQty objectAtIndex:i];
}
with this
if ([[code objectAtIndex:indexPath.row] isEqualToString:[updatedCodes objectAtIndex:i]])
{
cell.txt1.text = [updatedQty objectAtIndex:i];
}
and same change in Button Action method as
if ([newCode isEqualToString:[updatedCodes objectAtIndex:i]])
{
// do something
}
I am inserting and removing rows at random from a tableview. However, if the rows I want to remove are not in view, they are not actually removed in the UI when the given row does come back to view.
If I add and remove the rows only on visible rows, it works just fine. What could be causing this problem?
- (void)sectionHeaderView:(BaseSectionHeaderView *)sectionHeaderView sectionOpened:(NSInteger)sectionOpened {
LogInfo(#"opening section:%ld",sectionOpened);
[_dataSource setSectionAtIndex:sectionOpened Open:YES];
/*
Create an array containing the index paths of the rows to insert: These correspond to the rows for each quotation in the current section.
*/
NSInteger countOfRowsToInsert = [_dataSource tableView:_tableView numberOfRowsInSection:sectionOpened];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:sectionOpened]];
}
/*
Create an array containing the index paths of the rows to delete: These correspond to the rows for each quotation in the previously-open section, if there was one.
*/
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
if (self.openSection.openSectionHeaderView != nil) {
[_openSection.openSectionHeaderView toggleOpenWithUserAction:NO];
// NSInteger countOfRowsToDelete = [_purchaseInvoiceListTable.tableView numberOfRowsInSection:self.openSection.openSectionIndex];
NSInteger countOfRowsToDelete = [_dataSource tableView:_tableView numberOfRowsInSection:self.openSection.openSectionIndex];
[_dataSource setSectionAtIndex:self.openSection.openSectionIndex Open:NO];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:self.openSection.openSectionIndex]];
}
}
// style the animation so that there's a smooth flow in either direction
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (self.openSection.openSectionHeaderView == nil || sectionOpened < self.openSection.openSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
// apply the updates
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[_tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[_tableView endUpdates];
self.openSection.openSectionIndex = sectionOpened;
self.openSection.openSectionHeaderView = sectionHeaderView;
self.openSection.sectionHeight = [NSNumber numberWithFloat:sectionHeaderView.frame.size.height];
LogInfo(#"sectionOpened:%ld",sectionOpened);
}
i think you should reload the table view by this...
[tableView reloadData];
and if from this it will not give the expected result then remove the object from array that fill the table view's row. i think this will definitely work...
tell me is it working or not...
i have a UITableView that a user can add and rearrange objects in... when the user moves an item, i try to re-arrange the data array to fit what the user has changed his cell order to... i am obviously not doing it correctly... here is my code
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableOrderedSet *temp = [[NSMutableOrderedSet alloc]init];
if(fromIndexPath.row == toIndexPath.row){
temp = _dataArray;
}
int min,max;
if(fromIndexPath.row > toIndexPath.row){min = toIndexPath.row;max = fromIndexPath.row;}
if(fromIndexPath.row < toIndexPath.row){max = toIndexPath.row;min = fromIndexPath.row;}
for(int i = 0; i < _dataArray.count; i++)
{
NSLog(#"%i",i);
if(i == toIndexPath.row)
{
[temp insertObject:_dataArray[fromIndexPath.row] atIndex:i];
}
else
{
if(i >= min && i <= max)
{
if(fromIndexPath.row > toIndexPath.row)
{
[temp insertObject:_dataArray[i] atIndex:i+1];
}
else if(fromIndexPath.row < toIndexPath.row)
{
[temp insertObject:_dataArray[i] atIndex:i-1];
}
}
else
{
[temp insertObject:_dataArray[i] atIndex:i];
}
}
}
NSLog(#"================ Cell Array Testing ================");
for(int i = 0; i < temp.count; i++)
{
NSLog(#"Cell: %#",temp[i]);
}
_dataArray = temp;
}
I'm willing to change a specific header view of my UITableView when I click a row.
I've read all posts about it yet. I tried "reloadData", "setNeedDisplay", "reloadSections:withRowAnimation:", and several others ideas... there is nothing to do. My header view either doesn't update or it does weird things like updating only when I move the table view (which is not what I'm willing to achieve).
My code looks like this for now (regarding the UITableView delegates methods):
-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
if(tableView==_storeTableView){
return [_storeDataArray count];
} else {
return 1;
}
}
-(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section {
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:section];
if (!headerModel.headerView) {
NSString *shelfName = headerModel.shelf;
headerModel.headerView = [[[HouraStoreHeaderView alloc] initWithFrame:CGRectMake(0.0, 0.0, _storeTableView.bounds.size.width, 80) title:shelfName section:section subheaderNumber:([headerModel.openedSubHeaders count]-1) delegate:self] autorelease];
}
return headerModel.headerView;
} else {
return nil;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:section];
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
NSInteger numberOfRowsInSection = [[myDict allKeys] count];
return headerModel.open ? numberOfRowsInSection : 0;
} else if(tableView==_searchTableView){
return [_resultArray count];
} else {
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
cell.accessoryView=[[[HouraStoreCellView alloc] initWithFrame:CGRectMake(0.0, 0.0, _storeTableView.bounds.size.width, 50) title:[[myDict allKeys] objectAtIndex:indexPath.row]] autorelease];
return cell;
} else if (tableView==_searchTableView) {
cell.textLabel.text = [_resultArray objectAtIndex:indexPath.row];
return cell;
} else {
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:section];
NSInteger height = 59.0 + ([headerModel.openedSubHeaders count]-1)*41.0;
return height;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(tableView==_storeTableView){
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
if ([[myDict objectForKey:[[myDict allKeys] objectAtIndex:indexPath.row]] isKindOfClass:[NSDictionary class]]) {
[self cellOpened:indexPath];
} else {
[_activityIndicatorView startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_listProductsFoundedFinished:)
name:HouraSearchProductsDone
object:nil];
NSString *searchString = [[myDict allKeys] objectAtIndex:indexPath.row];
searchString = [searchString stringByReplacingOccurrencesOfString:#"\"" withString:#"\\u0022"];
[_singleton.util beginSearchProducts:searchString context:#"2"];
}
} else if(tableView==_searchTableView){
_searchBar.text = [_resultArray objectAtIndex:indexPath.row];
[_searchBar resignFirstResponder];
[_activityIndicatorView startAnimating];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_listProductsFoundedFinished:)
name:HouraSearchProductsDone
object:nil];
[_singleton.util beginSearchProducts:_searchBar.text context:#"2"];
}
}
-(void)headerView:(HouraStoreHeaderView*)headerView headerOpened:(NSInteger)headerOpened {
if (self.openSectionIndex!=NSNotFound) {
[self closeAllHeaders];
}
//[self closeAllHeaders];
HouraStoreHeaderModel *headerModel =nil;
headerModel = [self.headerInfoArray objectAtIndex:headerOpened];
headerModel.open = YES;
headerModel.headerView.disclosureButton.selected = YES;
NSDictionary *myDict = _storeDataDict;
for (NSInteger i = 0; i < [headerModel.openedSubHeaders count]; i++) {
myDict = [myDict objectForKey:[headerModel.openedSubHeaders objectAtIndex:i]];
}
NSInteger countOfRowsToInsert = [[myDict allKeys] count];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:headerOpened]];
}
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenSectionIndex = self.openSectionIndex;
if (previousOpenSectionIndex != NSNotFound) {
HouraStoreHeaderModel *previousHeaderModel = [self.headerInfoArray objectAtIndex:previousOpenSectionIndex];
previousHeaderModel.open = NO;
previousHeaderModel.headerView.disclosureButton.selected = NO;
[previousHeaderModel.headerView toggleOpenWithUserAction:NO];
NSInteger countOfRowsToDelete = [[[_storeDataDict objectForKey:previousHeaderModel.shelf ] allKeys] count];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
}
}
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenSectionIndex == NSNotFound || headerOpened < previousOpenSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
} else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
[_storeTableView beginUpdates];
[_storeTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[_storeTableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[_storeTableView endUpdates];
self.openSectionIndex = headerOpened;
}
-(void)headerView:(HouraStoreHeaderView*)headerView headerClosed:(NSInteger)headerClosed {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:headerClosed];
headerModel.open = NO;
headerModel.headerView.disclosureButton.selected = NO;
[headerModel cleanOpenedSubHeaders];
[self.headerInfoArray replaceObjectAtIndex:headerClosed withObject:headerModel];
NSInteger countOfRowsToDelete = [_storeTableView numberOfRowsInSection:headerClosed];
if (countOfRowsToDelete > 0) {
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:headerClosed]];
}
[_storeTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
}
self.openSectionIndex = NSNotFound;
}
-(void)cellOpened:(NSIndexPath*)indexPath {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
[self headerView:headerModel.headerView headerClosed:indexPath.section];
[headerModel addOpenedSubHeaders:[[[_storeDataDict objectForKey:headerModel.shelf] allKeys] objectAtIndex:indexPath.row]];
[self.headerInfoArray replaceObjectAtIndex:indexPath.section withObject:headerModel];
headerModel = [self.headerInfoArray objectAtIndex:indexPath.section];
[self headerView:headerModel.headerView headerOpened:indexPath.section];
}
-(void)closeAllHeaders {
for (NSInteger i = 0; i < [self.headerInfoArray count]; i++) {
HouraStoreHeaderModel *headerModel = [self.headerInfoArray objectAtIndex:i];
[self headerView:headerModel.headerView headerClosed:i];
}
}
What I'd like to do is, when I click a row, the section header update so it contains a new button with the row text. Then I dismiss the row and reload new datas in the section rows. I managed to handle the rows perfectly. But I can't find a way to get this header view updated.
Thx for any idea.
You just change it directly. I created an instance variable in the header file for a label that I will put in the header's view I'll create:
#interface MainViewController : UITableViewController {
// creating my datasource array instance variable
NSArray *_items;
// this is the label I will add to the header view when I create it
UILabel *_headerLabel;
}
#end
And in my tableView when they select a row I call a function that simply changes the text on the label:
#implementation MainViewController
- (id)init {
self = [super initWithStyle:UITableViewStyleGrouped];
/ filling my datasource with test strings
_items = #[#"one", #"two"];
return self;
}
- (void)changeHeaderLabel:(NSString *)newLabel {
// when this function gets called and is passed a string, I will simply
// set the text on the label to the new string and viola!
_headerLabel.text = newLabel;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// this table will only have a single section for demo purposes
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// return the count of my datasource array
return _items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// attempt to create a cell by reusing one with a given identifier
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
// if I wasn't able to reuse one
if (cell == nil) {
// create one from scratch with that identifier
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
// now simply set the text on the cell from my data source array of strings
cell.textLabel.text = _items[indexPath.row];
// and return the cell
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// deselect the row so the cell automatically fades out after selection
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// here you could do one of two things, either get a reference to the cell itself,
// and then get the value stored in it's textLabel
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *newHeaderTitleString = selectedCell.textLabel.text;
// OR you can get it right from your datasource
NSString *newHeaderTitleString = _items[indexPath.row];
// then just call the above function with the string as the single param
[self changeHeaderLabel:newHeaderTitleString];
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
// here I just create a view that will span the whole frame and is an arbitrary height
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 80)];
// set the background color to clear
headerView.backgroundColor = [UIColor clearColor];
// then I initialize my instance variable with a frame that's centered in the view
// for aesthetic purposes
_headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, self.view.frame.size.width - 10, 80)];
// then I set the text color, add an autoresizing mask so if the view rotates
// it still remains centered properly, set the text to some starting value,
// and add it to the headerView I previously created
_headerLabel.textColor = [UIColor darkGrayColor];
_headerLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_headerLabel.text = #"Before";
[headerView addSubview:_headerLabel];
// then I return the headerView
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
// return an arbitrary height here for testing
return 80;
}
That results in the following:
If you have any questions let me know! This is just a quick example to demonstrate it, but you may want to customize the view in a different way altogether. This should at least solve your problem and give you a starting point to work from.
Have you tried reloadRowsAtIndexPaths:withRowAnimation: where you set the row property of the NSIndexPath passed in as NSNotFound? So reloading just the header of section 3, for instance would look like
NSIndexPath * headerIndexPath = [NSIndexPath indexPathForRow: NSNotFound section:3];
[self.tableView reloadRowsAtIndexPaths:#[headerIndexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
I guarantee nothing, but I'm pretty sure it used to work before, because I used it a couple of times.
But even if it works, it's still a hack that might get broken by Apple any time.
edit
Ok, never mind. I tried this with iOS 7 in Xcode 5 and for some reason, even with NSNotFound as the row number, it still reloads the whole sections (with all its cells). So this does not work any more, damn.
I have a snippet of code of expanding the rows of cells of section. The error occurs at the xtable updates [UIViewAnimation initWithView:indexPath:endRect:endAlpha:startFraction:endFraction:curve:animateFromCurrentPosition:shouldDeleteAfterAnimation:editing:]
Snippet of code
-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)sectionOpened {
SectionInfo *sectionInfo = [self.sectionInfoArray objectAtIndex:sectionOpened];
sectionInfo.open = YES;
NSInteger countOfRowsToInsert = 5;
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:sectionOpened]];
}
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenSectionIndex = self.openSectionIndex;
if (previousOpenSectionIndex != NSNotFound) {
SectionInfo *previousOpenSection = [self.sectionInfoArray objectAtIndex:previousOpenSectionIndex];
previousOpenSection.open = NO;
[previousOpenSection.headerView toggleOpenWithUserAction:NO];
NSInteger countOfRowsToDelete = 5;
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
}
}
// Style the animation so that there's a smooth flow in either direction.
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenSectionIndex == NSNotFound || sectionOpened < previousOpenSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation =UITableViewRowAnimationBottom;
}
else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
// Apply the updates.
[xtable beginUpdates];
[xtable insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[xtable deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[xtable endUpdates];
self.openSectionIndex = sectionOpened;
}
what could possible be wrong? any ideas?
in my case helped
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0;
}