본문 바로가기

이상/iOS

[iOS] UIScrollView(Horizontal) 만들기 (+ SnapKit 사용법)

반응형

이전에 작성했던 UIScrollView 글의 제목을 Horizontal이라고 해놓고

 

본문에는 Vertical 내용을 작성한 걸 얼마 전에 알고 급히 제목을 수정했다.

 

그래서 이번에는 Horizontal 방향의 UIScrollView를 사용하는 법과

 

추가로 SnapKit에 대한 내용을 작성한다.

 

 

Horizontal 이든 Vertical 이든 어차피 같은 UIScrollView이고

 

방향을 어떻게 할 것이냐에 따라 고정되는 축만 달라진다.

 

Vertical일 때는 세로로 스크롤 해야하므로 width 가 고정이었으므로

 

Horizontal일 때는 가로로 스크롤 해야하므로 height 가 고정이다.

 

아래와 같이 height 가 80인 Horizontal 방향의 UIScrollView를 만들어 보자.

 

Horizontal UIScrollView

 

 

1. UIView로 UIScrollView의 Frame 잡기

 

굳이 없어도 되지만 Storyboard로 화면들을 그릴 경우

 

'이 영역은 UIScrollView 영역입니다' 라고 영역표시를 해주는 용도이다.

 

아래와 같이 UIViewController에 height가 80이고

 

top, leading, trailing을 SafeArea에 붙인 UIView를 추가한다.

 

Scroll Frame View 배치

 

이제 이 View에 UIScrollView[UIStackView[item, item, ...]]를 addSubview() 하면 된다.

 

 

2. UIScrollView, UIStackView 생성

 

UIScrollView와 UIStackView는 소스로 추가할 것이기 때문에 프로퍼티로 생성해준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class HorizontalScrollViewController: UIViewController {
 
    @IBOutlet weak var scrollFrameView: UIView!
    
    let scrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.showsVerticalScrollIndicator = false
        return scrollView
    }()
    let stackView: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .equalSpacing
        stackView.layoutMargins = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
        stackView.isLayoutMarginsRelativeArrangement = true
        stackView.spacing = 8
        return stackView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
cs

 

 

3. iconView 생성

 

UIStackView에 넣을 item으로 아래와 같은 iconView를 만든다.

 

iconView

 

위에는 icon이 들어갈 UIImageView가 있고 아래에는 index를 표시해줄 UILabel이 있다.

 

iconView 또한 소스만으로 만들 것이기 때문에 xib파일은 필요없다.

 

iconView를 만들기 전에 SnapKit이 뭔지 알아보자.

 

 

SnapKit

 

SnapKit

 

SnapKit은 iOS와 OS X 에서 Auto Layout을 쉽게 만들 수 있게 해주는 DSL이라고 한다.

 

초창기엔 nib/xib로 화면을 하나씩 그렸고 나중엔 여러 화면을 한 파일에서 볼 수 있게 Storyboard 가 생겼다.

 

여러 화면을 한 눈에 볼 수 있어서 화면 간의 관계나 순서를 파악하기 쉬운 장점이 있지만

 

단점으로는 화면이 많아질수록 무거워지고, 여러사람이 git으로 동시에 작업할 경우 commit 시에 충돌나기 쉽다.

 

나도 Constraint 하나 추가할 때마다 몇십초씩 걸려서 너무 답답했던 경험이 있다.

 

때문에 Storyboard를 사용하지 않고 순수 소스로 모든 화면을 그린 앱들도 있다.

(이번에 여러군데 면접을 보다가 알게 됐다.)

 

이 때 SnapKit을 사용하면 아주 간편하게 화면을 그릴 수 있다.

 

 

우선 SnapKit을 사용하지 않고 UIView 하나를 superView에 addSubview() 해보자.

 

Constraint를 사용할 것이므로 translatesAutoresizingMaskIntoConstraints를 false로 설정해주고

 

addSubview() 한 후에 leading, top, trailing, bottom에 대해 constraint를 설정한다.

 

1
2
3
4
5
6
7
8
9
10
// subView에 대해 AutoResizingMask를 Constraints로 변환하지 않습니다. 
// -> Constraints 사용할래요. 
subView.translatesAutoresizingMaskIntoConstraints = false 
// view에 subView add 
view.addSubview(subView) 
// subView의 Constraints 설정 
subView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 
subView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true 
subView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 
subView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
cs

 

만약 화면에 배치할 View들이 많으면 많을수록 View에 대한 소스의 양이 엄청 많아질 것이다.

 

