본문 바로가기

iOS

UIStackView

728x90

UIStackView를 사용하면 화면을 구성할 때 뎁스를 깊게 들어가지 않고 View를 구성할 수 있습니다. (레이아웃을 안의 내용에 따라 알아서 레이아웃을 잡아주기 때문에, 몇 가지 조건을 작성해준다면, 생각보다 화면을 구성할 때 많이 쓰이기도 하고 유용하게 쓰입니다.)

 

StackView란?

여러 개의 뷰(-> 여기서 말하는 View는 UIComponent를 통칭하는 말입니다.)를 정렬해서 나타낼 때 능률적인 인터페이스입니다.

✔️ 스택 뷰는 사용자 UI를 빠르고 쉽게 설계할 수 있는 강력한 도구입니다.

✔️ 스택 뷰 안의 여러 가지 속성을 사용해 배열된 뷰의 배치 방법을 지정할 수 있습니다.

 

🤔 여러 개의 뷰를 정렬한다?

예를 들어서, 화면에 버튼을 나란히 정렬하고 싶다고 생각해봅시다. (마치 앱 서비스에서 소셜 로그인 버튼들이 상하로 같은 간격으로 나열되어 있는 것과 말이죠.)

 

만약, 스택 뷰를 사용하지 않는다면?

✔️ 각 버튼에 대해서 AutoLayout을 통해 화면에 배치를 할 수 있습니다.

✔️ 버튼 하나하나마다 Constraints를 지정할 수도 있지만, 지금 다루어 볼 스택 뷰를 통해서 더 간단하게 나타낼 수 있습니다.

 

🤔 스택 뷰를 사용하면, 각 뷰의 레이아웃을 지정하지 않아도 되는 것인가?

맞습니다. 스택 뷰의 내부 뷰들은 스택 뷰 자체의 옵션들로 제어가 되기 때문에 내부의 레이아웃을 지정할 필요가 없습니다.

 

그러나, 아예 설정하지 않아도 되는 것인가?

❌ 그렇지 않습니다. >> 스택 뷰의 위치 정도는 AutoLayout으로 지정해야 합니다. 

>> 내부 뷰들의 크기에 따라서 width/height 값이 자동으로 정해지기 때문에 Top, Leading 값 정도를 지정하면 (위치 정도) AutoLayout이 설정됩니다.

>> 만약 세 개의 버튼 중 하나의 가로 값이 100 -> 300으로 증가하게 되면, 스택 뷰는 내부 뷰들 중에서 가장 큰 뷰의 가로 값에 맞춰서 증가하게 됩니다.

(스택 뷰의 AutoLayout을 맞춰주면, 내부 뷰들은 그에 따라 별도의 Constraints 설정 없이 정렬됩니다.)

 

StackView 사용법

먼저 스택 뷰는 내부 뷰들이 가로/세로에 따라 어떻게 정렬되는가를 기준으로 크게 두 가지 종류가 있습니다. 

- 좌-> 우로 내부 뷰들이 쌓인다면 : Horizontal Stack View

- 상->하로 내부 뷰들이 쌓인다면 : Vertical Stack View

 

로그인 시, 소셜 로그인 버튼이 상하로 배치되어 있는 화면을 예시로 하겠습니다. (+ 코드 베이스를 곁들인)

 

✅ 가장 먼저 스택 뷰를 생성해보겠습니다. 

    private lazy var buttonStackView = UIStackView().then {
        $0.axis = .vertical
        $0.distribution = .equalSpacing
        $0.alignment = .fill
        $0.spacing = UIRatio.height(value926: 12, value568: 8)
    }

 

위의 코드에서 스택 뷰의 옵션을 설정한 부분들을 볼 수 있습니다.

Axis

스택 뷰가 수직으로 정렬될 것인지, 수평으로 정렬될 것인지 지정하는 옵션입니다.

 

Distribution

스택 뷰 안 뷰들의 X축을 정렬하는 옵션입니다. .fill, .fillEqually, .fillProportionally, .equalSpacing, .equalCentering 옵션 값들이 있습니다. 

 

.fill

Stack View가 arranged views의 크기를 조정하여 Stack View axis를 따라 사용 가능한 공간을 채우는 레이아웃입니다.

arrranged views가 Stack View에 맞지 않으면 압축 저항(compression resistance priority) 우선순위에 따라 보기를 축소합니다.

arranged views가 Stack View를 채우지 않는 경우, 고정 우선순위(hugging priority)에 따라 뷰를 확장합니다.

만약 모호할 경우 Stack View는 arranged viewsarrangedSubviews array의 index를 기준으로 arranged views의 크기를 조정합니다.

 

스택 뷰 안 뷰들의 크기로 채워진 모습을 볼 수 있습니다. 

 

.fillEqually

Stack View가  arranged views의 크기를 조정하여 Stack View axis를 따라 사용 가능한 공간을 채우는 레이아웃입니다.

View의 크기가 Stack View axis 옵션에 따라 모두 동일한 크기로 조정됩니다.

 

.fillProportionally

Stack View가  arranged views의 크기를 조정하여 Stack View axis를 따라 사용 가능한 공간을 채우는 레이아웃입니다.

View는 Stack View의 Axis 옵션을 따라 고유한 콘텐츠 크기에 따라 비례하여 크기가 조정됩니다.

 

.equalSpacing

