I'm trying to remove the first collection view cell. I'm having trouble getting the indexPath for the first item in the collection view. Here is what I've got so far.
-(void)removeFirstItemFromCollectionView
{
NSLog(#"%i", [self.hostQueue count]);
[self.collectionView performBatchUpdates:^{
NSIndexPath * firstIndexPath = [self.collectionView indexPathForCell:(CollectionViewCell *)[self.collectionView viewWithTag:0]];
NSArray *indexPaths = #[firstIndexPath];
//delete item from hostQueue
[self.hostQueue removeObjectAtIndex:0];
// Now delete the items from the collection view.
[self.collectionView deleteItemsAtIndexPaths:indexPaths];
} completion:nil];
}
I'm getting a crash with:
NSIndexPath * firstIndexPath = [self.collectionView indexPathForCell:(CollectionViewCell *)[self.collectionView viewWithTag:0]];
In my dataSource I set the cell's tag equal to the indexPath.row. I guess this doesn't work? How should I be getting the first indexPath?
Thanks.
You don't need to call indexPathForCell as you know that you want to delete the first cell - index 0. You can use the NSIndexPath class method indexPathForItem:inSection: to create the index path directly -
- (void)removeFirstItemFromCollectionView
{
NSLog(#"%i", [self.hostQueue count]);
//delete item from hostQueue
[self.hostQueue removeObjectAtIndex:0];
// Now delete the items from the collection view.
[self.collectionView deleteItemsAtIndexPaths:#[[NSIndexPath indexPathForItem:0 inSection:0]]];
}
Related
I have a swift table view/collection view. Is there anyway I could append items to the end of the container ? Like an upside down stack or something ?
You need insert rows at in UITableView and collection view for that first need to insert in you data source.
For TableView
//Update data source with the object that you need to add
[tableDataSource addObject:newObject];
NSInteger row = [tableDataSource count]-1 ;//specify a row where you need to add new row in your case last
NSInteger section = //specify the section where the new row to be added,
//section = 0 here since you need to add row at first section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
For CollectionView
[self.myCollectionView performBatchUpdates:^{
[collectionDataSource addObject:newObject];
NSInteger row = [collectionDataSource count]-1 ;//specify a row where you need to add new row in your case last
to add new row in your case last
NSInteger section = //specify the section where the new row to be added,
//section = 0 here since you need to add row at first section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.myCollectionView insertItemsAtIndexPath: indexPath];
} completion:nil];
I have a UITableView with expandable sections. When a user goes to another view, I need all the expanded sections to collapse, which I'll need to put in the viewWillDisappear method.
I've found solutions only on how to delete all rows from a table view at once, but is there a way to delete all the rows from a specific section?
EDIT:
I have figured out a solution, but I'm not sure if it's optimal or can lead to inefficiencies in the future. Whenever a cell is expanded, it gets added to an NSMutableIndexSet. So in my viewWillDisappear method, I iterate over the expanded sections like so:
-(void)viewWillDisappear:(BOOL)animated
{
if (expandedSections.count != 0) {
NSLog(#"COLLAPSING CALLED");
[self.tableView beginUpdates];
NSUInteger section = [expandedSections firstIndex];
do
{
NSInteger rows;
NSMutableArray *tmpArray = [NSMutableArray array];
rows = [self tableView:self.tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
for (int i=1; i<rows; i++) {
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i inSection:section];
[tmpArray addObject:tmpIndexPath];
}
[self.tableView deleteRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationTop];
NSIndexPath *expandableCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:section];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:expandableCellIndexPath];
cell.accessoryView = [DTCustomColoredAccessory accessoryWithColor:[self.colorHolder objectAtIndex:section] type:DTCustomColoredAccessoryTypeRight];
section = [expandedSections indexGreaterThanIndex:section];
} while (section != NSNotFound);
[self.tableView endUpdates];
}
}
Please let me know if this is a good solution or, if I'm suspecting correctly, if this will lead to slower transitions between views in the future when there are more rows in each expanded section. Any help or advice would be appreciated. Thanks.
If you want to animate changes, you will need to first update your data source (to return 0 for number of rows in the section) then remove section and add section at the same index path in one transaction between [tv beginUpdates] [tv endUpdates]
Otherwise just update the data source and reload the table on your way back to the VC (if you don't want any animations)
I did not read this in detail but surely the for loop should start at zero.
for (int i=0; i<rows; i++) {
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i inSection:section];
[tmpArray addObject:tmpIndexPath];
}
otherwise you will only delete all but the first cells in the section.
I am developing an ToDoList iOS app that uses table view. Cell contents(UITableCellView) are filled by a instance variable of nsmutablearray.
When I add a new ToDoItem, the content is being displayed twice for each row I add. When I put a breakpoint and debug the code I found that cellForRowAtIndexPath method is called twice for every row I add. Why is that happening. How to handle this case or is there any other alternative method to return UITableCellView cell for display.
Method is shown below
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell;
cell = [self.tableView dequeueReusableCellWithIdentifier:#"ListPrototypeCell" forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"ListPrototypeCell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
ToDoItem *item = [self.toDoItems objectAtIndex:indexPath.row];
cell.textLabel.text = item.itemName;
return cell;
}
This method is called when user adds new item
-(IBAction)unwindToList:(UIStoryboardSegue *)segue{
AddToDoItemViewController *ITEM = [segue sourceViewController];
ToDoItem *tItem = ITEM.toDoItem;
if (tItem!=nil) {
[self.toDoItems addObject:tItem];
[self.tableView reloadData];
[self setItems:self.toDoItems];
}
}
EDIT:
-(void)setItems:(NSMutableArray *)item{
if (item!=nil) {
tDItems = item;
}
}
tDItems is just another instance variable I am using
Thanks in advance
just check your array if duplicate items are added to the array cause even if cellForRowAtIndexPath is called twice it will not create duplicate cells if items in array are not duplicate. cellForRowAtIndexPath might get called twice because of multiple reloads
i think problem is with the reloading the table data ..try to reload data like this...may be it works
-(IBAction)unwindToList:(UIStoryboardSegue *)segue{
AddToDoItemViewController *ITEM = [segue sourceViewController];
ToDoItem *tItem = ITEM.toDoItem;
if (tItem!=nil) {
[self.toDoItems addObject:tItem];
[self setItems:self.toDoItems];
}
[self.tableView reloadData];
}
Use insertRowsAtIndexPaths with [ToDoItem addObject:[NSIndexPath indexPathForRow:i inSection:0]];
where i= indexPath for where you want to insert Element.
` [tableView beginUpdates];
[insertRowAtIndexArray removeAllObjects];
[insertRowAtIndexArray addObject:[NSIndexPath indexPathForRow:i inSection:0]];
[toDoItems addObject:[NSIndexPath indexPathForRow:i inSection:0]];
i++;
[tableView insertRowsAtIndexPaths:insertRowAtIndexArray withRowAnimation:(UITableViewRowAnimationAutomatic)];
[tableView endUpdates];`
I did a small fix and it worked. Below is the code
-(IBAction)unwindToList:(UIStoryboardSegue *)segue{
AddToDoItemViewController *ITEM = [segue sourceViewController];
ToDoItem *tItem = ITEM.toDoItem;
if (tItem!=nil) {
if ([self.toDoItems count] == 0) { // This is the fix made
[self.toDoItems addObject:tItem];
}
[self.tableView reloadData];
[self setItems:self.toDoItems];
}
}
But I am not sure how the items are getting added from the second item onwards.addObject is working only for the first item and there are no duplicates in the view. But if i remove that if condition then duplicates can be seen. Now I am debugging to see why only it is working for first item.
I am trying to allow just two rows to be selected at a time in a table view, but it seems that I can't get a good result!
The idea is that you select one row and this sends an object to core data, then when you select a second row that sends a different thing to core data. But if you select more than two rows, it displays an alert view that says you can only select two rows.
I have tried this:
NSArray *indexPathArray = [[NSArray alloc]init];
indexPathArray = [self.mainTableView indexPathsForSelectedRows];
if ( indexPathArray.count == 1) {
NSLog(#"%#",#"we have 1 cell selected");
}
if ( indexPathArray.count == 2) {
NSLog(#"%#",#"We have 2 cells selected!");
}
if (indexPathArray.count > 2) {
NSLog(#"%#",#"ERROR ERROR!!!");
}
and I have tried many other google and stack overflow suggestions but didn't get to an end!
So how can I achieve this?
This is the method in which I have the code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath(NSIndexPath*)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self setSelection:indexPath];
_profile = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSInteger rowNumber = 0;
for (NSInteger i = 0; i < indexPath.section; i++) {
rowNumber += [self tableView:tableView numberOfRowsInSection:i];
}
rowNumber += indexPath.row;
NSLog(#"%ld",(long)rowNumber);
NSArray *indexPathArray = [[NSArray alloc]init];
indexPathArray = [self.mainTableView indexPathsForSelectedRows];
if ( indexPathArray.count == 1) {
NSLog(#"%#",#"we have 1 cell selected");
}
if ( indexPathArray.count == 2) {
NSLog(#"%#",#"We have 2 cells selected!");
}
if (indexPathArray.count > 2) {
NSLog(#"%#",#"ERROR ERROR!!!");
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade]; }
Delete this line:
[tableView deselectRowAtIndexPath:indexPath animated:YES];
from your didSelectRowAtIndexPath: method.
Comment/Delete out the following lines in your code in method
- (void)tableView:(UITableView )tableView didSelectRowAtIndexPath(NSIndexPath)indexPath
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
And check. Your logical part should then work fine.
Explanation: TableView method indexPathsForSelectedRows will return the number of rows selected. And your are deselecting the selected row using this line of code:
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Commenting/deleting only this single line of code will give you indexPathArray count as 1, because you still reload the row at the end of your method using:
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
The above piece of code also resets the row to be unselected as you are reloading it. So you need to comment or delete it out too.
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.