이럴 때 SnapKit을 사용하면 유용하다.

 

SnapKit은 translatesAutoresizingMaskIntoConstraints를 알아서 false로 설정해준다.

 

그리고 Constraint를 설정할 상대 View (위의 예시에서는 superView)가 같은 부분들은 쉼표로 같이 사용할 수 있다.

 

위의 소스를 그대로 SnapKit을 사용하면 아래와 같아진다.

 

1
2
3
4
5
6
// view에 subView add 
view.addSubview(subView) 
// subView의 Constraints 설정 
subView.snp.makeConstraints { make in 
    make.top.left.right.bottom.equalToSuperview() 
}
cs

 

대충봐도 적은 양의 소스로 끝냈다.

 

SnapKit을 사용할 때 주의할 점은 개발할 화면의 구조를 확실하게 알고 있어야한다는 점이다.

 

이제 SnapKit을 이용해서 소스로 iconView를 만들어보자.

 

 

UIView를 하나 만들고 위에 UIImageView를 addSubview() 한 후, UILabel을 아래에 addSubview() 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// iconView 생성 
let iconView = UIView() 
// icon 부분의 UIImageView 
let imageView: UIImageView = {
    let imageView = UIImageView() 
    imageView.contentMode = .scaleAspectFit 
    imageView.image = UIImage(named: "ProfileIcon.png"
    return imageView 
}() 
// iconView에 addSubview() 한 다음 
iconView.addSubview(imageView) 
// Constraints 설정 
imageView.snp.makeConstraints { make in 
    // top, leading, trailing을 부모뷰에 붙이고 
    make.top.left.right.equalToSuperview() 
    // X축은 가운데로 고정할게. 
    make.centerX.equalToSuperview() 
      
// index 부분의 UILabel
let label: UILabel = { 
    let label = UILabel() 
    label.text = "\(count+1)" 
    label.font = .systemFont(ofSize: FontSize.small) 
    return label
}()
// iconView에 addSubview() 한 다음 
iconView.addSubview(label)// Constraints 설정 
label.snp.makeConstraints { make in 
    // top을 imageView의 bottom에 8만큼 떨어지게 붙이고 
    make.top.equalTo(imageView.snp.bottom).offset(8
    // bottom은 부모뷰에 붙이고 X축은 가운데로 고정할게. 
    make.bottom.centerX.equalToSuperview() 
}
cs

 

SnapKit을 이용하여 iconView를 만들었다.

 

이제 마무리 해보자.

 

 

4. StackView, ScrollView

 

View depth

 

위 그림처럼 StackView가 ScrollView의 자식이고, ScrollView가 ScrollFrameView의 자식이 돼야한다.

 

이제 iconView 20개를 StackView에 add 한 다음, StackView를 ScrollView에 add 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
for count in 0..<20 {
    // iconView 만들어서
    let iconView = UIView()
    let imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.image = UIImage(named: "ProfileIcon.png")
        return imageView
    }()
    iconView.addSubview(imageView)
    imageView.snp.makeConstraints { make in
        make.top.left.right.equalToSuperview()
        make.centerX.equalToSuperview()
    }
    let label: UILabel = {
        let label = UILabel()
        label.text = "\(count+1)"
        label.font = .systemFont(ofSize: FontSize.small)
        return label
    }()
    iconView.addSubview(label)
    label.snp.makeConstraints { make in
        make.top.equalTo(imageView.snp.bottom).offset(8)
        make.bottom.centerX.equalToSuperview()
    }
    // stackView에 추가
    stackView.addArrangedSubview(iconView)
}
// scrollView에 stackView를 add 하고
scrollView.addSubview(stackView)
// stackView Constraints 설정
stackView.snp.makeConstraints { make in
    make.top.left.right.bottom.height.equalToSuperview()
}
// scrollFrameView 에 scrollView를 add 하고
scrollFrameView.addSubview(scrollView)
// scrollView Constraints 설정
scrollView.snp.makeConstraints { make in
    make.top.left.right.bottom.height.equalToSuperview()
}
cs

 

위에서 설명한 대로 height를 Storyboard에서 추가한 scrollFrameView에 고정시킨다.

 

이렇게 하면 아래와 같은 구조가 될 것이다.

 

Horizontal ScrollView 구조

 

이제 돌려보자!

 

결과

 

이상 Horizontal ScrollView와 SnapKit에 대한 내용 끝!

반응형