Stack View가 axis옵션을 따라 사용 가능한 공간을 채울 수 있도록 arranged views를 배치하는 레이아웃입니다.

arranged views가 Stack View를 채우지 못하면 View 사이의 간격을 균등하게 Padding  합니다.

arranged views가 Stack View에 맞지 않으면 압축 저항 우선순위(compression resistance priority)에 따라 View를 축소합니다.

만약 모호할 경우 Stack View는 arrangedSubViews array의 index  기준으로 보기를 축소합니다.

 

.equalCentering

View 간 간격 특성의 거리를 유지하면서 Stack View axis 옵션을 따라 center-to-center로 같은 각을 갖도록 arranged views의 위치를 지정하려고 시도하는 레이아웃입니다.

arranged views가 Stack View에 맞지 않으면 간격 속성에 의해 정의된 최소 간격에 도달할 때까지 간격을 축소합니다.

그래도 View가 맞지 않으면 Stack View는 압축 저장 우선순위(compression resistance priority)에 따라 arranged view를 축소합니다.

 

Alignment

스택 뷰 안 뷰들의 Y축을 정렬하는 옵션입니다. .fill, .leading, .center, .trailing 옵션 값들이 있습니다.

 

.fill

스택 뷰 안 뷰들의 width값을 스택 뷰의 width값과 맞춘다는 의미입니다.

 

.leading/.trailing

스택 뷰 안 뷰들을 스택 뷰의 왼쪽/오른쪽에 정렬한다는 의미입니다.

 

.center

스택 뷰 안 뷰들을 스택 뷰의 중앙에 정렬한다는 의미입니다.

 

 

✅ 스택 뷰 안의 뷰들을 생성해보겠습니다.

    private lazy var naverButtonView = UIView().then {
        $0.backgroundColor = .System.green
    }
    
    private lazy var naverButtonImageView = UIImageView().then {
        $0.image = Asset.logoNaver.image
    }
    
    private lazy var naverButtonLabel = UILabel().then {
        $0.font = .NotoSansKR.regular.fontHRatio(value926: 16, value568: 14)
        $0.text = "네이버로 로그인"
        $0.textColor = .Grayscale.white
    }
    
    private lazy var kakaoButtonView = UIView().then {
        $0.backgroundColor = .init(hex: "#FEE500")
    }
    
    private lazy var kakaoButtonImageView = UIImageView().then {
        $0.image = Asset.logoKakao.image
    }
    
    private lazy var kakaoButtonLabel = UILabel().then {
        $0.font = .NotoSansKR.regular.fontHRatio(value926: 16, value568: 14)
        $0.text = "카카오톡으로 로그인"
        $0.textColor = .init(hex: "#38321C")
    }
    
    private lazy var appleButtonView = UIView().then {
        $0.backgroundColor = .Grayscale.black
    }
    
    private lazy var appleButtonImageView = UIImageView().then {
        $0.image = Asset.logoApple.image
    }
    
    private lazy var appleButtonLabel = UILabel().then {
        $0.font = .NotoSansKR.regular.fontHRatio(value926: 16, value568: 14)
        $0.text = "Apple로 로그인"
        $0.textColor = .Grayscale.white
    }

위와 같이 생성을 하고 (저는 소셜 로그인 버튼을 UIButton으로 생성하지 않고 뷰로 생성 후 안에 이미지와 라벨을 넣었습니다. 그리고 rx를 통해 View를 탭 했을 때의 제스처로 이벤트를 구현했습니다.)

 

✅ 뷰를 넣어보겠습니다.

        self.addSubview(buttonStackView)
      
        buttonStackView.addArrangedSubview(naverButtonView)
        buttonStackView.addArrangedSubview(kakaoButtonView)
        buttonStackView.addArrangedSubview(appleButtonView)

위와 같이 뷰 안에 버튼 스택 뷰를 넣고,

스택 뷰 안에 addArrangedSubview를 통해 subView를 넣어주었습니다. 

 

        for v in buttonStackView.arrangedSubviews {
            v.snp.makeConstraints {
                $0.height.equalToHRatio(value926: 60, value568: 47)
            }
        }

위의 코드를 작성하여 스택 뷰안에서 각 버튼 뷰의 높이 값을 지정하면, 가장 처음 스택 뷰를 생성할 때의 spacing 값의 간격을 갖고 있고 + 위와 같은 높이 값을 갖고 있는 (소셜 로그인 버튼을 담겨 있는) 스택 뷰가 생성됩니다. 

 

앞서 말씀드린 것과 같이, 스택 뷰 안에서의 레이아웃은 따로 지정할 필요가 없습니다. 그러나 스택 뷰 자체의 위치는 지정해야 합니다.

        buttonStackView.snp.makeConstraints {
            $0.leading.trailing.equalToSuperview().inset(20)
            $0.bottom.equalTo(safeAreaLayoutGuide).offsetHRatio(value926: -96, value568: -29.12)
        }

 

이렇게 코드를 작성하고 나면,

위와 같은 화면을 구현할 수 있습니다.

'iOS' 카테고리의 다른 글

Apple Developer Pending  (0) 2022.04.19
Autolayout Animation  (0) 2022.04.17
iOS-Concurrency  (0) 2022.04.09
SwiftGen  (0) 2022.04.05
ClipsToBounds vs MasksToBounds  (0) 2022.03.29