SwiftUI로 UI를 구현할 때의 패러다임, 즉 근본적인 사고방식과 틀은 ”뷰 안의 뷰”이다.
SwiftUI에서는 여러개의 작은 뷰를 더 큰 뷰에 넣어 UI를 구현한다.
여기서 이야기하는 작은 뷰, 큰 뷰는 무엇을 이야기하는 걸까?
SwiftUI의 뷰들은 레고와 비슷하다.
우리는 작은 레고 블럭을 조립해 의자, 자동차, 책상같은 레고 블럭을 만들고, 그 블럭들을 조합해 더 큰 블럭을 만든다.
예를 들어, 레고로 심슨의 집을 만들고 싶다면 레고 블럭을 조립해서 소파, TV 등의 블럭을 만들고 이 블럭들을 조합해 거실, 침실같은 방을, 그 방을 조합해 집을 완성한다.
SwiftUI에서의 View도 이와 같은 방식으로 구현된다.
뷰들을 조합해 더 큰 뷰를 만들고, 이 뷰들을 조합해서 또 더 큰 뷰를 만든다.
이 레고블럭들을 SwiftUI의 뷰들과 매치시켜보자면 다음과 같이 정리해볼 수 있을 것 이다.
RoundedRectangle, Circle, Text, Button 등의 UI Element
HStack, VStack, ZStack, LazyVGrid 등의 Container(Combiner View라고도 부른다)에 블럭 조각을 넣어 더 큰 뷰를 만든다.
위에서 만든 Element들과 Container들을 조합해서 더 큰 뷰를 만든다.
우리가 레고를 조립하기 위해 박스를 뜯어보면 아래 사진처럼 레고 블럭들이 비닐 백에 나눠 담겨있다.
조립하기 쉽도록 부품별로 필요한 레고블럭을 나눠서 담아 놓은 것이다.
비닐 백 하나는 날개 부분, 다른 비닐백은 조종석 부분 등으로 작은 블럭을 만들기 위한 레고 블럭들이 나뉘어 담겨 있다.
SwiftUI에서도 작은 뷰들로 큰 뷰를 만들 때, 작은 뷰들을 백에 담아서 Combiner View에게 전달한다.
‘백에 담는다’는 표현이 모호할 수 있는데 쉽게 말하면 레고 블럭들, 즉 여러 UI Element를 나열해 하나의 블럭으로 만든다는 것이다.
다음과 같이 말이다.
ZStack {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.white)
RoundedRectangle(cornerRadius: 20)
.strokeBorder(lineWidth: 3)
.foregroundColor(.yellow)
Text(content)
.font(.largeTitle)
}
이렇게 작은 뷰들을 하나의 블럭으로 만드는 것에는 ViewBuilder와 ForEach 등이 있는데, 이 녀석들에 대해서는 다음에 더 알아보도록 하자.
지금까지 이야기했던 내용을 실제로 View 코드를 짜는 걸 보면서 정리해보자.
코드와 프리뷰를 함께 보면, 아래와 같은 모습을 확인할 수 있다.
공부할수록 블럭을 가지고 조립하면서 노는 것 같아서 정말 재미있다😎
struct ContentView: View {
let emojis: [String] = ["✈️", "🚌", "🚟", "🛵", "⛵️", "🚡", "🚗", "🛻", "🚂", "🛳", "🚉", "🚀", "🛶", "🚁", "🚜", "🚁"]
@State var emojiCount = 4
var body: some View {
VStack {
ScrollView {
LazyVGrid(columns: [GridItem(), GridItem()]) {
ForEach(emojis, id:\.self) { emoji in
CardView(content: emoji)
.aspectRatio(2/3, contentMode: .fit)
}
}
}
Spacer(minLength: 30)
HStack {
button
.font(.largeTitle)
button
.font(.largeTitle)
button
.font(.largeTitle)
}
}
}
var button: some View {
Button {
emojiCount += 1
} label: {
Image(systemName: "plus.circle")
}
}
}
struct CardView: View {
var content: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 20)
.strokeBorder(lineWidth: 3)
.foregroundColor(.yellow)
Text(content)
.font(.largeTitle)
}
}
}