관리 메뉴

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

[iOS - swift] UI 컴포넌트 - Horizontal Scroll View (수평 스크롤 뷰) 본문

UI 컴포넌트 (swift)

[iOS - swift] UI 컴포넌트 - Horizontal Scroll View (수평 스크롤 뷰)

jake-kim 2021. 9. 29. 00:05

Horizontal Scroll View

  • 단순히 인터렉션이 없고 보여주기만 한다면 해당 Horizontal Scroll View 사용해도 무방하지만,
    Interaction이 필요한 경우 해당 클릭된 셀이 어떤 셀 이벤트인지 확인할 수 있는 ViewPager 글 참고

구현 아이디어

  • imageView를 scrollView에 추가할 때 xOffset값을 horizontalWidth값에 비례하여 삽입 후, scrollView의 contentSize.width값을 horizontalWidth * imageViews.count만큼 설정하면 완료

구현

  • BaseScrollView 정의
class BaseScrollView<Model>: UIScrollView {

    var model: Model? {
        didSet {
            if let model = model {
                bind(model)
            }
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        configure()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func configure() {}
    func bind(_ model: Model) {}
}
  • HorizontalScrollView 추가
    • 초기화에 horizontalWidth와 horizontalHeight를 받아서 imageView의 width와 height값에 사용
    • 단, imageView.contentMode = .scaleAspectFit하여 이미지의 fit만큼만 커지도록 설정하면 아이폰은 세로보다 가로가 짧으므로 가로가 기준으로 되어, 가로가 꽉차게 이미지 표출
    • `isPagingEnabled = true` 설정으로 scroll이 한번에 되지 않고 swipe 시 하나의 view마다 멈추도록 설정
class HorizontalScrollView: BaseScrollView<[UIImage]> {

    let horizontalWidth: CGFloat
    let horizontalHeight: CGFloat

    init(horizontalWidth: CGFloat, horizontalHeight: CGFloat) {
        self.horizontalWidth = horizontalWidth
        self.horizontalHeight = horizontalHeight

        super.init(frame: .zero)

        configure()
    }

    override func configure() {
        super.configure()

        isPagingEnabled = true
        showsHorizontalScrollIndicator = false
    }

    override func bind(_ model: [UIImage]) {
        super.bind(model)

    }
}
  • bind 메소드 내부에 horizontalScroll이 되도록 설정하는 메소드 호출
    • setImages(): image들의 offset을 설정하여 scrollView내부에 imageView를 삽입
    • updateScrollViewContentWidth(): scrollView의 contentSize를 늘려주어 스크롤이 동작하도록 설정
    override func bind(_ model: [UIImage]) {
        super.bind(model)

        setImages()

        updateScrollViewContentWidth()
    }
  • setImages(): image들의 offset을 설정하여 scrollView내부에 imageView를 삽입
    private func setImages() {
        guard let images = model else { return }
        images
            .enumerated()
            .forEach {
                let imageView = UIImageView(image: $0.element)
                imageView.contentMode = .scaleAspectFit
                let xOffset = horizontalWidth * CGFloat($0.offset)

                imageView.frame = CGRect(x: xOffset, y: 0, width: horizontalWidth, height: horizontalHeight)
                addSubview(imageView)
            }
    }
  • updateScrollViewContentWidth(): scrollView의 contentSize를 늘려주어 스크롤이 동작하도록 설정
    private func updateScrollViewContentWidth() {
        contentSize.width = horizontalWidth * CGFloat(model?.count ?? 1)
    }
  • HorizontalScrollView를 사용하는 쪽
class ViewController: UIViewController {

    lazy var horizontalScrollView: HorizontalScrollView = {
        let view = HorizontalScrollView(horizontalWidth: view.frame.width, horizontalHeight: view.frame.height)

        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupViews()
        addSubviews()
        makeConstraints()
    }

    func setupViews() {
        view.backgroundColor = .white
        let image1 = UIImage(named: "image1")!
        let image2 = UIImage(named: "image2")!
        let image3 = UIImage(named: "image3")!

        horizontalScrollView.model = [image1, image2, image3]
    }

    func addSubviews() {
        view.addSubview(horizontalScrollView)
    }

    func makeConstraints() {
        horizontalScrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            horizontalScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            horizontalScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            horizontalScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            horizontalScrollView.topAnchor.constraint(equalTo: view.topAnchor)
        ])
    }
}

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

Comments