iyOmSd/Title: Swift

[Swift] - Simple Carousel Effect CollectionView With Animation

냄수 2020. 5. 8. 22:41
반응형

최근에 애니메이션에 관심이 많아져서 계속 애니메이션 관련글만 올리고있네요 ㅎㅎ

오늘 구현해볼 애니메이션이에요

 

 

대표적으로 음악앱을보면 앨범을 띄울때 이런 방식으로 띄우는걸 볼수있어요

이런 효과를 Carousel이라고 불러요

사전정의에는 '회전목마' 라고 하네요

이 애니메이션을 포함해서 다양한 Carousel효과가 많더라구요

 

 

처음이기때문에

자료를 찾아보면서 이것저것 해봤는데

좋은 코드가 있거나 방법이있다면

댓글달아주세요 😁

 

 

 

시작해볼까요..!

 

 

class ViewController: UIViewController {
    @IBOutlet weak var collectionView: UICollectionView!
    
    let cellSize = CGSize(width: 200, height: 500)
    var minItemSpacing: CGFloat = 20
    let cellCount = 8
    var previousIndex = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCollectionView()
    }
    
    func setupCollectionView() {
        collectionView.contentInsetAdjustmentBehavior = .never
        let cellWidth: CGFloat = floor(cellSize.width)
        let insetX = (view.bounds.width - cellWidth) / 2.0
        
        collectionView.contentInset = UIEdgeInsets(top: 0, left: insetX, bottom: 0, right: insetX)
        collectionView.decelerationRate = .fast
        collectionView.delegate = self
        collectionView.dataSource = self
    }
}

 

contentInsetAdjustmentBehavior

safe area때문에 가려지는 것을 방지하기위해서 자동으로 Inset을 조정해주는 역할을해요

때문에 스크롤뷰나, 컬랙션뷰등 스크롤있는 컴포넌트를 건들때 위의 공간이 남는 이유가 되기도하죠

 

FlowlayoutDelegate를 활용하거나

collectionView의 flowlayout을 변수로 가져올 수 있는데

이 둘을 섞어쓰면 변수값이 이상해지더라구요

 

아무튼 하나만 사용하세요..!

저는 전자를 택햇어요

 

 

 

collectionView의 item이 처음에 중앙에 와야 이쁘겟죠??

그래서 contentInset 작업을 해주고!!

 

뭐 빠르게 스크롤하겟다...delegate..datasource.. 기본적인걸 설정해줬어요

 

 

extension ViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return minItemSpacing
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return cellSize
    }
    
    // MARK: Paging Effect
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let cellWidthIncludeSpacing = cellSize.width + minItemSpacing
        
        var offset = targetContentOffset.pointee
        let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludeSpacing
        let roundedIndex: CGFloat = round(index)
        
        offset = CGPoint(x: roundedIndex * cellWidthIncludeSpacing - scrollView.contentInset.left, y: scrollView.contentInset.top)
        targetContentOffset.pointee = offset
    }
}

 

다음으로는 아이템간격을 조절해주고

셀사이즈를 지정해주고

 

페이징 효과가 나도록 할거에요!!

스토리보드에 있는

isPagingEnabled

를 사용하게되면 한페이지의 넓이를 조절 할 수가없어요!!

크기가 같다면 상관이없지만 다르기때문에

scrollViewWillEndDragging

을 이용해서 코드로 구현할거에요

 

 

해당 아이템의 index를 offset을 이용해서 쉽게 구할수있지만

저희는 inset을 줫기때문에

contentInset만큼 더해줘야해요

 

pageIndex X Cell한칸(spacing+cellWidth) - inset을 하면

한페이지의 넓이만큼 딱 움직이겟죠??

 

여기까지 구현했다면

단순한 가운데정렬 페이징을 하는 효과를 볼 수 있어요

 

 

 

    // MARK: Carousel Effect
    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let cellWidthIncludeSpacing = cellSize.width + minItemSpacing
        let offsetX = collectionView.contentOffset.x
        let index = (offsetX + collectionView.contentInset.left) / cellWidthIncludeSpacing
        let roundedIndex = round(index)
        let indexPath = IndexPath(item: Int(roundedIndex), section: 0)
        if let cell = collectionView.cellForItem(at: indexPath) {
            animateZoomforCell(zoomCell: cell)
        }
        
        if Int(roundedIndex) != previousIndex {
            let preIndexPath = IndexPath(item: previousIndex, section: 0)
            if let preCell = collectionView.cellForItem(at: preIndexPath) {
                animateZoomforCellremove(zoomCell: preCell)
            }
            previousIndex = indexPath.item
        }
    }

이제 Cell이 포커스되면 커지는 효과를 줘보려고해요

 

offset을 이용해서 현재 보여지는 index를 찾고

그 index에 있는 cell을 커지게

전의 cell은 원래 상태로 되돌리는 방식으로 구현해봤어요

 

 

    func animateZoomforCell(zoomCell: UICollectionViewCell) {
        UIView.animate(
            withDuration: 0.2,
            delay: 0,
            options: .curveEaseOut,
            animations: {
                zoomCell.transform = .identity
        },
            completion: nil)
    }
    
    func animateZoomforCellremove(zoomCell: UICollectionViewCell) {
        UIView.animate(
            withDuration: 0.2,
            delay: 0,
            options: .curveEaseOut,
            animations: {
                zoomCell.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        },
            completion: nil)
        
    }

 

 

초기상태 scale 0.5 -> identity로 커지는 효과

애니메이션은 간단하죠 ㅎㅎ

 

 

여기까지 하셨다면 처음에 봤던 애니메이션을 볼 수 있을거에요

구현하긴 어려운데 하고나면 너무 이쁜게 매력인거 같아요

 

 

반응형