Cannot refresh 1 cell in UICollectionview iOS - ios

I have added 1 new cell in my UICollectionView, but it only refreshes after I scroll down and scroll to top, and that cell will be appeared. I want it will appeare immediately after inserting a new cell, without scrolling. I tried the following code. Please help me to correct it.
[self performBatchUpdates:^{
self.arrayWithIndexPaths = [NSMutableArray array];
[self.arrayWithIndexPaths addObject:[NSIndexPath
indexPathForRow:_numberOfItemsInCollectionView -1
inSection:SECTION_COUNT-1]];
[self insertItemsAtIndexPaths:self.arrayWithIndexPaths];
//prepare generate XML
[self prepareForGenerateXML:selectedItemsArray andCameraMode:NO];
} completion:nil];

Related

UICollectionView scrollToItemAtIndexPath scrolls to the wrong path

I have two UICollectionViews and when the user scrolls on one, the other should also change by scrolling to the same index path. I've implemented the following code so that, when the first UICollectionView decelerates and lands on one cell, the second UICollectionView should automatically scroll to that same IndexPath:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
// Only do this for the first UICollectionView
if (scrollView.tag == 0) {
// Find the new IndexPath
CGRect visibleRect = (CGRect){.origin = self.filterCollectionView.contentOffset, .size = self.filterCollectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.filterCollectionView indexPathForItemAtPoint:visiblePoint];
// On main thread, scroll second CollectionView to visibleIndexPath
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Row: %li, Sec: %li", visibleIndexPath.row, visibleIndexPath.section);
[self.filterTitleCollectionView reloadData];
[self.filterTitleCollectionView layoutIfNeeded];
[self.filterTitleCollectionView setNeedsDisplay];
//scroll titles to same index path
[self.filterTitleCollectionView scrollToItemAtIndexPath:visibleIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
NSLog(#"Row: %li, Sec: %li", self.filterTitleCollectionView.indexPathsForSelectedItems[0].row, self.filterTitleCollectionView.indexPathsForSelectedItems[0].section);
});
}
}
For some reason, the filterTitleCollectionView does not scroll to the visibleIndexPath. Or if it does scroll, it scrolls to the wrong cell (in a predictable pattern). You can see the video of it here:
https://streamable.com/q2lnnw
The row number is in the title of the cell. As you can see, the second UICollectionView keeps scrolling to row 2 for some reason.
The correct IndexPath is printed, but it scrolls to the wrong IndexPath or doesn't scroll at all.

scrollToRowAtIndexPath doesn't scroll to correct position when segment value changed

