UICollectionView current visible cell index
IosObjective CIpadUicollectionviewIos Problem Overview
I am using UICollectionView
first time in my iPad application.
I have set UICollectionView
such that its size and cell size is same, means only once cell is displayed at a time.
Problem:
Now when user scroll UICollectionView
I need to know which cell is visible I have to update other UI elements on change. I didn't find any delegate method for this. How can I achieve this?
Code:
[self.mainImageCollection setTag:MAIN_IMAGE_COLLECTION_VIEW];
[self.mainImageCollection registerClass:[InspirationMainImageCollectionCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[self.mainImageFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.mainImageFlowLayout setMinimumInteritemSpacing:0.0f];
[self.mainImageFlowLayout setMinimumLineSpacing:0.0f];
self.mainImageFlowLayout.minimumLineSpacing = 0;
[self.mainImageCollection setPagingEnabled:YES];
[self.mainImageCollection setShowsHorizontalScrollIndicator:NO];
[self.mainImageCollection setCollectionViewLayout:self.mainImageFlowLayout];
What I have tried:
As UICollectionView
conforms to UIScrollView
, I got when user scroll ends with UIScrollViewDelegate
method
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
But inside above function how can I get current visible cell index of UICollectionView
?
Ios Solutions
Solution 1 - Ios
indexPathsForVisibleItems
might work for most situations, but sometimes it returns an array with more than one index path and it can be tricky figuring out the one you want. In those situations, you can do something like this:
CGRect visibleRect = (CGRect){.origin = self.collectionView.contentOffset, .size = self.collectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.collectionView indexPathForItemAtPoint:visiblePoint];
This works especially well when each item in your collection view takes up the whole screen.
Swift version
let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)
Solution 2 - Ios
The method [collectionView visibleCells] give you all visibleCells array you want. Use it when you want to get
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
for (UICollectionViewCell *cell in [self.mainImageCollection visibleCells]) {
NSIndexPath *indexPath = [self.mainImageCollection indexPathForCell:cell];
NSLog(@"%@",indexPath);
}
}
Update to Swift 5:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
for cell in yourCollectionView.visibleCells {
let indexPath = yourCollectionView.indexPath(for: cell)
print(indexPath)
}
}
Solution 3 - Ios
Swift 5:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = collectionView.contentOffset
visibleRect.size = collectionView.bounds.size
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
guard let indexPath = collectionView.indexPathForItem(at: visiblePoint) else { return }
print(indexPath)
}
Working Answers Combined In Swift 2.2 :
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = self.collectionView.contentOffset
visibleRect.size = self.collectionView.bounds.size
let visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect))
let visibleIndexPath: NSIndexPath = self.collectionView.indexPathForItemAtPoint(visiblePoint)
guard let indexPath = visibleIndexPath else { return }
print(indexPath)
}
Solution 4 - Ios
For completeness sake, this is the method that ended up working for me. It was a combination of @Anthony & @iAn's methods.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGRect visibleRect = (CGRect){.origin = self.collectionView.contentOffset, .size = self.collectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.collectionView indexPathForItemAtPoint:visiblePoint];
NSLog(@"%@",visibleIndexPath);
}
Solution 5 - Ios
UICollectionView current visible cell index: Swift 3, 4 and 5+
var visibleCurrentCellIndexPath: IndexPath? {
for cell in self.collectionView.visibleCells {
let indexPath = self.collectionView.indexPath(for: cell)
return indexPath
}
return nil
}
As an Extension:
extension UICollectionView {
var visibleCurrentCellIndexPath: IndexPath? {
for cell in self.visibleCells {
let indexPath = self.indexPath(for: cell)
return indexPath
}
return nil
}
}
Usage:
if let indexPath = collectionView.visibleCurrentCellIndexPath {
/// do something
}
Solution 6 - Ios
It will probably be best to use UICollectionViewDelegate methods: (Swift 3)
// Called before the cell is displayed
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print(indexPath.row)
}
// Called when the cell is displayed
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
print(indexPath.row)
}
Solution 7 - Ios
Just want to add for others : for some reason, I didnt not get the cell that was visible to the user when I was scrolling to previous cell in collectionView with pagingEnabled.
So I insert the code inside dispatch_async to give it some "air" and this works for me.
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
dispatch_async(dispatch_get_main_queue(), ^{
UICollectionViewCell * visibleCell= [[self.collectionView visibleCells] objectAtIndex:0];
[visibleCell doSomthing];
});
}
Solution 8 - Ios
Swift 3.0
Simplest solution which will give you indexPath for visible cells..
yourCollectionView.indexPathsForVisibleItems
will return the array of indexpath.
Just take the first object from array like this.
yourCollectionView.indexPathsForVisibleItems.first
I guess it should work fine with Objective - C as well.
Solution 9 - Ios
> For Swift 3.0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let visibleRect = CGRect(origin: colView.contentOffset, size: colView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let indexPath = colView.indexPathForItem(at: visiblePoint)
}
Solution 10 - Ios
converting @Anthony's answer to Swift 3.0 worked perfectly for me:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = yourCollectionView.contentOffset
visibleRect.size = yourCollectionView.bounds.size
let visiblePoint = CGPoint(x: CGFloat(visibleRect.midX), y: CGFloat(visibleRect.midY))
let visibleIndexPath: IndexPath? = yourCollectionView.indexPathForItem(at: visiblePoint)
print("Visible cell's index is : \(visibleIndexPath?.row)!")
}
Solution 11 - Ios
You can use scrollViewDidEndDecelerating
: for this
//@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
for (UICollectionViewCell *cell in [self.collectionView visibleCells]) {
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSUInteger lastIndex = [indexPath indexAtPosition:[indexPath length] - 1];
NSLog(@"visible cell value %d",lastIndex);
}
}
Solution 12 - Ios
In this thread, There are so many solutions that work fine if cell takes full screen but they use collection view bounds and midpoints of Visible rect However there is a simple solution to this problem
DispatchQueue.main.async {
let visibleCell = self.collImages.visibleCells.first
print(self.collImages.indexPath(for: visibleCell))
}
by this, you can get indexPath of the visible cell. I have added DispatchQueue because when you swipe faster and if for a brief moment the next cell is shown then without dispactchQueue you'll get indexPath of briefly shown cell not the cell that is being displayed on the screen.
Solution 13 - Ios
This is old question but in my case...
- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {
_m_offsetIdx = [m_cv indexPathForCell:m_cv.visibleCells.firstObject].row;
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
_m_offsetIdx = [m_cv indexPathForCell:m_cv.visibleCells.lastObject].row;
}
Solution 14 - Ios
Also check this snippet
let isCellVisible = collectionView.visibleCells.map { collectionView.indexPath(for: $0) }.contains(inspectingIndexPath)
Solution 15 - Ios
try this, it works. (in the example below i have 3 cells for example.)
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let visibleRect = CGRect(origin: self.collectionView.contentOffset, size: self.collectionView.bounds.size)
let visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect))
let visibleIndexPath = self.collectionView.indexPathForItemAtPoint(visiblePoint)
if let v = visibleIndexPath {
switch v.item {
case 0: setImageDescription()
break
case 1: setImageConditions()
break
case 2: setImageResults()
break
default: break
}
}
Solution 16 - Ios
Swift 3 & Swift 4:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = collectionView.contentOffset
visibleRect.size = collectionView.bounds.size
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
guard let indexPath = collectionView.indexPathForItem(at: visiblePoint) else { return }
print(indexPath[1])
}
If you want to show actual number than you can add +1