【问题标题】:UICollectionView horizontal paging with 3 itemsUICollectionView 水平分页 3 项
【发布时间】:2014-04-29 11:19:19
【问题描述】:

我需要在 UICollectionView 中显示 3 个项目,并像这样启用分页

但我越来越像这样了

我已经制作了自定义流程,并且启用了分页但无法获得我需要的内容。我怎样才能做到这一点,或者我应该调查哪个代表,或者将我引导到一些链接,我可以从那里获得针对这种情况的帮助。

- (void)awakeFromNib
{
    self.itemSize = CGSizeMake(480, 626);
    self.minimumInteritemSpacing = 112;
    self.minimumLineSpacing = 112;
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.sectionInset = UIEdgeInsetsMake(0, 272, 0, 272);
}

【问题讨论】:

    标签: ios ipad uicollectionview


    【解决方案1】:

    编辑: 演示链接:https://github.com/raheelsadiq/UICollectionView-horizontal-paging-with-3-items

    经过大量搜索后,我找到了下一个滚动点并禁用分页。在 scrollviewWillEndDragging 中滚动到下一个单元格 x。

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
    {
    
        float pageWidth = 480 + 50; // width + space
    
        float currentOffset = scrollView.contentOffset.x;
        float targetOffset = targetContentOffset->x;
        float newTargetOffset = 0;
    
        if (targetOffset > currentOffset)
            newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
        else
            newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;
    
        if (newTargetOffset < 0)
            newTargetOffset = 0;
        else if (newTargetOffset > scrollView.contentSize.width)
            newTargetOffset = scrollView.contentSize.width;
    
        targetContentOffset->x = currentOffset;
        [scrollView setContentOffset:CGPointMake(newTargetOffset, scrollView.contentOffset.y) animated:YES];
    }
    

    我还必须使左右小而中心大,所以我用变换来做到这一点。 问题是找到索引,所以很难找到。

    对于在同一方法中进行左右变换,请使用 newTargetOffset

    int index = newTargetOffset / pageWidth;
    
    if (index == 0) { // If first index 
        UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index  inSection:0]];
    
        [UIView animateWithDuration:ANIMATION_SPEED animations:^{
            cell.transform = CGAffineTransformIdentity;
        }];
        cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index + 1  inSection:0]];
        [UIView animateWithDuration:ANIMATION_SPEED animations:^{
            cell.transform = TRANSFORM_CELL_VALUE;
        }];
    }else{
        UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
        [UIView animateWithDuration:ANIMATION_SPEED animations:^{
            cell.transform = CGAffineTransformIdentity;
        }];
    
        index --; // left
        cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
        [UIView animateWithDuration:ANIMATION_SPEED animations:^{
            cell.transform = TRANSFORM_CELL_VALUE;
        }];
    
        index ++;
        index ++; // right
        cell = [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
        [UIView animateWithDuration:ANIMATION_SPEED animations:^{
            cell.transform = TRANSFORM_CELL_VALUE;
        }];
    }
    

    并在 cellForRowAtIndex 中添加

    if (indexPath.row == 0 && isfirstTimeTransform) { // make a bool and set YES initially, this check will prevent fist load transform
        isfirstTimeTransform = NO;
    }else{
        cell.transform = TRANSFORM_CELL_VALUE; // the new cell will always be transform and without animation 
    }
    

    也添加这两个宏或者你希望同时处理这两个宏

    #define TRANSFORM_CELL_VALUE CGAffineTransformMakeScale(0.8, 0.8)
    #define ANIMATION_SPEED 0.2
    

    最终结果是

    【讨论】:

    • 如果我们可以转到 stackoverflow 的聊天部分,我将不胜感激
    • 你能告诉我你是用什么方法写的“左右变换”边的代码
    • 我迫切需要这种功能。你愿意帮我吗?
    • 任何人都可以有 iPhone 6 和 6plus 的解决方案吗?我尝试了这段代码,但无法根据设备大小设置正确的逻辑..
    • @Wolverine 你得到 iPhone 6 和 6 Plus 的解决方案了吗?
    【解决方案2】:

    @Raheel SadiqSwift 3 中回答的第一部分,没有转换。

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
            let pageWidth: Float = Float(self.collectionView.frame.width / 3) //480 + 50
            // width + space
            let currentOffset: Float = Float(scrollView.contentOffset.x)
            let targetOffset: Float = Float(targetContentOffset.pointee.x)
            var newTargetOffset: Float = 0
            if targetOffset > currentOffset {
                newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
            }
            else {
                newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
            }
            if newTargetOffset < 0 {
                newTargetOffset = 0
            }
            else if (newTargetOffset > Float(scrollView.contentSize.width)){
                newTargetOffset = Float(Float(scrollView.contentSize.width))
            }
    
            targetContentOffset.pointee.x = CGFloat(currentOffset)
            scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: scrollView.contentOffset.y), animated: true)
    
        }
    

    【讨论】:

    • 你的回答很有帮助
    • 这对于使分页正常工作非常有帮助。非常感谢。
    【解决方案3】:

    Swift 3.0 基于Raheel Sadiq的完整解决方案

    var isfirstTimeTransform:Bool = true
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    
        let cell : UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCustomViewCell", for: indexPath)
    
        if (indexPath.row == 0 && isfirstTimeTransform) { 
            isfirstTimeTransform = false
        }else{
            cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) 
        }
    
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
        return CGSize(width: collectionView.bounds.width/3, height: collectionView.bounds.height)
    }
    
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        // Simulate "Page" Function
        let pageWidth: Float = Float(self.collectionView.frame.width/3 + 20)
        let currentOffset: Float = Float(scrollView.contentOffset.x)
        let targetOffset: Float = Float(targetContentOffset.pointee.x)
        var newTargetOffset: Float = 0
        if targetOffset > currentOffset {
            newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth
        }
        else {
            newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth
        }
        if newTargetOffset < 0 {
            newTargetOffset = 0
        }
        else if (newTargetOffset > Float(scrollView.contentSize.width)){
            newTargetOffset = Float(Float(scrollView.contentSize.width))
        }
    
        targetContentOffset.pointee.x = CGFloat(currentOffset)
        scrollView.setContentOffset(CGPoint(x: CGFloat(newTargetOffset), y: scrollView.contentOffset.y), animated: true)
    
        // Make Transition Effects for cells
        let duration = 0.2
        var index = newTargetOffset / pageWidth;
        var cell:UICollectionViewCell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0))!
        if (index == 0) { // If first index
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform.identity
            }, completion: nil)
            index += 1
            cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0))!
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
            }, completion: nil)
        }else{
            UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                cell.transform = CGAffineTransform.identity;
            }, completion: nil)
    
            index -= 1 // left
            if let cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0)) {
                UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                    cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8);
                }, completion: nil)
            }
    
            index += 1
            index += 1 // right
            if let cell = self.collectionView.cellForItem(at: IndexPath(row: Int(index), section: 0)) {
                UIView.animate(withDuration: duration, delay: 0.0, options: [ .curveEaseOut], animations: {
                    cell.transform = CGAffineTransform(scaleX: 0.8, y: 0.8);
                }, completion: nil)
            }
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      @raheel-sadiq 的答案很好,但我认为很难理解。在我看来,这是一个可读性很强的版本:

       func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint,
                                         targetContentOffset: UnsafeMutablePointer<CGPoint>) {
      
              //minimumLineSpacing and insetForSection are two constants in my code
      
              //this cell width is for my case, adapt to yours
              let cellItemWidth = view.frame.width - (insetForSection.left + insetForSection.right)
              let pageWidth = Float(cellItemWidth + minimumLineSpacing)
      
              let offsetXAfterDragging = Float(scrollView.contentOffset.x)
              let targetOffsetX = Float(targetContentOffset.pointee.x)
      
              let pagesCountForOffset = pagesCount(forOffset: offsetXAfterDragging, withTargetOffset: targetOffsetX, pageWidth: pageWidth)
      
              var newTargetOffsetX = pagesCountForOffset * pageWidth
      
              keepNewTargetInBounds(&newTargetOffsetX, scrollView)
      
              //ignore target
              targetContentOffset.pointee.x = CGFloat(offsetXAfterDragging)
      
              let newTargetPoint = CGPoint(x: CGFloat(newTargetOffsetX), y: scrollView.contentOffset.y)
              scrollView.setContentOffset(newTargetPoint, animated: true)
      
              //if you're using pageControl
              pageControl.currentPage = Int(newTargetOffsetX / pageWidth)
      
          }
      
          fileprivate func pagesCount(forOffset offset: Float, withTargetOffset targetOffset: Float, pageWidth: Float) -> Float {
              let isRightDirection = targetOffset > offset
              let roundFunction = isRightDirection ? ceilf : floorf
              let pagesCountForOffset = roundFunction(offset / pageWidth)
              return pagesCountForOffset
          }
      
          fileprivate func keepNewTargetInBounds(_ newTargetOffsetX: inout Float, _ scrollView: UIScrollView) {
              if newTargetOffsetX < 0 { newTargetOffsetX = 0 }
              let contentSizeWidth = Float(scrollView.contentSize.width)
              if newTargetOffsetX > contentSizeWidth { newTargetOffsetX = contentSizeWidth }
          }
      

      【讨论】:

      • 很好的答案.. 但就我而言,它只是显示下一个单元格的某些部分。但不是以前的单元格。你能帮我解决这个问题吗?
      【解决方案5】:

      您必须重写流布局的 targetContentOffsetForProposedContentOffset:withScrollingVelocity: 方法。这样你就可以捕捉到滚动视图的停止点。

      -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
      {
          CGFloat yOffset = MAXFLOAT;
      
          CGRect proposedRect;
          proposedRect.origin = proposedContentOffset;
          proposedRect.size = self.collectionView.bounds.size;
          CGPoint proposedCenterPoint = CGPointMake(CGRectGetMidX(proposedRect), CGRectGetMidY(proposedRect)) ;
      
          NSArray *array = [super layoutAttributesForElementsInRect:proposedRect];
      
          for (UICollectionViewLayoutAttributes *attributes in array)
          {
              CGFloat newOffset = attributes.center.y - proposedCenterPoint.y;
              if ( fabsf(newOffset) < fabs(yOffset))
              {
                  yOffset = newOffset;
              }
          }
      
          return CGPointMake(proposedContentOffset.x, proposedContentOffset.y + yOffset);
      }
      

      您还需要将流布局的 sectionInset 设置为使第一个单元格和最后一个单元格居中。我的例子是高度但很容易切换到宽度。

      CGFloat height = (self.collectionView.bounds.size.height / 2.0 ) - (self.itemSize.height / 2.0) ;
      self.sectionInset = UIEdgeInsetsMake(height, 30.0, height, 30.0) ;
      

      【讨论】:

      • 我也发现了这个方法,它没有帮助,proposedContentOffset.x 总是 1024(ipad) 的倍数,这没有帮助,我需要第二个单元格,让第一个单元格在 x = 272间距为 112,宽度必须为 864。但我没有得到索引,所以我可以计算,任何其他想法或改进这个
      • 是的,我已经设置了部分插图,我已经附加了有问题的自定义流程
      • 打开分页后,滚动视图计算页面。如果您希望单元格位于中心,则必须关闭分页。然后计算离中心最近的单元格并返回该点。
      【解决方案6】:

      我的水平集合视图分页解决方案

      func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
          if scrollView == collectionView { collectionView.scrollToPage() }
      }
      
      func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
          if scrollView == collectionView { if !decelerate { collectionView.scrollToPage() } }
      }
      

      collectionView 的小扩展

      public func scrollToPage() {
              var currentCellOffset = contentOffset
              currentCellOffset.x += width / 2
              var path = indexPathForItem(at: currentCellOffset)
              if path.isNil {
                  currentCellOffset.x += 15
                  path = indexPathForItem(at: currentCellOffset)
              }
              if path != nil {
                  logInfo("Scrolling to page \(path!)")
                  scrollToItem(at: path!, at: .centeredHorizontally, animated: true)
              }
          }
      

      【讨论】:

      • 嗨,我们为什么要添加 currentCellOffset.x += 15?
      • @Ishika 对不起,它对我来说也不是很好的代码,肯定有一些原因我使用它仍然可以正常工作,但为什么我把它放在那里......如果你删除它并且它工作在这里写它;)希望下次我也会为我写评论或更好地命名变量,这样更有意义......
      • @Ishika Ou 我想我现在明白了。这是因为您想要视图中间的下一个项目的索引路径...所以确切的一半很麻烦...我不确定滚动反词时是否应该没有 -15 但这会使整个解决方案更加复杂。
      猜你喜欢
      • 1970-01-01
      • 2016-10-04
      • 2013-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-17
      • 1970-01-01
      • 2020-11-20
      相关资源
      最近更新 更多