[SwiftUI]SwiftUI 커스텀 뷰 선언하기
개요
SwiftUI는 사용자 인터페이스 디자인을 선언형(declarative) 방식으로 제공합니다. 전통적인 명령형(imperative) 방식에서는 뷰를 생성하고, 배치하고, 설정하는 책임은 물론, 상태 변화에 따라 뷰를 지속적으로 업데이트하는 책임까지 컨트롤러 코드가 부담합니다.
반면, 선언형 방식에서는 UI의 원하는 레이아웃을 반영하는 뷰 계층을 선언함으로써, 사용자 인터페이스의 간단한 설명(description)을 만듭니다. 그 이후에는 SwiftUI가 사용자 입력이나 상태 변화 같은 이벤트에 반응하여 뷰를 그리거나 업데이트하는 과정을 관리합니다.
SwiftUI는 사용자 인터페이스 내에서 뷰를 정의하고 구성하기 위한 도구를 제공합니다. SwiftUI가 제공하는 내장 뷰들과 이미 정의된 다른 컴포지트 뷰들을 조합하여 커스텀 뷰를 만들 수 있습니다. 뷰는 수정자(modifier)를 통해 설정하고, 데이터 모델과 연결할 수 있습니다. 이후 이러한 커스텀 뷰를 앱의 뷰 계층 안에 배치할 수 있습니다.
View 프로토콜을 따르기
커스텀 뷰 타입을 선언하려면, View 프로토콜을 따르는 구조체를 정의해야 합니다:
1
2
3
4
5
struct MyView: View {
}
Swift의 다른 프로토콜들과 마찬가지로, View 프로토콜은 특정 기능의 설계도를 제공합니다. 여기서는 SwiftUI가 화면에 그리는 요소의 동작을 정의합니다. 프로토콜을 따르기 위해 필요한 요구사항을 충족하고 나면, 해당 뷰를 뷰 계층에 삽입하여 앱의 사용자 인터페이스 일부로 만들 수 있습니다.
body 선언하기
View 프로토콜의 주요 요구사항은 body라는 계산 프로퍼티(computed property)를 정의하는 것입니다:
1
2
3
4
5
6
7
8
9
struct MyView: View {
var body: some View {
}
}
SwiftUI는 뷰를 업데이트할 필요가 있을 때마다 이 프로퍼티의 값을 읽습니다. 이러한 업데이트는 사용자 입력이나 시스템 이벤트에 반응하여 반복적으로 일어날 수 있습니다. 이때 반환된 값은 SwiftUI가 화면에 그리는 요소입니다.
View 프로토콜의 부가적인 요구사항은 body 프로퍼티에 대한 연관 타입(associated type)을 지정해야 한다는 것입니다. 하지만 이 타입을 명시적으로 선언할 필요는 없습니다. 대신 some View라는 **불투명 타입(opaque type)**을 사용하여 body의 반환값이 View 프로토콜을 따른다는 사실만 명시합니다. 정확한 타입은 body의 내용에 따라 달라지며, Swift가 이를 자동으로 추론합니다.
뷰 콘텐츠 구성하기
body 프로퍼티에 콘텐츠를 추가하여 뷰의 외형을 설명합니다. SwiftUI에서 제공하는 내장 뷰는 물론, 다른 곳에서 정의한 커스텀 뷰도 사용할 수 있습니다. 예를 들어, 내장 Text 뷰를 사용하여 “Hello, World!”라는 문자열을 표시할 수 있습니다:
1
2
3
4
5
6
7
8
9
10
11
struct MyView: View {
var body: some View {
Text("Hello, World!")
}
}
SwiftUI는 Text, Toggle, ProgressView와 같은 특정 콘텐츠를 위한 뷰 외에도, 다른 뷰들을 정렬하는 데 사용할 수 있는 내장 뷰들을 제공합니다. 예를 들어 VStack을 사용해 두 개의 Text 뷰를 수직으로 쌓을 수 있습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct MyView: View {
var body: some View {
VStack {
Text("Hello, World!")
Text("Glad to meet you.")
}
}
}
위 예시처럼 여러 자식 뷰를 입력받는 뷰는 일반적으로 ViewBuilder 특성이 지정된 클로저를 통해 구성됩니다. 이로 인해 호출 시점에서 별도의 문법 없이 여러 구문을 나열할 수 있습니다.
뷰를 수정자로 구성하기
body에 정의된 뷰를 설정하려면 **뷰 수정자(view modifier)**를 적용합니다. 수정자는 뷰에 호출되는 메서드일 뿐이며, 호출 시 원래 뷰 대신 사용할 수 있는 새로운 뷰를 반환합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct MyView: View {
var body: some View {
VStack {
Text("Hello, World!")
.font(.title)
Text("Glad to meet you.")
}
}
}
데이터 관리하기
뷰에 값을 전달하려면 속성(property)을 추가하세요:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct MyView: View {
let helloFont: Font
var body: some View {
VStack {
Text("Hello, World!")
.font(helloFont)
Text("Glad to meet you.")
}
}
}
SwiftUI는 입력값 변경을 감지하고 관련된 UI만 다시 그립니다. 따라서 SwiftUI가 뷰를 언제든지 재초기화할 수 있다는 점을 감안하여, 초기화 시 무거운 작업은 피해야 합니다.
뷰 계층에 추가하기
정의한 뷰는 내장 뷰처럼 다른 뷰 안에 포함시킬 수 있습니다. 예를 들어 앱의 루트 뷰인 ContentView에 MyView를 포함할 수 있습니다:
1
2
3
4
5
6
7
8
9
10
11
struct ContentView: View {
var body: some View {
MyView(helloFont: .title)
}
}
또는 앱 내 새로운 Scene의 루트 뷰로도 사용할 수 있습니다. SwiftUI로 앱 구조를 설계하는 방법은 App organization을 참고하세요.



