SwiftUI 뷰
- 뷰 : View 프로토콜을 따르는 구조체로 선언
- View 프로토콜을 따르는 구조체는 body 프로퍼티를 가지고 있어야 되며, body 안에 뷰가 선언되어야 함
- 텍스트 레이블, 텍스트 필드, 메뉴 토글, 레이아웃 매니저 뷰 등 다양한 뷰가 내장되어 있음
- 각 뷰는 View 프로토콜을 따르는 독립적인 객체
- 커스텀 뷰의 크기와 복잡성 또는 커스텀 뷰에 캡슐화된 자식 뷰의 개수와는 관계없이, 하나의 커스텀 뷰는 사용자 인터페이스 모양과 동작을 정의하는 하나의 객체
기본 뷰 생성
- 기본 ContentView
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
}
}
- View 프로토콜을 따르며, 필수 요소인 body 프로퍼티 가지고 있음
- 내장 컴포넌트인 Text 뷰의 인스턴스가 body 프로퍼티 포함
뷰 추가
- body 프로퍼티는 하나의 뷰를 변환하도록 구성되어 있어 다음의 예제와 같이 뷰를 추가하면 구문 오류 발생
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, world!")
Text("Hello, world!") // 유효하지 않은 구조
}
}
→ 뷰를 추가하기 위해서는 스택(stack)이나 폼(form) 같은 컨테이너 뷰에 뷰들을 배치해야 함
- 수직 스택 (VStack) 사용 → 뷰들이 수직 방향으로 배치
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, world!")
Text("Hello, world!")
}
}
}
- SwiftUI 뷰는 기본적으로 부모 뷰와 자식 뷰 형태의 계층 구조가 됨
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
VStack {
Text("Text1")
Text("Text2")
HStack{
Text("Text3")
Text("Text4")
}
}
Text("Text5")
}
}
}
- 컨테이너에 포함된 여러 뷰를 연결하면 하나의 뷰로 간주됨
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, ") + Text("how ") + Text("are you?")
}
}
→ body 프로퍼티의 클로저는 반환 구문이 없음 : 단일 표현식이기 때문
- 클로저에 별도의 표현식이 추가되면 return 구문 추가해야 함
import SwiftUI
struct ContentView: View {
var body: some View {
var myString = "Welcome to SwiftUI"
return VStack{
Text("Hello, world!")
Text("Goodbye, world!")
}
}
}
하위 뷰로 작업하기
- 뷰는 최대한 작고 가볍게 → 재사용 가능한 컴포넌트 생성 권장
- 뷰 선언부를 더 쉽게 관리하여 레이아웃이 더 효율적으로 랜더링 가능
- 이전 코드에서 HStack 뷰를 MyHStackView라는 이름의 하위 뷰로 나눌 수 있음
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
VStack {
Text("Text1")
Text("Text2")
MyHStackView()
}
Text("Text5")
}
}
}
struct MyHStackView : View{
var body: some View {
HStack {
Text("Text3")
Text("Text4")
}
}
}
프로퍼티로서의 뷰
- 하위 뷰 생성 외에도 뷰를 계층구조하는 방법
- 프로퍼티를 뷰에 할당
struct ContentView : View {
var body: some View {
VStack {
Text("Main Title")
.font(.largeTitle)
HStack{
Text("Car Image")
Image(systemName: "car.fill")
}
}
}
}
→ 위와 같은 구조에서 선언부 일부를 프로퍼티 값으로 이동하고 이름으로 참조
- HStack을 carStack 이름의 프로퍼티에 할당하고 참조
struct ContentView : View {
let carStack = HStack{
Text("Car Image")
Image(systemName: "car.fill")
}
var body: some View {
VStack {
Text("Main Title")
.font(.largeTitle)
carStack
}
}
}
뷰 변경
- 수정자(modifier)를 뷰에 적용하여 커스터마이징 가능
- 수정자는 뷰의 인스턴스에서 호출하는 메서드 형태를 취함
- 원래의 뷰를 다른 뷰를 감싸는 방식으로 수정자들이 연결되어 뷰를 변경함
// 폰트와 포그라운드 색상 변경
Text("Main Title")
.font(.headline)
.foregroundStyle(.red)
// 이미지 뷰가 허용하는 공간 안에 이미지를 정배율로 표현
Image(systemName: "car.fill")
.resizable()
.aspectRatio(contentMode: .fit)
// 커스텀 뷰의 Text 뷰들의 폰트를 largeTitle 폰트로 변경
MyHStackView()
.font(.largeTitle)
텍스트 스타일로 작업
- 앞의 예제에서는 텍스트 폰트를 내장된 스타일은 largeTitle을 사용함
- 텍스트 크기를 사용자가 선택 가능
- settings → display & Brightness → Text Size 에서 슬라이더로 조정 가능
- 제공되는 스타일
- headline
- subheadline
- body
- callout
- footnote
- caption
- 커스텀 폰트 → 사용자가 선택한 텍스트 크기와 상관없이 고정된 크기
struct ContentView : View {
var body: some View {
Text("Sample Text")
.font(.custom("Copperplate", size: 70))
}
}
수정자 순서
- 수정자들의 순서가 중요함
Text("Sample Text")
.border(Color.black)
.padding()
→ 패딩을 적용시켜서 텍스트와 경계선 사이의 간격을 기대했지만, 없음
- 패딩은 border 경계선 밖에 적용되었기 때문 → 순서를 바꿔야 됨
Text("Sample Text")
.padding()
.border(Color.black)
커스텀 수정자
- 다음의 수정자는 뷰 선언부에 공통으로 필요한 수정자라고 가정
var body: some View {
Text("Text 1")
.font(.largeTitle)
.background(.white)
.border(.gray, width: 0.2)
.shadow(color: .black, radius: 5, x: 0, y: 5)
}
- 위와 같은 수정자 4개가 항상 필요할 때 계속 적용하는 것보다 이 커스텀 수정자를 묶어서 참조 가능함
- 커스텀 수정자는 ViewModifier 프로토콜을 따르는 구조체로 선언 가능
struct StandardTitle : ViewModifier {
func body(content: Content) -> some View {
content
.font(.largeTitle)
.background(.white)
.border(.gray, width: 0.2)
.shadow(color: .black, radius: 5, x: 0, y: 5)
}
}
var body: some View {
Text("Text 1")
.modifier(StandardTitle())
Text("Text 2")
.modifier(StandardTitle())
}
이벤트 처리
- Button 뷰는 버튼 내용과 함께 클릭이 감지될 때 호출될 메서드로 선언되어야 함
- ex) 뷰 전체를 하나의 버튼으로 지정할 수 있음
- 아래는 Text 뷰를 감싸는 Button 뷰로, 클릭하면 buttonPressed() 메서드 호출되는 코드
struct ContentView : View {
var body: some View {
Button(action: buttonPressed, label: {
Text("Click Me")
})
}
func buttonPressed(){
// 동작 코드
}
}
- 액션 함수 지정 대신 클로저 사용 가능
struct ContentView : View {
var body: some View {
// 클로저 사용
Button(action: {
// 동작 코드
}, label: {
Text("Click Me")
})
}
}
- 이미지 뷰를 버튼으로 만드는 경우
struct ContentView : View {
var body: some View {
Button(action: {print("Hello")}, label: {
Image(systemName: "square.and.arrow.down")
})
}
}
onAppear 메서드와 onDisappear 메서드
- 레이아웃 안에 뷰가 나타나거나 사라질 때 초기화 작업과 해제 작업을 수행하기 위해 지정된 뷰에 액션 메서드를 선언하기도 함
struct ContentView : View {
var body: some View {
Text("Hello World")
.onAppear(perform: {
// 뷰가 나타날 때 수행될 코드
})
.onDisappear(perform: {
// 뷰가 사라질 떄 수행될 코드
})
}
}
커스텀 컨테이너 뷰 만들기
- 하위 뷰의 한계 : 컨테이너 뷰의 콘텐트가 정적(static)이라는 것
- 하위뷰가 레이아웃에 포함되는 시점에 동적으로 지정할 수가 없음
- 아래는 3개의 텍스트 뷰가 VStack 안에 포함되며 임의의 간격과 폰트 설정으로 된 하위 뷰
struct MyVStack: View {
var body: some View {
VStack(spacing: 10){
Text("Text Item 1")
Text("Text Item 2")
Text("Text Item 3")
}
.font(.largeTitle)
}
}
→ 폰트와 간격만 같게 하고 싶을 때 다른 뷰들을 생성하지 못한다는 단점 : 유연성이 떨어짐 → ViewBuilder 클로저 속성 이용
- ViewBuilder
- 클로저 형태
- 하위 뷰로 구성된 커스텀 뷰를 만들 때 사용
- 레이아웃 선언부 내에 사용될 때까지 내용을 선언할 필요가 없음
- 콘텐트 뷰들을 받아서 동적으로 만들어진 단일 뷰로 반환함
// ViewBuilder 사용
struct MyVStack<Content: View>: View {
let content : () -> Content
init(@ViewBuilder content : @escaping () -> Content){
self.content = content
}
var body: some View {
VStack(spacing: 10){
content()
}
.font(.largeTitle)
}
}
- View 프로토콜을 따름
- body에는 VStack 선언부 포함
- 스택에 정적 뷰를 포함하는 대신 하위 뷰들은 초기화 메서드에 전달됨
- ViewBuilder에 의해 처리되어 VStack에 하위뷰들로 포함
- 커스텀 MyStack 뷰는 레이아웃 내에 사용될 서로 다른 하위 뷰들로 초기화 가능
struct ContentView : View {
var body: some View {
MyVStack{
Text("Text 1")
Text("Text 1")
HStack{
Image(systemName: "star.fill")
Image(systemName: "star.fill")
Image(systemName: "star")
}
}
}
}
'SwiftUI' 카테고리의 다른 글
SwiftUI - 상태 프로퍼티 (State), Observable 객체, Environment 객체 사용 (0) | 2024.01.15 |
---|---|
SwiftUI - 스택, 프레임 (1) | 2024.01.13 |
SwiftUI - 프로젝트 분석 (0) | 2024.01.07 |
SwiftUI - SwiftUI 모드로 Xcode 이용 (1) | 2024.01.07 |
SwiftUI - 개요 (0) | 2024.01.07 |