ios UICollectionView:页面控件的当前索引路径

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20626744/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 22:48:11  来源:igfitidea点击:

UICollectionView: current index path for page control

iosobjective-cpaginationuicollectionviewuipagecontrol

提问by hzxu

I'm using a UICollectionView with a flow layout to show a list of cells, I also have a page control to indicate current page, but there seems to be no way to get current index path, I know I can get visible cells:

我正在使用带有流布局的 UICollectionView 来显示单元格列表,我还有一个页面控件来指示当前页面,但似乎无法获取当前索引路径,我知道我可以获得可见单元格:

UICollectionView current visible cell index

UICollectionView 当前可见单元格索引

however there can be more than one visible cells, even if each of my cells occupies full width of the screen, if I scroll it to have two halves of two cells, then they are both visible, so is there a way to get only one current visible cell's index?

但是,可以有多个可见单元格,即使我的每个单元格都占据了屏幕的整个宽度,如果我滚动它以获得两个单元格的两半,那么它们都是可见的,所以有没有办法只得到一个当前可见单元格的索引?

Thanks

谢谢

回答by andykkt

You can get the current index by monitoring contentOffset in scrollViewDidScroll delegate

您可以通过在 scrollViewDidScroll 委托中监控 contentOffset 来获取当前索引

it will be something like this

它会是这样的

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    NSInteger currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width;

}

回答by Dmitry Coolerov

Get page via NSIndexPath from center of view.

从视图中心通过 NSIndexPath 获取页面。

Works even your page not equal to width of UICollectionView.

即使您的页面不等于 UICollectionView 的宽度也能工作。

    func scrollViewDidScroll(scrollView: UIScrollView) {
    let center = CGPoint(x: scrollView.contentOffset.x + (scrollView.frame.width / 2), y: (scrollView.frame.height / 2))
    if let ip = collectionView.indexPathForItemAtPoint(center) {
        self.pageControl.currentPage = ip.row
    }
}

回答by Jorge Paiz

Definitely you need catch the visible item when the scroll movement is stopped. Use next code to do it.

当滚动移动停止时,您肯定需要捕获可见项目。使用下一个代码来做到这一点。

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if let indexPath = myCollectionView.indexPathsForVisibleItems.first {
        myPageControl.currentPage = indexPath.row
    }
}

回答by Ramdhas

  1. Place PageControlin your view or set by Code.
  2. Set UIScrollViewDelegate
  3. In Collectionview-> cellForItemAtIndexPath (Method) add the below code for calculate the Number of pages,

    int pages = floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width);
    [pageControl setNumberOfPages:pages];
    
  4. Add the ScrollView Delegatemethod,

    #pragma mark - UIScrollViewDelegate for UIPageControl
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        CGFloat pageWidth = ImageCollectionView.frame.size.width;
        float currentPage = ImageCollectionView.contentOffset.x / pageWidth;
    
        if (0.0f != fmodf(currentPage, 1.0f))
        {
            pageControl.currentPage = currentPage + 1;
        }
        else
        {
            pageControl.currentPage = currentPage;
        }
        NSLog(@"finishPage: %ld", (long)pageControl.currentPage);
    }
    
  1. PageControl放在您的视图中或由代码设置。
  2. 设置UIScrollViewDelegate
  3. Collectionview-> cellForItemAtIndexPath (Method) 中添加以下代码来计算页数,

    int pages = floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width);
    [pageControl setNumberOfPages:pages];
    
  4. 添加ScrollView 委托方法,

    #pragma mark - UIScrollViewDelegate for UIPageControl
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        CGFloat pageWidth = ImageCollectionView.frame.size.width;
        float currentPage = ImageCollectionView.contentOffset.x / pageWidth;
    
        if (0.0f != fmodf(currentPage, 1.0f))
        {
            pageControl.currentPage = currentPage + 1;
        }
        else
        {
            pageControl.currentPage = currentPage;
        }
        NSLog(@"finishPage: %ld", (long)pageControl.currentPage);
    }
    

回答by Hitesh Savaliya

I had similar situation where my flow layout was set for UICollectionViewScrollDirectionHorizontal and I was using page control to show the current page.

我有类似的情况,我的流布局是为 UICollectionViewScrollDirectionHorizo​​ntal 设置的,我使用页面控件来显示当前页面。

I achieved it using custom flow layout.

我使用自定义流布局实现了它。

/------------------------ Header file (.h) for custom header ------------------------/

/------------------------ 自定义标头的头文件 (.h) ---------------- --------/

/**
* The customViewFlowLayoutDelegate protocol defines methods that let you coordinate with
*location of cell which is centered.
*/

@protocol CustomViewFlowLayoutDelegate <UICollectionViewDelegateFlowLayout>

/** Informs delegate about location of centered cell in grid.
*  Delegate should use this location 'indexPath' information to 
*   adjust it's conten associated with this cell. 
*   @param indexpath of cell in collection view which is centered.
*/

- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout cellCenteredAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface customViewFlowLayout : UICollectionViewFlowLayout
@property (nonatomic, weak) id<CustomViewFlowLayoutDelegate> delegate;
@end

/------------------- Implementation file (.m) for custom header -------------------/

/------------------- 自定义标头的实现文件 (.m) -------------------/

@implementation customViewFlowLayout
- (void)prepareLayout {
 [super prepareLayout];
 }

static const CGFloat ACTIVE_DISTANCE = 10.0f; //Distance of given cell from center of visible rect
 static const CGFloat ITEM_SIZE = 40.0f; // Width/Height of cell.

