개발을 시작하는 이야기

CollectionView를 활용한 이미지 슬라이드 적용 하기 본문

개발 이야기/Swift

CollectionView를 활용한 이미지 슬라이드 적용 하기

Teiresias 2022. 6. 13. 18:46

면접에서 내 프로젝트들을 보시곤 이미지 슬라이드를 해본적은 없냐는 질문을 받아서 아직 이미지를 대량으로 받은 적이 없어서 적용을 안해보았고, 웹으로는 해보았다고 말씀드렸었다. 그래서 이번에 프로젝트를 생각할 때 슬라이드를 해봐야 겠다고 생각했지만, 웹과 앱은 달랐기 때문에 제로 베이스에서 다시 생각해서 작성했다.


일단 웹에서 슬라이드를 만들 때는 이미지 만큼의 div를 만들어서 회전시키는 방식으로 작성했는데, 앱에서는 그렇게 해주기 보다는 CollectionView를 사용하기로 했다. 그래서 일단은 ViewController에 CollectionView, 그리고 슬라이드 위치를 표시해줄 ProgressView를 만들어주었다.

private var collectionView: UICollectionView = {
    let flowLayout = UICollectionViewFlowLayout()
    flowLayout.minimumLineSpacing = 0
    flowLayout.minimumInteritemSpacing = 0
    flowLayout.scrollDirection = .horizontal

    let view = UICollectionView(frame: .zero,
                                collectionViewLayout: flowLayout)
    view.translatesAutoresizingMaskIntoConstraints = false
    view.isPagingEnabled = true
    view.showsHorizontalScrollIndicator = false
    return view
}()

private var progressView: UIProgressView = {
   let view = UIProgressView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.trackTintColor = .gray
    view.progressTintColor = .white
    return view
}()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    setupView()
}

private func setupView() {
    view.addSubview(collectionView)
    view.addSubview(progressView)

    collectionView.register(collectionViewCell.self,
                            forCellWithReuseIdentifier: collectionViewCell.reuseIdentifier)
    collectionView.dataSource = self
    collectionView.delegate = self

    NSLayoutConstraint.activate([
        collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        collectionView.heightAnchor.constraint(equalToConstant: 300),

        progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        progressView.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor, constant:  -20),
        progressView.widthAnchor.constraint(equalToConstant: view.frame.width * 0.8)
    ])
}

그리고 뷰에 들어갈 데이터를 만들어 주었다. 원래는 Cell에 ImageView를 넣어 데이터 통신을 거쳐 받아온 이미지를 넣어주어야 하지만 이번에는 Cell의 배경 색상에 색상을 변경하는 방식으로 작성했다. Cell에 들어갈 색상을 colors 배열에 작성해 넣어 주었다.

//MARK: - Data
let colors: [UIColor] = [.red, .orange, .yellow, .green, .systemBlue, .blue, .purple]

이제 CollectionView를 설정해 주어야 하는데 이미지 슬라이더를 만들때 고려해야 하는것은 최초 그리고 마지막 슬라이드이다. 최초 슬라이드에선 좌측으로 넘어갈때를, 마지막 슬라이드에서는 우측으로 넘어갈때를 고려해주어야 한다. 그래서 방식을 기존의 웹에서 적용했던 방식을 차용해서 만들어 주었다.

 

슬라이드를 만들때, 이미지의 양의 3배수로 작성해서 처음 보여주는 슬라이드를 중간의 2번째 세트의 슬라이드 부터 보여주는 방식으로 작성해준다. 그렇게 되면 로드하고 최초 슬라이드에서 좌측으로 넘어가도, 마지막 슬라이드 에서도 모두 정상적으로 이동할 수 있다. 다만 3개의 슬라이드 세트중 처음과 마지막 세트로 진입하면 각각 앞과 뒤에 새로운 슬라이드 세트를 각각 추가해주기만 하면 된다.

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let color = colors[indexPath.item % colors.count]
        guard let item = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCell.reuseIdentifier, for: indexPath) as? collectionViewCell else { return UICollectionViewCell() }
        
        item.backgroundColor = color
        return item
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return colors.count * 3
    }
}

CollectionVIew 사용을 위해 DateSource를 작성해주었다. 슬라이드가 될 셀의 갯수에 * 3을 해주어서 3개의 세트를 작성해주었다.

 

그러면 이제 화면을 로드할때 2번째 세트의 슬라이드를 보여주어야 한다. 그래서 찾은 메소드가 scrollToItem(at:at:animated:) 이다.

 

Apple Developer Documentation

 

developer.apple.com

이 메서드를 그냥 사용하면 에러가 발생했다. 생각해보면 당연한 문제였다. 아직 Cell이 다 로드되지 않은 상황에서 이동을 시키기 때문이다. 그래서 이 메서드가 실행되는 타이밍을 정해주어야 했는데, view의 하위view가 로드됨을 알려주는 viewDidLayoutSubviews를 활용해서 해결해 주었다. 

 

Apple Developer Documentation

 

developer.apple.com

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    let segmentSize = colors.count
    collectionView.scrollToItem(at: IndexPath(item: segmentSize, section: 0),
                              at: .centeredHorizontally,
                              animated: false)
}

글이 좀 길어지는것 같아서 ProgressView 설정부터는 다음번 글로 이어서 작성! 😜

 

한번에 코드로 보고 싶으신 분들은 Github에 코드 전문을 올려두었습니다.