I have a segment control with a table view. When user clicks segment control, then table view changes data source. I would like table to scroll to last position user had seen.
For example: user was in segment 1 and looking at table view line 3.
segment 1 | segment 2
line 3
line 4
line 5
Then user clicked segment 2 then clicked back to segment 1. I want table view scroll to line 3.
Code is as below. In viewDidAppear I have same code to get lastSavedRow and call scrollToRowAtIndexPath. When app gets loaded the scrolling works. However, in segmentValueChanged action it doesn't roll to correct position. In debugger I am sure the lastSavedRow has correct number.
- (IBAction) segmentVauleChanged : (id)sender
{
// remember row number before segment value changes
NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
NSArray * sortedIndexPaths = [indexPaths sortedArrayUsingSelector:#selector(compare:)];
NSIndexPath *firstVisibleIndexPath = [sortedIndexPaths objectAtIndex:0];
// function to write row number and seg index to user default
[self saveScrollPositionInSegment: self.currentSegmentSelectedIndex row:firstVisibleIndexPath.row];
self.currentSegmentSelectedIndex = segmentController.selectedSegmentIndex;
[self.tableView reloadData];
// scroll to last saved position
int lastSavedRow = [self getScrollPositionInSegment:segmentController.selectedSegmentIndex];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:lastSavedRow inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
Does anyone have same problem? Many thanks in advance!
The lastSavedRow will on the top of the current tableview. I think there maybe something wrong with the dealing with datasource. Maybe after change the datasource, the last postion the user seen was not the index you figured out!
If you provide more information or your example, we can help you to find out the mistake.
I would do the following.
1 .Call visibleCells before calling indexPathsForVisibleRows
[self.tableView visibleCells];
2.Look at the need for sorting the array before identifying firstVisibleIndexPath
3.reloadData is not synchronous so its possible that the table is not completed loading before you try to scroll to an index. So try to see if you can do the scrolling in one of the delegate functions instead of here. See this.
hackerinheels 's (3) is right. reloadData hasn't finished when scrolling table is called. So I added the code:
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
if (section == ([self.tableView numberOfSections] - 1)) {
//NSLog(#"finish loading");
// put the scrolling table call here: [self scrollTableView ];
}
return nil;
}
Thank you all for helping!

Dynamically adding content to UICollectionView

I have a UICollectionView that is backed by a web service. I initially load 100 items from the service into the collection view. When the user reaches the bottom of the view, I dyanmically load the next "page" of content.
This is working, but my collectionView flashes and re-draws the entire view from the top. I am trying to get it to just begin drawing the new content at the bottom of the view.
Code for adding new content to the collectionview:
- (void)insertIntoCollectionView: (int) itemCnt {
// only do the batchupdate for NON-initial load
if(curPage != 0) {
// Add items to main photos array, and update collectionview using batchUpdate
[self.collectionView performBatchUpdates:^{
// Get starting size before update
int resultsSize = itemCnt;
// Create a new array to hold the indexpath for the inserted data
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
// Add indexpath objects to the new array to be added to the collection view
for (int i = resultsSize; i < resultsSize + itemCnt; i++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
}
// Update the collectionview
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
}
completion:nil];
} else {
[self.collectionView reloadData];
}
curPage++;
Anything obvious? It functionally works, but looks pretty bad with all of the flashing and re-drawing.
OK, managed to fix it myself. I was using the incorrect values for the resultSize variable. The for loop should start at the end of the current array count, and loop through the number of item that we added, thus adding them at the end. I was starting at the beginning of the array index 1 when building the arrayWithIndexPaths, causing it to re-draw the whole view.

UICollectionView Scrolling Horizontally

I am implementing a UICollectionView in my iOS app. I have it so each cell is the width of the screen and I want to have it so when scrolling horizontally it locks onto the visible cell and moves it to the center of the screen. The code bellow I have only works for the first cell. I don't understand how to get this to work for any cell the user has visible.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
int index = 0;
for (TextCell * t in [_tabBarCollectionView visibleCells]) {
if ((t.center.x>0)&&(t.center.x<[[UIScreen mainScreen]bounds].size.width)) {
[_tabBarCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
break;
}
index++;
}
}
You can turn paging on for the collection view and it will have that effect. Go to the xib file or storyboard that has the collection added and enable paging under its properties.

UICollectionView animate data change

In my Project I use UICollectionView to display a grid of icons.
The user is able to change the ordering by clicking a segmented control which calling a fetch from core data with different NSSortDescriptor.
The amount of data is always the same, just ending up in different sections / rows:
- (IBAction)sortSegmentedControlChanged:(id)sender {
_fetchedResultsController = nil;
_fetchedResultsController = [self newFetchResultsControllerForSort];
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
[self.collectionView reloadData];
}
The problem is that reloadData doesn't animate the change, UICollectionView just pops with the new data.
Should I keep track in which indexPath a cell was before and after change, and use [self.collectionView moveItemAtIndexPath: toIndexPath:] to perform the animation for the change or there is a better method ?
I didn't get much into subclassing collectionViews so any help will be great...
Thanks,
Bill.
Wrapping -reloadData in -performBatchUpdates: does not seem to cause a one-section collection view to animate.
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadData];
} completion:nil];
However, this code works:
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];
reloadData doesn't animate, nor does it reliabably do so when put in a UIView animation block. It wants to be in a UICollecitonView performBatchUpdates block, so try something more like:
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
// do something on completion
}];
This is what I did to animate reload of ALL SECTIONS:
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];
Swift 3
let range = Range(uncheckedBounds: (0, collectionView.numberOfSections))
let indexSet = IndexSet(integersIn: range)
collectionView.reloadSections(indexSet)
For Swift users, if your collectionview only has one section:
self.collectionView.performBatchUpdates({
let indexSet = IndexSet(integersIn: 0...0)
self.collectionView.reloadSections(indexSet)
}, completion: nil)
As seen on https://stackoverflow.com/a/42001389/4455570
Reloading the whole collection view inside a performBatchUpdates:completion: block does a glitchy animation for me on iOS 9 simulator. If you have a specific UICollectionViewCell you want do delete, or if you have it's index path, you could call deleteItemsAtIndexPaths: in that block. By using deleteItemsAtIndexPaths:, it does a smooth and nice animation.
UICollectionViewCell* cellToDelete = /* ... */;
NSIndexPath* indexPathToDelete = /* ... */;
[self.collectionView performBatchUpdates:^{
[self.collectionView deleteItemsAtIndexPaths:#[[self.collectionView indexPathForCell:cell]]];
// or...
[self.collectionView deleteItemsAtIndexPaths:#[indexPath]];
} completion:nil];
The help text says:
Call this method to reload all of the items in the collection view.
This causes the collection view to discard any currently visible items
and redisplay them. For efficiency, the collection view only displays
those cells and supplementary views that are visible. If the
collection data shrinks as a result of the reload, the collection view
adjusts its scrolling offsets accordingly. You should not call this
method in the middle of animation blocks where items are being
inserted or deleted. Insertions and deletions automatically cause the
table’s data to be updated appropriately.
I think the key part is "causes the collection view to discard any currently visible items". How is it going to animate the movement of items it has discarded?

Resources