관리 메뉴

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

[iOS - swift] UI 컴포넌트 - 수평 스크롤 뷰 (ScrollView + StackView) 본문

UI 컴포넌트 (swift)

[iOS - swift] UI 컴포넌트 - 수평 스크롤 뷰 (ScrollView + StackView)

jake-kim 2021. 7. 14. 01:32

cf) collectionView를 이용한 수평 스크롤 뷰: https://ios-development.tistory.com/632

 

[iOS - swift] CollectionView(컬렉션 뷰) - 수평 스크롤 뷰 (UICollectionViewFlowLayout)

1. custom layout이 아닌 UICollectionViewFlowLayout 사용한 방법 2. UICollectionViewFlowLayout을 이용한 CarouselView (수평 스크롤 뷰) 3. custom layout을 이용한 UICollectionView CollectionView에 사용..

ios-development.tistory.com


scrollView + stackView를 이용한 가로 스크롤 뷰

구성

  • ScrollView 안에 StackView 추가
  • StackView안에 Button들을 추가
  • StackView의 constraint를 leading, trailing 값을 설정하여 좌우 스크롤이 되도록 설정

BaseScrollView

  • configure(): 기본적인 레이아웃 설정
  • bind(): data가 변경되면 불려지도록 구현
class BaseScrollView: UIScrollView {
    override init(frame: CGRect) {
        super.init(frame: frame)

        configure()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("Not implemented xib init")
    }

    func configure() {}
    func bind() {}
}

데이터 소스 정의

  • type을 가지고 있고 이 type에 따라 값이 결정
struct SomeDataModel {
    enum DataModelType: String {
        case one
        case two
        case three
        case four
        case five
        case six
    }

    let type: DataModelType

    var name: String {
        return type.rawValue
    }

    var iamge: UIImage {
        switch type {
        case .one: return UIImage(named: "card")!
        case .two: return UIImage(named: "checkbox")!
        case .three: return UIImage(named: "work")!
        case .four: return UIImage(named: "star")!
        case .five: return UIImage(named: "cp")!
        case .six: return UIImage(named: "profile")!
        }
    }

}

struct Mocks {
    static func getDataSource() -> [SomeDataModel] {
        return [SomeDataModel(type: .one),
                SomeDataModel(type: .two),
                SomeDataModel(type: .three),
                SomeDataModel(type: .four),
                SomeDataModel(type: .five),
                SomeDataModel(type: .six)]
    }
}

가로 스크롤 뷰 구현

  • HorizontalScrollView
class HorizontalScrollView: BaseScrollView {
}
  • ScrollView안에 들어갈 stackView 추가
    private lazy var stackView: UIStackView = {
        let view = UIStackView()
        view.axis = .horizontal
        view.spacing = 16.0
        view.backgroundColor = .separator

        return view
    }()
  • dataSource 추가
    • 참고) dataSource의 타입은 Generic으로 선언하면 더욱 좋은 코드
    var dataSource: [SomeDataModel]? {
        didSet { bind() }
    }
  • 기본 레이아웃 설정하는 configure() 구현
    • 핵심: StackView의 leading, trailing의 constraint를 설정하여 scoroll이 되게끔 설정
    • scroll은 contents의 영역이 frame영역보다 큰 경우 가능하므로, scroll의 frame인 stackView의 width값이 정해지도록 설정
    override func configure() {
        super.configure()

        showsHorizontalScrollIndicator = false
        bounces = false

        addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.leading.trailing.equalToSuperview() /// 이 값이 없으면 scroll 안되는 것 주의
            make.top.bottom.equalToSuperview()
        }
    }
  • bind() 구현
    • 핵심: StackView의 leading, trailing의 constraint를 설정하여 scoroll이 되게끔 설정
    • scroll은 contents의 영역이 frame영역보다 큰 경우 가능하므로, scroll의 frame인 stackView의 width값이 정해지도록 설정
    override func bind() {
        super.bind()

        dataSource?.forEach { data in
            let button = UIButton()
            button.setTitleColor(.blue, for: .normal)
            button.setTitle(data.name, for: .normal)
            button.setImage(data.iamge, for: .normal)
            button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: 0.0)

            stackView.addArrangedSubview(button)
            button.snp.makeConstraints { make in
                make.height.equalTo(42)
            }
        }
    }

사용하는 쪽

class ViewController: UIViewController {

    lazy var horizontalScrollView: HorizontalScrollView = {
        let view = HorizontalScrollView()

        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        configure()

        insertDataSource()
    }

    private func configure() {
        view.addSubview(horizontalScrollView)

        horizontalScrollView.snp.makeConstraints { make in
            make.center.width.equalToSuperview()
            make.height.equalTo(56)
        }

    }

    private func insertDataSource() {
        horizontalScrollView.dataSource = Mocks.getDataSource()
    }
}

* source code: https://github.com/JK0369/HorizontalScrollView

Comments