UIStackView: [Apple] UIStackView

J.Noma·2021년 11월 22일
0

iOS : View : UIKit

목록 보기
3/17
post-custom-banner

출처 : https://developer.apple.com/documentation/uikit/uistackview


StackView
column 혹은 row 방향으로 view의 collection을 나열한 인터페이스


🐶 Overview

✔️ Stack View 개념 요약

  • 오토레이아웃 : Stack View는 당신에게 오토레이아웃의 강력한 기능을 활용할 수 있게 해줍니다

  • 동적 변화 : stack view는 device의 orientation/screen크기/가능한 공간의 어떠한 변화에든
    '동적으로 적응'할 수 있는 유저 인터페이스를 만듭니다

  • subview를 array로 관리 : stack view는 arrangedSubviews라는 배열 프로퍼티에 들어있는 view들의 레이아웃을 관리합니다
    이 subview들은 stack view의 axis를 따라 정렬되며, 그 순서는 arrangedSubviews 배열의 순서를 따릅니다

  • stack view의 레이아웃 프로퍼티 : 정확한 레이아웃은 stack view의 axis/distribution/alignment/spacing등의 프로퍼티에 따라 달라집니다


✔️ 스토리보드에서 Stack View 만들기 예시

  1. stack view를 사용하기 위해 스토리보드를 열고
  2. 라이브러리에서 Horizontal 혹은 Vertical stack view를 드래그해와서 원하는 곳에 위치시킵니다
  3. 그리고, stack의 content를 stack안에 드래그앤드랍합니다
    (필요에 따라, content를 더 추가합니다)
  4. 또한, Attribute Inspector에서 stack view의 프로퍼티를 수정하여
    stack content의 외형을 조정할 수도 있습니다

❗️Note
stack view의 위치는 반드시 정의해야 하며 크기는 선택적으로 정의합니다
그 후, 내부 content의 레이아웃과 크기를 관리합니다



🐱 Stack View 내부 content에 대한 오토 레이아웃

stack view는 내부 content의 위치와 크기를 정하기 위해 오토레이아웃을 사용합니다

✔️ 기본적으로 first/last content는 Edge에 고정된다

stack view는 첫번째/마지막 view를 stack의 edge에 정렬합니다 (stack axis를 따라서)

Horizontal stack
first view의 leading edge가 stack의 leading edge에 고정됩니다
그리고 last view의 trailing edge가 stack의 trailing edge에 고정됩니다

Vertical stack
first view의 top edge가 stack의 top edge에 고정됩니다
그리고 last view의 bottom edge가 stack의 bottom edge에 고정됩니다

✅ NOTE : 반드시 edge에 고정되진 않는다
만약 stack view의 isLayoutMarginRelativeArrangement프로퍼티를 true로 만들면
stack은 content를 자신의 edge가 아닌 margin에 고정합니다


✔️ Distribution

axis 방향의 position/size를 결정합니다

  • 각 content의 axis방향 size를 계산하기 위해 기본적으로
    intrinsicContentSize라는 프로퍼티를 사용합니다
    (UIStackView.Distribution.fillEqually는 제외)

  • 단, UIStackView.Distribution.fillEqually는 이 프로퍼티를 무시하고
    모든 content의 size를 동일하게 resize합니다

  • 가능하다면, stack view는 모든 content를 각자의 최대 intrisic size까지 늘립니다


✔️ Alignment

axis 수직방향의 position/size를 결정합니다

  • stack view는 axis에 수직방향으로의 size를 계산할 때도, intrinsicContentSize를 사용합니다
    (UIStackView.Alignment.fill는 제외)

  • UIStackView.Alignment.fill는 수직방향 size를 stack view와 동일하도록 최대한 늘립니다

  • 가능하다면, stack view는 모든 content의 수직방향 size를 최대 intrinsic size까지 늘립니다


UIStackView.Alignment.fill 예시 (vertical)



🐭 Stack View의 위치/크기 설정

내부 content를 레이아웃함에 있어서는 오토레이아웃을 사용하지 않을 수도 있지만
stack view 자체의 위치를 지정하려면 오토레이아웃이 필요합니다

✔️ Position

일반적으로 stack view의 위치를 정의하기 위해서는, 적어도 2개의 edge는 고정해줘야 합니다

Stack의 position은 내부 content의 'Baseline'을 기준으로 설정할 수 있습니다

