출처 : https://developer.apple.com/documentation/uikit/uistackview
StackView
column 혹은 row 방향으로 view의 collection을 나열한 인터페이스
오토레이아웃 : 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
등의 프로퍼티에 따라 달라집니다
❗️Note
stack view의 위치는 반드시 정의해야 하며 크기는 선택적으로 정의합니다
그 후, 내부 content의 레이아웃과 크기를 관리합니다
stack view는 내부 content의 위치와 크기를 정하기 위해 오토레이아웃을 사용합니다
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에 고정합니다
axis 방향의 position/size를 결정합니다
각 content의 axis방향 size를 계산하기 위해 기본적으로
intrinsicContentSize
라는 프로퍼티를 사용합니다
(UIStackView.Distribution.fillEqually는 제외)
단, UIStackView.Distribution.fillEqually
는 이 프로퍼티를 무시하고
모든 content의 size를 동일하게 resize합니다
가능하다면, stack view는 모든 content를 각자의 최대 intrisic size까지 늘립니다
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)
내부 content를 레이아웃함에 있어서는 오토레이아웃을 사용하지 않을 수도 있지만
stack view 자체의 위치를 지정하려면 오토레이아웃이 필요합니다
일반적으로 stack view의 위치를 정의하기 위해서는, 적어도 2개의 edge는 고정해줘야 합니다
top, bottom, or center Y 대신 baseline을 기준으로 설정할 수도 있습니다
(forFirstBaselineLayout
와 forLastBaselineLayout
계산프로퍼티 활용)
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이 이상한 위치에 잡힙니다
stack view의 size는 추가적인 constraint없이, 내부 content에 기반하여 계산됩니다
axis방향 size
stack view의 axis방향 size는 내부 content의 size와 content간 space의 총합으로 결정됩니다
수직방향 size
stack view의 axis 수직방향 size는 가장 큰 content의 size를 따라갑니다
isLayoutMarginRelativeArrangement
프로퍼티를 true로 두면
위 조건으로 정해진 size에 더해 추가적인 margin 공간을 줄 수도 있습니다
stack view의 height/width를 명시하기 위해 추가 constraint를 제공할 수도 있습니다
이 경우, stack view는 내부 content에 의해 size가 정해지는게 아니라
추가 constraint를 따라갑니다
이로 인해, 내부 content size는 intrinsic size가 아닌 그에 맞춰 resize됩니다
구체적인 레이아웃은 stack view의 프로퍼티들에 따라 달라집니다
(Distribution
/ Alignment
)
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
먼저, 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보다 낮게 설정
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
이 경우, stack view의 크기는 고정됩니다
content size는 모든 방향에 대해 intrinsic을 유지하지 못하고
distribution/alignment 설정에 따라 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을 줄 것인지
전형적으로, content가 많지 않을 때는 하나의 stack view로 처리합니다
하지만, 좀 더 복잡한 view 계층을 만들기 위해 stack view들을 중첩(nesting)시킬 수 있습니다
예시
1. 큰 vertical stack 생성
2. 그 안에 작은 horizontal stack 생성
3. 작은 horizontal stack 안에 label과 빈 공간 생성
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를 추가할 수 있습니다
stack view는 arrangedSubviews
array와 subviews
array를 모두 가집니다
stack view에서 arrangedSubviews
는 subviews
의 subset입니다
(arrangedSubview에 있는 모든 것은 subview에도 있다)
이를 위해, 자동으로 해주는 몇가지 동작들이 있습니다
arrangedSubviews
에 view추가
subviews에도 자동 추가합니다
subviews
의 view삭제
arrangedSubviews에서도 자동 삭제합니다
arrangedSubviews
의 view삭제
subviews에 아무 일도 일어나지 않습니다
다만, stack view의 관리대상이 아니게 되어 화면이 보이지 않습니다
(여전히 view 계층의 일부로 존재하긴 합니다)
arrangedSubviews
의 item 순서는 stack에서 보여지는 순서를 뜻합니다
예로, horizontal stack에선 가장 왼쪽에 보이는 view가 첫번째 item입니다
vertical stack에선 가장 윗쪽 view가 첫번째 item입니다
subview
의 item 순서는 Z축 순서를 뜻합니다
view들이 overlap되는 경우, 가장 바닥에 있는 view가 첫번째 item입니다
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는 Regular/Compact를 말합니다)
size class별 프로퍼티 값을 Interface Builder에서 직접 정의할 수 있습니다
시스템은 stack view의 size class 변화에는 자동으로 애니메이션을 적용해줍니다