'collectionView Animation on SwipUP?

I want to animate a collectionView on swipe on the basis of content offset. it's like a page controller but shows only one swipe. I have attached a video because I know the explanation is not good.

Link

you can download the video from there.



Solution 1:[1]

There can be several ways to do what you want, here is one way:

I am setting up a UIImageView along with an overlay UIView which is pinned to the main view's leading, trailing, top and bottom anchors.

My aim is to swipe up a collection view like your example and then change the color of the overlay view based on the cell tapped in the collection view.

Here are some vars and the initial set up:

class SwipeCollectionView: UIViewController
{
    private var collectionView: UICollectionView!
    
    private let imageView = UIImageView()
    
    private let overlayView = UIView()
    
    // Collection view data source
    private let colors: [UIColor] = [.red,
                                     .systemBlue,
                                     .orange,
                                     .systemTeal,
                                     .purple,
                                     .systemYellow]
    
    // Store the collection view's bottom anchor which is used
    // to animate the collection view
    private var cvBottomAnchor: NSLayoutConstraint?
    
    // Use this flag to disable swipe actions when carousel is showing
    private var isCarouselShowing = false

    // Use this to check if we are swiping up
    private var previousSwipeLocation: CGPoint?
    
    // Random width and height, change as you wish
    private let cellWidth: CGFloat = 100
    private let collectionViewHeight: CGFloat = 185
    
    private let reuseIdentifier = "cell"
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        configureNavigationBar()
        configureOverlayView()
        configureCollectionView()
    }
    
    private func configureNavigationBar()
    {
        title = "Swipe CV"
        
        let appearance = UINavigationBarAppearance()

        // Color of the nav bar background
        appearance.backgroundColor = .white // primary black for you

        navigationController?.navigationBar.standardAppearance = appearance
        navigationController?.navigationBar.scrollEdgeAppearance = appearance
    }

Now once some basic set up is done, here is how the image and the overlay view was set up. Nothing fancy happens here but pay attention to the gesture recognizer added to the overlay view

private func configureOverlayView()
{
    imageView.image = UIImage(named: "dog")
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFill
    
    overlayView.backgroundColor = colors.first!.withAlphaComponent(0.5)
    overlayView.translatesAutoresizingMaskIntoConstraints = false
    
    view.addSubview(imageView)
    view.addSubview(overlayView)
    
    // Auto layout pinning the image view and overlay view
    // to the main container view
    view.addConstraints([
    
        imageView.leadingAnchor
            .constraint(equalTo: view.leadingAnchor),
        imageView.topAnchor
            .constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        imageView.trailingAnchor
            .constraint(equalTo: view.trailingAnchor),
        imageView.bottomAnchor
            .constraint(equalTo: view.bottomAnchor),
        
        overlayView.leadingAnchor
            .constraint(equalTo: view.leadingAnchor),
        overlayView.topAnchor
            .constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        overlayView.trailingAnchor
            .constraint(equalTo: view.trailingAnchor),
        overlayView.bottomAnchor
            .constraint(equalTo: view.bottomAnchor)
    
    ])
    
    // We will observe a swipe gesture to check if it is a swipe
    // upwards and then react accordingly
    let swipeGesture = UIPanGestureRecognizer(target: self,
                                              action: #selector(didSwipe(_:)))
    overlayView.addGestureRecognizer(swipeGesture)
}

Now once that is done, we need to create a horizontal scrolling uicollectionview that is positioned off screen which can be animated in when we swipe up, here is how this is done

private func configureCollectionView()
{
    collectionView = UICollectionView(frame: .zero,
                                      collectionViewLayout: createLayout())
    
    collectionView.backgroundColor = .clear
    
    collectionView.register(UICollectionViewCell.self,
                            forCellWithReuseIdentifier: reuseIdentifier)
    
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    
    collectionView.showsHorizontalScrollIndicator = false
    
    // Add some padding for the content on the left
    collectionView.contentInset = UIEdgeInsets(top: 0,
                                               left: 15,
                                               bottom: 0,
                                               right: 0)
    
    collectionView.dataSource = self
    collectionView.delegate = self
    
    overlayView.addSubview(collectionView)
    
    // Collection View should start below the screen
    // We need to persist with this constraint so we can change it later
    
    let bottomAnchor = overlayView.safeAreaLayoutGuide.bottomAnchor
    
    cvBottomAnchor
        = collectionView.bottomAnchor.constraint(equalTo: bottomAnchor,
                                                 constant: collectionViewHeight)
    
    // Collection View starts as hidden and will be animated in swipe up
    collectionView.alpha = 0.0
    
    // Add collection view constraints
    overlayView.addConstraints([
    
        collectionView.leadingAnchor.constraint(equalTo: overlayView.leadingAnchor),
        cvBottomAnchor!,
        collectionView.trailingAnchor.constraint(equalTo: overlayView.trailingAnchor),
        collectionView.heightAnchor.constraint(equalToConstant: collectionViewHeight)
    
    ])
}

private func createLayout() -> UICollectionViewFlowLayout
{
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    
    layout.itemSize = CGSize(width: cellWidth, height: collectionViewHeight)
    layout.minimumInteritemSpacing = 20
    
    return layout
}

The code up to this point should give you something like this:

UIImageView with Overlay

You still do not see the collection view and for that, implement the didSwipe action of the pan gesture

@objc
private func didSwipe(_ gesture: UIGestureRecognizer)
{
    if !isCarouselShowing
    {
        let currentSwipeLocation = gesture.location(in: view)
        
        if gesture.state == .began
        {
            // record the swipe location when we start the pan gesture
            previousSwipeLocation = currentSwipeLocation
        }
        
        // On swipe continuation, verify the swipe is in the upward direction
        if gesture.state == .changed,
           let previousSwipeLocation = previousSwipeLocation,
           currentSwipeLocation.y < previousSwipeLocation.y
        {
            isCarouselShowing = true
            revealCollectionView()
        }
    }
}

// Animate the y position of the collection view and the alpha
private func revealCollectionView()
{
    // We need to set the top constraint (y position)
    // to be somewhere above the screen plus some padding
    cvBottomAnchor?.constant = 0 - 75
    
    UIView.animate(withDuration: 0.25) { [weak self] in
        
        // animate change in constraints
        self?.overlayView.layoutIfNeeded()
        
        // reveal the collection view
        self?.collectionView.alpha = 1.0
        
    } completion: { (finished) in
        
        // do something
    }
}

Finally, just for completeness, here is the collection view data source and delegate:

extension SwipeCollectionView: UICollectionViewDataSource
{
    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int
    {
        return colors.count
    }
    
    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
        let cell
            = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier,
                                                      for: indexPath)
        
        cell.layer.cornerRadius = 20
        cell.clipsToBounds = true
        cell.contentView.backgroundColor = colors[indexPath.item]
        
        return cell
    }
}

extension SwipeCollectionView: UICollectionViewDelegate
{
    func collectionView(_ collectionView: UICollectionView,
                        didSelectItemAt indexPath: IndexPath)
    {
        overlayView.backgroundColor
            = colors[indexPath.item].withAlphaComponent(0.5)
    }
}

Now running this should give you this experience when you swipe up:

UICollectionView Autolayout Constraints Animation Reveal Swipe Pan Gesture Recognizer Swift iOS

I believe this should be enough to get you started

Update

The full source code of the example is available on a gist here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1