top, bottom, or center Y 대신 baseline을 기준으로 설정할 수도 있습니다
(forFirstBaselineLayoutforLastBaselineLayout 계산프로퍼티 활용)

  • horizontal stack view
    : tallest content를 UIView로 반환합니다
    (만약 tallest content가 stack view인 경우라면, 즉. nested stack view라면 더 타고 들어가서 tallest의 tallest를 찾는 행위를 하게 됩니다)

  • vertical stack view
    : 최상위/최하위 content를 UIView로 반환합니다
    (이것도 마찬가지로 nested stack일 경우 한 번 더 타고 들어갑니다)

❗️Note❗️ Baseline은 content height가 intrinsic일 때만 적용가능
Baseline alignment는 content의 height가 자신의 intrinsic height와 동일할 때만 동작합니다
만약 content가 stretched/compressed 되었을 경우, baseline이 이상한 위치에 잡힙니다


✔️ Size

stack view의 size는 추가적인 constraint없이, 내부 content에 기반하여 계산됩니다

  1. axis방향 size
    stack view의 axis방향 size는 내부 content의 size와 content간 space의 총합으로 결정됩니다

  2. 수직방향 size
    stack view의 axis 수직방향 size는 가장 큰 content의 size를 따라갑니다

isLayoutMarginRelativeArrangement 프로퍼티를 true로 두면
위 조건으로 정해진 size에 더해 추가적인 margin 공간을 줄 수도 있습니다


✔️ Size에 별도로 constraint를 거는 경우

stack view의 height/width를 명시하기 위해 추가 constraint를 제공할 수도 있습니다

이 경우, stack view는 내부 content에 의해 size가 정해지는게 아니라
추가 constraint를 따라갑니다

이로 인해, 내부 content size는 intrinsic size가 아닌 그에 맞춰 resize됩니다

구체적인 레이아웃은 stack view의 프로퍼티들에 따라 달라집니다
(Distribution / Alignment)



🐭 일반적인 Stack view 레이아웃 예시

✔️ 2면 edge 고정

2면의 edge를 superview에 고정하여 stack view position을 정의할 수 있습니다

이 경우, stack view의 size가 내부 content에 따라 자유롭게 늘어날 수 있습니다
(content size에 있어 모든 방향에 대해 intrinsic 유지가능)


예시
1. leading과 top edge를 superview에 고정시키고
2. label들은 서로 firstbaseline이 align되도록 적용
3. label 간 간격은 8point


✔️ 3면 edge 고정 (axis 방향)

먼저, axis 방향의 2면 edge를 고정함으로써 stack view의 axis방향 size를 결정합니다
2면 edge가 맞춰졌지만 axis 방향만의 2면이므로 수직방향 edge 최소 1개를 추가로 고정해줘야 합니다

axis방향 size를 직접 정해주는데 이런 경우, 내부 content는 이 공간을 채우도록 크기/위치가 결정됩니다
(고정되지 않은 수직방향은 자유롭게 늘어날 수 있습니다)

content의 수직방향 size는 intrinsic으로 유지됩니다
content의 axis방향 size는 stack view의 distribution에 따라 결정됩니다


예시
0. stack view에 label(First Name)과 빈 공간을 넣음
1. leading/trailing/top을 superview에 고정
2. distribution : fill
3. 빈 공간의 hugging priority를 label보다 낮게 설정


✔️ 3면 edge 고정 (axis 수직방향)

2번 방식과 반대로, axis에 수직방향의 2면 edge를 고정합니다
그리고, axis방향은 1면 edge만 고정합니다

이 방식은 content를 추가/제거함에 따라 stack view size가 늘어났다/줄었다하게 할 수 있습니다

content의 axis방향 size는 intrinsic으로 유지됩니다
(distribution을 fillEqually로 하지 않는 한)

content의 수직방향 size는 stack view의 alignment에 따라 결정됩니다

예시
0. vertical stack
1. leading/trailing을 superview에 고정
2. top을 superview에 고정
3. label간 8pt spacing
4. center alignment


✔️ 4면 edge 고정

이 경우, stack view의 크기는 고정됩니다

content size는 모든 방향에 대해 intrinsic을 유지하지 못하고
distribution/alignment 설정에 따라 stack view를 가득 채우도록 설정됩니다

예시



🐹 Content의 위치/크기 설정

✔️ Stack View 프로퍼티들

