UICollectionView Cell滚动到中心

时间:2014-06-02 09:05:30

标签: ios objective-c uicollectionviewcell uicollectionviewlayout

我在UIViewController中使用UICollectionView。

我的collectionview属性设置如下。

enter image description here

现在我希望滚动后单元格在屏幕上居中! 选项1:

enter image description here

选项2: enter image description here

我需要做什么来实现选项2?

更新

最后我使用了以下代码作为滚动,其他答案并不顺利。

  - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{    CGFloat offsetAdjustment = MAXFLOAT;
    CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);

    CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
    NSArray* array = [super layoutAttributesForElementsInRect:targetRect];

    for (UICollectionViewLayoutAttributes* layoutAttributes in array) {
        CGFloat itemHorizontalCenter = layoutAttributes.center.x;

        if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {
            offsetAdjustment = itemHorizontalCenter - horizontalCenter;
        }
    }    
    return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

7 个答案:

答案 0 :(得分:15)

您可以覆盖targetContentOffsetForProposedContentOffset:withScrollingVelocity:子类中的UICollectionViewLayout方法,并像这样计算您的偏移量:

@property (nonatomic, assign) CGFloat previousOffset;
@property (nonatomic, assign) NSInteger currentPage;

...

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    NSInteger itemsCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];

    // Imitating paging behaviour
    // Check previous offset and scroll direction
    if ((self.previousOffset > self.collectionView.contentOffset.x) && (velocity.x < 0.0f)) {
        self.currentPage = MAX(self.currentPage - 1, 0);
    } else if ((self.previousOffset < self.collectionView.contentOffset.x) && (velocity.x > 0.0f)) {
        self.currentPage = MIN(self.currentPage + 1, itemsCount - 1);
    }

    // Update offset by using item size + spacing
    CGFloat updatedOffset = (self.itemSize.width + self.minimumInteritemSpacing) * self.currentPage;
    self.previousOffset = updatedOffset;

    return CGPointMake(updatedOffset, proposedContentOffset.y);
}

编辑:感谢您指出这一点,忘了说您必须先禁用分页:

self.collectionView.pagingEnabled = NO;

更新:附加Swift 4.2版本

...
collectionView.isPagingEnabled = false
...

class YourCollectionLayoutSubclass: UICollectionViewFlowLayout {

    private var previousOffset: CGFloat = 0
    private var currentPage: Int = 0

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        guard let collectionView = collectionView else {
            return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        }

        let itemsCount = collectionView.numberOfItems(inSection: 0)

        // Imitating paging behaviour
        // Check previous offset and scroll direction
        if previousOffset > collectionView.contentOffset.x && velocity.x < 0 {
            currentPage = max(currentPage - 1, 0)
        } else if previousOffset < collectionView.contentOffset.x && velocity.x > 0 {
            currentPage = min(currentPage + 1, itemsCount - 1)
        }

        // Update offset by using item size + spacing
        let updatedOffset = (itemSize.width + minimumInteritemSpacing) * CGFloat(currentPage)
        previousOffset = updatedOffset

        return CGPoint(x: updatedOffset, y: proposedContentOffset.y)
    }
}

答案 1 :(得分:9)

你可以使用代码 self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)

答案 2 :(得分:6)

这是@ dmitry-zhukov的Swift 3版本(谢谢顺便说一句!)

class PagedCollectionLayout : UICollectionViewFlowLayout {

    var previousOffset : CGFloat = 0
    var currentPage : CGFloat = 0

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

        let sup = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)

        guard
            let validCollection = collectionView,
            let dataSource = validCollection.dataSource
            else { return sup }

        let itemsCount = dataSource.collectionView(validCollection, numberOfItemsInSection: 0)

        // Imitating paging behaviour
        // Check previous offset and scroll direction
        if  (previousOffset > validCollection.contentOffset.x) && (velocity.x < 0) {
            currentPage = max(currentPage - 1, 0)
        }
        else if (previousOffset < validCollection.contentOffset.x) && (velocity.x > 0) {
            currentPage = min(currentPage + 1, CGFloat(itemsCount - 1))
        }

        // Update offset by using item size + spacing
        let updatedOffset = ((itemSize.width + minimumInteritemSpacing) * currentPage)
        self.previousOffset = updatedOffset

        let updatedPoint = CGPoint(x: updatedOffset, y: proposedContentOffset.y)

        return updatedPoint
    }
}

答案 3 :(得分:1)

我发现了很多信息和解决方案。

现在,我用这个。

关于UICollectionViewFlowLayout覆盖:

override public func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

    if display != .inline {
        return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
    }

    guard let collectionView = collectionView else {
        return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
    }

    let willtoNextX: CGFloat

    if proposedContentOffset.x <= 0 || collectionView.contentOffset == proposedContentOffset {
        willtoNextX = proposedContentOffset.x
    } else {

        let width = collectionView.bounds.size.width
        willtoNextX = collectionView.contentOffset.x + (velocity.x > 0 ?  width : -width)
    }

    let targetRect = CGRect(x: willtoNextX, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)

    var offsetAdjustCoefficient = CGFloat.greatestFiniteMagnitude

    let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left

    let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)

    layoutAttributesArray?.forEach({ (layoutAttributes) in
        let itemOffset = layoutAttributes.frame.origin.x
        if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustCoefficient)) {
            offsetAdjustCoefficient = itemOffset - horizontalOffset
        }
    })

    return CGPoint(x: proposedContentOffset.x + offsetAdjustCoefficient, y: proposedContentOffset.y)
}

以及在UICollectionViewController上:

collectionView.decelerationRate = .fast
collectionView.isPagingEnabled = false
collectionView.contentInset = UIEdgeInsets.init(top: 0, left: 16, bottom: 0, right: 16)

现在,牢房在中心!

preview

答案 4 :(得分:0)

我不知道为什么每个人的答案都是如此复杂,我只需在Interface Builder中打开 {{#if_equal x "my_string"}} x is "my_string" {{else}} x isn't "my_string" {{/if_equal}} 即可。

答案 5 :(得分:0)

设置了适当的itemSizeleftright插入后,我宁愿这样做,而不是对布局进行子类化

//Setting decelerationRate to fast gives a nice experience
collectionView.decelerationRate = .fast

//Add this to your view anywhere
func centerCell () {
    let centerPoint = CGPoint(x: collectionView.contentOffset.x + collectionView.frame.midX, y: 100)
    if let path = collectionView.indexPathForItem(at: centerPoint) {
        collectionView.scrollToItem(at: path, at: .centeredHorizontally, animated: true)
    }
}

//Set collectionView.delegate = self then add below funcs
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
   centerCell()
}
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    centerCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate {
        centerCell()
    }
}

答案 6 :(得分:0)

我的像分页一样滚动的解决方案。

http://XXX.XXX.XXX.com/app/kibana#/discover/450afff0-786c-11e8-9b62-1100221a2566?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-7d,mode:quick,to:now))&_a=(columns:!(msg,req.headers.authorization),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'platform-js*',key:msg,negate:!f,value:'request%20finish'),query:(match:(msg:(query:'request%20finish',type:phrase))))),index:'platform-js*',interval:auto,query:(query_string:(analyze_wildcard:!t,query:'name:%22reporting-js_access%22%20%20AND%20req.url:(*report*%20OR%20*advertisers*)')),sort:!('@timestamp',desc))