안녕하세요. 오늘은 SwiftUI의 Layout에 대해서 알아보도록 하겠습니다.
공식 문서에 따르면 Layout의 정의는 “A type that defines the geometry of a collection of views.” 입니다. 의역을 하자면 view들의 collection의 “기하학”을 정의하는 타입이라고 하는데요. 간단하게 이야기하면 여러 개의 view가 각각 어디에 위치할지 정의할 때 사용한다고 보면 되겠습니다.
SwiftUI는 여러 개의 view를 어디 위치시킬지 정의하는 타입이 이미 여러가지 있습니다. 대표적인 것이 VStack, HStack, ZStack이 되겠는데요. VStack은 이전 view의 아래에, HStack은 이전 view의 오른쪽에, ZStack은 이전 view의 (z축 상의) 앞에 위치시킵니다.
이렇게 기본적으로 주어진 Layout 이외에 좀 더 복잡한 Layout을 정의하고 싶을 때 Layout을 활용하면 됩니다.
Layout protocol을 상속 받은 struct는 내부에 sizeThatFits와 placeSubviews의 2종류의 함수를 구현해야 합니다. 함수를 하나하나 뜯어보도록 하겠습니다.
먼저 sizeThatFits 함수입니다. 해당 함수는 layout의 전체 크기를 정해주는 함수입니다. proposal, subviews, cache를 인자로 받습니다.
먼저 proposal은 ProposedViewSize 타입인데요. 쉽게 말하면 부모 View에서 받은 사이즈라고 보시면 될 것 같습니다. 부모 View에서 해당 Layout에 부여할 수 있는 크기를 제안하고 그것을 활용해서 layout의 크기를 구하는데 사용합니다.
subviews는 우리가 Layout 안에 전달한 뷰들입니다. 해당 view들의 사이즈를 가지고 전체 layout의 크기를 구하면 됩니다.
마지막으로 cache인데요. 이 부분에 대한 설명은 이번 포스팅에서는 생략하겠습니다.
위 3가지를 활용해서 layout의 사이즈를 계산하고 CGSize로 리턴해주면 됩니다.
func sizeThatFits(
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout ()
) -> CGSize {
// Calculate and return the size of the layout container.
}
다음은 placeSubviews 함수인데요. 이 함수는 실제로 subview를 배치하는 함수입니다. 인자는 총 4가지입니다.
처음은 bounds인데요. CGRect 타입입니다. 전체 layout이 차지하는 영역을 의미합니다. x, y좌표와 width, height 정보 등 subview를 배치하기 위한 유용한 정보들이 많이 저장되어 있습니다.
proposal와 subview는 sizeThatFits에서 설명한 것과 동일한 객체입니다. cache는 말씀 드렸듯이 생략합니다.
subviews는 Collection 타입으로 아래의 예시와 같이 반복문을 사용할 수 있습니다. 우리가 layout의 {} 안에 전달한 순서대로 subview들이 저장되어 있습니다. 각각의 subview들은 place라는 함수를 통해서 실제로 View에 배치할 수 있습니다.
func placeSubviews(
in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout ()
) {
var point = bounds.origin
for subview in subviews {
subview.place(at: point, anchor: .topLeading, proposal: .unspecified)
point.y += subview.dimensions(in: .unspecified).height
}
}
다음 포스팅에서 이 함수들을 활용해서 원하는 Layout을 만들어보겠습니다.