관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 가로 슬라이드 뷰 (SliderCollectionView) 구현 본문

UI 컴포넌트 (swift)

[iOS - swift] 가로 슬라이드 뷰 (SliderCollectionView) 구현

jake-kim 2021. 12. 15. 00:36

SliderCollectionView

  • Grid모양의 CollectionView에서 해당 셀을 선택하면, 해당하는 셀에 대한 정보를 두 번째 ViewController에 넘기고, SliderCollectionView로 정보를 표출

구현 완료된 슬라이드 뷰
디바이스 회전 대응

편리함을 위해서 사용한 프레임워크 사용방법 참고

SliderCollectionView 구현 핵심

  • 화면: ImageViewController(Grid형태의 CollectionView) -> DetailViewController(Slider형태의 CollectionView)
    • 첫 번째 Grid 형태의 CollectionView가 있는 ImageViewController에서 Cell을 탭하면 해당하는 Cell의 정보를 두 번째 ViewController에게 넘기고 여기서 SliderCollectionView 표출
  • 커스텀 CollectionViewCell은 단순히 ImageView가 있고, contentMode는 scaleAspectFill로하고 clipsToBounds를 on한 상태
    // SliderCollectionViewCell.swift
    
    import UIKit
    import Reusable
    import SnapKit
    
    final class SliderCollectionViewCell: UICollectionViewCell, Reusable {
      // MARK: UI
      private let photoImageView = UIImageView().then {
        $0.contentMode = .scaleAspectFill
        $0.clipsToBounds = true
      }
      
      // MARK: Initializers
      override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.contentView.addSubview(self.photoImageView)
        self.photoImageView.snp.makeConstraints {
          $0.edges.equalToSuperview()
        }
      }
      
      @available(*, unavailable)
      required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
      }
      
      func setImage(_ image: UIImage) {
        photoImageView.image = image
      }
    }​
  • SliderCollectionViewscrollDirection 값을 horizontal로 부여
    // DetailViewController.swift
    
    // MARK: UI
    private let sliderCollectionView = UICollectionView(
      frame: .zero,
      collectionViewLayout: UICollectionViewFlowLayout().then {
        $0.minimumLineSpacing = Metric.collectionViewSpacing
        $0.minimumInteritemSpacing = Metric.collectionViewSpacing
        $0.scrollDirection = .horizontal // <- 여기
      }
    ).then {
      $0.register(cellType: SliderCollectionViewCell.self)
      $0.contentInset = Metric.collectionViewContentInset
      $0.showsHorizontalScrollIndicator = false
      $0.allowsSelection = true
      $0.isScrollEnabled = true
      $0.bounces = true
      $0.backgroundColor = Color.clear
      $0.isPagingEnabled = true
    }​
  • autolayout
    // DetailViewController.swift
    
    private func configureLayout() {
      view.addSubview(self.sliderCollectionView)
      
      self.sliderCollectionView.snp.makeConstraints {
        $0.centerX.centerY.equalToSuperview()
        $0.height.equalTo(view.safeAreaLayoutGuide).inset(Metric.collectionViewVerticalInset)
        $0.left.right.equalTo(view.safeAreaLayoutGuide).inset(Metric.collectionViewHorizontalInset)
      }
    }
  • CollectionView에 이미지들을 가로로 나열하기 위해서 item들의 size들을 collectionView와 동일하게 설정
    • UICollectionViewDeelgateFlowLayout 델리게이트에서 sizeForItemAt 델리게이트 메소드 사용
      // DetailViewController.swift
      
      // Delegate 준수
      self.sliderCollectionView.rx.setDelegate(self)
        .disposed(by: self.disposeBag)
        
      // Delegate 구현
      extension DetailViewController: UICollectionViewDelegateFlowLayout {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
          let widthPadding = Metric.collectionViewSpacing
          let width = sliderCollectionView.bounds.size.width - widthPadding
          let height = sliderCollectionView.bounds.size.height
          return CGSize(width: width, height: height)
        }
      }
  • 디바이스 가로, 세로 회전 대응
    • 기존에 vertical inset의 크기가 screen의 1/3로 잡혀 있었으므로 디바이스가 회전하여 가로모드가 되면 크기가 찌부되는 현상(UI 미표출) 발생
    • 가로모드인 경우에는 vertical inset의 크기를 작게하는 코드 필요
// exetnsion으로 추가
public extension Reactive where Base: UIViewController {
  var viewWillTransition: ControlEvent<Void> {
    let source = self.methodInvoked(#selector(Base.viewWillTransition)).map { _ in }
    return ControlEvent(events: source)
  }
}

// bind
self.rx.viewWillTransition
  .bind { [weak self] in self?.updateConstraints() }
  .disposed(by: disposeBag)
  
// update constraints
private func updateConstraints() {
  let isPortrait = UIDevice.current.orientation.isPortrait
  let verticalInset = isPortrait
  ? Metric.collectionViewVerticalInset
  : Metric.collectionViewHorizontalInset
  
  self.sliderCollectionView.snp.updateConstraints {
    $0.height.equalTo(view.safeAreaLayoutGuide).inset(verticalInset)
  }
}

 

* 전체 소스 코드: https://github.com/JK0369/ExPhotoSlider

Comments