- (id)init {
    if (self = [super init]) {
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.minimumInteritemSpacing = 60.0f;
    self.sectionInset = UIEdgeInsetsZero;
    self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
    self.minimumLineSpacing = 0;
}
    return self;
    }

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
   NSArray *attributes = [super layoutAttributesForElementsInRect:rect];

CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;

for (UICollectionViewLayoutAttributes *attribute in attributes) {
    if (CGRectIntersectsRect(attribute.frame, rect)) {

        CGFloat distance = CGRectGetMidX(visibleRect) - attribute.center.x;
        // Make sure given cell is center
        if (ABS(distance) < ACTIVE_DISTANCE) {
            [self.delegate collectionView:self.collectionView layout:self cellCenteredAtIndexPath:attribute.indexPath];
        }
    }
}
return attributes;
}

Your class containing collection view must conform to protocol 'CustomViewFlowLayoutDelegate' I described earlier in custom layout header file. Like:

包含集合视图的类必须符合我之前在自定义布局头文件中描述的协议“CustomViewFlowLayoutDelegate”。喜欢:

@interface MyCollectionViewController () <UICollectionViewDataSource, UICollectionViewDelegate, CustomViewFlowLayoutDelegate>
@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
@property (strong, nonatomic) IBOutlet UIPageControl *pageControl;
....
....
@end

There are two ways to hook your custom layout to collection view, either in xib OR in code like say in viewDidLoad:

有两种方法可以将自定义布局挂钩到集合视图,要么在 xib 中,要么在代码中,例如在 viewDidLoad 中说的:

customViewFlowLayout *flowLayout = [[customViewFlowLayout alloc]init];
flowLayout.delegate = self;
self.collectionView.collectionViewLayout = flowLayout;
self.collectionView.pagingEnabled = YES; //Matching your situation probably?

Last thing, in MyCollectionViewController implementation file, implement delegate method of 'CustomViewFlowLayoutDelegate'.

最后一件事,在 MyCollectionViewController 实现文件中,实现“CustomViewFlowLayoutDelegate”的委托方法。

- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout cellCenteredAtIndexPath:(NSIndexPath *)indexPath {
self.pageControl.currentPage = indexPath.row;

}

}

I hope this would be helpful. :)

我希望这会有所帮助。:)

回答by Mr Zee

for swift 4.2

快速 4.2

@IBOutlet weak var mPageControl: UIPageControl!
@IBOutlet weak var mCollectionSlider: UICollectionView!

private var _currentIndex = 0
private var T1:Timer!
private var _indexPath:IndexPath = [0,0]

private func _GenerateNextPage(){
    self._currentIndex = mCollectionSlider.indexPathForItem(at: CGPoint.init(x: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midX, y: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midY))?.item ?? 0
    self.mPageControl.currentPage = self._currentIndex
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    _SetTimer(AutoScrollInterval)
    _GenerateNextPage()
}

@objc private func _AutoScroll(){
    self._indexPath = IndexPath.init(item: self._currentIndex+1, section: 0)
    if !(self._indexPath.item < self.numberOfItems){
        _indexPath = [0,0]
    }
    self.mCollectionSlider.scrollToItem(at: self._indexPath, at: .centeredHorizontally, animated: true)
}
private func _SetTimer(_ interval:TimeInterval){
    if T1 == nil{
        T1 = Timer.scheduledTimer(timeInterval: interval , target:self , selector: #selector(_AutoScroll), userInfo: nil, repeats: true)
    }
}

you can skip the function _SetTimer() , thats for auto scroll

您可以跳过函数 _SetTimer() ,即自动滚动

回答by Onik IV

With UICollectionViewDelegate methods

使用 UICollectionViewDelegate 方法

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    pageControl.currentPage = indexPath.row
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if pageControl.currentPage == indexPath.row {
        pageControl.currentPage = collectionView.indexPath(for: collectionView.visibleCells.first!)!.row
    }
}

回答by Ahmed Safadi

Swift 5.1

斯威夫特 5.1

The easy way and more safety from nil crash

零崩溃的简单方法和更高的安全性

func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if collectionView == newsCollectionView {
        if newsPager.currentPage == indexPath.row {
            guard let visible = newsCollectionView.visibleCells.first else { return }
            guard let index = newsCollectionView.indexPath(for: visible)?.row else { return }
            newsPager.currentPage = index
        }

    }
}

回答by Hemang

Note- I have found andykkt'sansweruseful but since it is in obj-c converted it to swift and also implemented logic in another UIScrollViewdelegate for a smoother effect.

注意- 我发现andykkt 的答案很有用,但由于它在 obj-c 中将其转换为 swift 并且还在另一个UIScrollView委托中实现了逻辑以获得更平滑的效果。

func updatePageNumber() {
    // If not case to `Int` will give an error.
    let currentPage = Int(ceil(scrollView.contentOffset.x / scrollView.frame.size.width))
    pageControl.currentPage = currentPage
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    // This will be call when you scrolls it manually.
    updatePageNumber()
}

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    // This will be call when you scrolls it programmatically.
    updatePageNumber()
}

回答by Davender Verma

(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat pageWidth = _cvImagesList.frame.size.width;
    float currentPage = _cvImagesList.contentOffset.x / pageWidth;

     _pageControl.currentPage = currentPage + 1;
    NSLog(@"finishPage: %ld", (long)_pageControl.currentPage);
}