content의 위치와 크기를 결정하는 Stack view의 프로퍼티들이 존재합니다

  • axis
    stack의 방향을 결정. (vertical / horizontal)

  • distribution
    axis 방향 layout

  • alignment
    axis 수직방향 layout

  • spacing
    content간 최소 간격

  • isBaselineRelativeArrangement
    content의 vertical alignment를 baseline끼리 맞출 것인지

  • isLayoutMarginsRelativeArrangement
    content들을 stack view의 edge에 맞추지 않고 내부 margin을 줄 것인지


✔️ Nested Stack View

전형적으로, content가 많지 않을 때는 하나의 stack view로 처리합니다

하지만, 좀 더 복잡한 view 계층을 만들기 위해 stack view들을 중첩(nesting)시킬 수 있습니다


예시
1. 큰 vertical stack 생성
2. 그 안에 작은 horizontal stack 생성
3. 작은 horizontal stack 안에 label과 빈 공간 생성


✔️ 그 외 추가 constraint

content에 추가적인 constraint를 주어 미세조정할 수도 있습니다

각 content에 대해, 최대/최소 height를 제한하는 등의 constraint를 추가로 걸 수 있습니다

혹은, 각 content에 대해 aspect ratio를 걸 수도 있습니다

stack view는 content를 배열할 때 이런 constraint들을 모두 사용합니다

예로, image view는 image가 resize될 때
고정된 aspect ratio를 유지하도록 constraint를 가지고 있습니다

❗️NOTE❗️
content에 constraint를 추가할 땐 충돌이 발생하지 않게 주의해야 합니다
일반적인 rule이 있다면, content의 어떤방향으로의 size가 디폴트로 intrinsic이라면
그 방향으로는 안전하게 constraint를 추가할 수 있습니다



🐰 arrangedView와 subview 간 관계

stack view는 arrangedSubviews array와 subviews array를 모두 가집니다

✔️ arrangedSubviews는 subviews의 subset이다

stack view에서 arrangedSubviewssubviews의 subset입니다
(arrangedSubview에 있는 모든 것은 subview에도 있다)

이를 위해, 자동으로 해주는 몇가지 동작들이 있습니다

  • arrangedSubviews에 view추가
    subviews에도 자동 추가합니다

  • subviews의 view삭제
    arrangedSubviews에서도 자동 삭제합니다

  • arrangedSubviews의 view삭제
    subviews에 아무 일도 일어나지 않습니다
    다만, stack view의 관리대상이 아니게 되어 화면이 보이지 않습니다
    (여전히 view 계층의 일부로 존재하긴 합니다)


✔️ 두 array의 item 순서는 독립적이다

  • arrangedSubviews의 item 순서는 stack에서 보여지는 순서를 뜻합니다
    예로, horizontal stack에선 가장 왼쪽에 보이는 view가 첫번째 item입니다
    vertical stack에선 가장 윗쪽 view가 첫번째 item입니다

  • subview의 item 순서는 Z축 순서를 뜻합니다
    view들이 overlap되는 경우, 가장 바닥에 있는 view가 첫번째 item입니다



🦊 Content를 동적으로 변경하기

✔️ content 추가/제거/숨겨짐

stack view는 content가 추가/제거될 때 혹은 숨겨질 때마다 자동으로 레이아웃이 업데이트됩니다

// Appears to remove the first arranged view from the stack.
// The view is still inside the stack, it's just no longer visible, and no longer contributes to the layout.
let firstView = stackView.arrangedSubviews[0]
firstView.isHidden = true

✔️ 프로퍼티 변경

stack view는 또한 프로퍼티 변경에 자동으로 반응합니다
예로, axis 프로퍼티를 변경하면 orientation이 동적으로 변경됩니다

// Toggle between a vertical and horizontal stack
if stackView.axis == .Horizontal {
    stackView.axis = .Vertical
}
else {
    stackView.axis = .Horizontal
}

✔️ 애니메이션 적용

이런 동적 변경에 애니메이션을 적용할 수 있습니다

// Animates removing the first item in the stack.
UIView.animateWithDuration(0.25) { () -> Void in
    let firstView = stackView.arrangedSubviews[0]
    firstView.isHidden = true
}

✔️ size class 변화

(size class는 Regular/Compact를 말합니다)

size class별 프로퍼티 값을 Interface Builder에서 직접 정의할 수 있습니다

시스템은 stack view의 size class 변화에는 자동으로 애니메이션을 적용해줍니다

profile
노션으로 이사갑니다 https://tungsten-run-778.notion.site/Study-Archive-98e51c3793684d428070695d5722d1fe
post-custom-banner

0개의 댓글