0

I am using swift 3 to write an app which has a UIViewController looks like the following picture. It contains a horizontal-scrollable collectionView (the blue block) on the top, and its functionality is to switch between different labels(first, prev, current, next, last...etc, the total amount of labels are not fixed). What I need to achieve is that on scrolling (or panning) the collection view, the 'NEXT' or 'PREV' should move (and anchor automatically) to the center with velocity similar to paging animation. How could I achieve this? Currently I use the Paging Enabled attribute of the scrollView, however this only work when there is one label in the window.

Similar features may look like the middle "albums" section of iTunes app, which the collectionView cell would scroll to some pre-defined point automatically with some animation once it detects any swipe/pan gesture.

enter image description here

whitney13625
  • 583
  • 1
  • 9
  • 28

2 Answers2

1

according to your comment if you want to do the Tap to select thing there are 2 things you need to do

1- Disable Collection View Scrolling in viewDidLoad

collectionView.isScrollingEnabled = false 

2- Display Selected Cell in Center , add this in your didSelectItemAtIndexPath method

 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

collection.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizantally)

let cell  = collectionView.cellForItem(at : indexPath) as! YourCollectionViewCellClass
cell.titleLable.font = UIFont.boldSystemFont(ofSize : 20)
}
Abdul Waheed
  • 863
  • 8
  • 14
1

Thanks to Raheel's answer here, I finally find a way for a desirable scroll effect. Some key functions are listed as follow in swift:

  1. First, disable Paging Enabled attribute of the scrollView
  2. Define setIdentityLayout() and setNonIdentityLayout() which defines the layouts of both selected / non-selected cell (for example, MyCollectionViewCell in this case)

  3. Define some related properties such as:

    lazy var COLLECTION_VIEW_WIDTH: CGFloat = {
        return CGFloat(self.collectionView.frame.size.width / 2)
    }()
    
    fileprivate let TRANSFORM_CELL_VALUE = CGAffineTransform(scaleX: 1, y: 1) // Or define other transform effects here
    fileprivate let ANIMATION_SPEED = 0.2
    
  4. implement the following key methods for UIScrollViewDelegate:

    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        // Do tableView data refresh according to the corresponding pages here
    }
    
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    
        // Scroll to corresponding position
        let pageWidth: Float = Float(COLLECTION_VIEW_WIDTH)  // 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)
    
        // Set transforms
        let identityIndex: Int = Int(newTargetOffset / pageWidth)
    
        var cell = delegate!.roomCollections.cellForItem(at: IndexPath.init(row: identityIndex, section: 0)) as? MyCollectionViewCell
    
        UIView.animate(withDuration: ANIMATION_SPEED, animations: {
            cell?.transform = CGAffineTransform.identity
            cell?.setIdentityLayout()
        })
    
        // right cell
        cell = delegate!.roomCollections.cellForItem(at: IndexPath.init(row: identityIndex + 1, section: 0)) as? MyCollectionViewCell
    
        UIView.animate(withDuration: ANIMATION_SPEED, animations: {
            cell?.transform = self.TRANSFORM_CELL_VALUE
            cell?.setNonIdentityLayout()
        })
    
        // left cell, which is not necessary at index 0
        if (identityIndex != 0) { 
            cell = delegate!.roomCollections.cellForItem(at: IndexPath.init(row: identityIndex - 1, section: 0)) as? MyCollectionViewCell
            UIView.animate(withDuration: ANIMATION_SPEED, animations: {
                cell?.transform = self.TRANSFORM_CELL_VALUE
                cell?.setNonIdentityLayout()
            })
        }
    }
    
  5. Finally, define initial layout in func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath):

    if (indexPath.row == 0 && /* other first item selected condition */) {
        cell.setIdentityLayout()
    } else {
        cell.transform = TRANSFORM_CELL_VALUE 
        cell.setNonIdentityLayout()
    }
    

To add touch scroll effect, simply add target to each collection views, and do similar calculation which changes the contentOffSet of the scrollView, which is omitted here because this feature is simpler and not the primary question.

whitney13625
  • 583
  • 1
  • 9
  • 28