Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
Tags
- uiscrollview
- Clean Code
- Protocol
- ribs
- 클린 코드
- SWIFT
- Human interface guide
- rxswift
- 스위프트
- 리펙터링
- tableView
- UICollectionView
- RxCocoa
- UITextView
- ios
- Xcode
- clean architecture
- MVVM
- Observable
- map
- HIG
- Refactoring
- 애니메이션
- 리펙토링
- swiftUI
- uitableview
- swift documentation
- combine
- collectionview
- 리팩토링
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - SwiftUI] 뷰 주입받는 방법 (Generics, AnyView) 본문
뷰 주입받는 방법 3가지
특성 | 단점 | ||
1. generics | 최적화 유지, 타입 안전 | 뷰 타입이 고정됨 | ⭐ 추천 |
2. AnyView | 동적 뷰 변경 가능 | 성능 저하 가능성 | ⚠️ 가능하면 피할 것 |
1. generics 방법
- View를 준수하는 타입으로 한정하여, 제네릭을 선언하는 방식
- 최적화가 유지되는 장점
// 주입받은 뷰를 표시하는 컨테이너
struct ContainerView<Content: View>: View {
let content: Content
var body: some View {
VStack {
Text("현재 뷰")
.font(.headline)
content
}
.padding()
.background(Color.gray.opacity(0.2))
.cornerRadius(10)
}
}
// 외부에서 동적으로 뷰를 주입
struct ContentView: View {
@State private var isTextView = true
var body: some View {
VStack {
if isTextView {
ContainerView(content: Text("이것은 텍스트 뷰"))
} else {
ContainerView(content: Button("버튼입니다") {})
}
Button("뷰 변경") {
isTextView.toggle()
}
.padding()
}
}
}
- 단점은 뷰 타입이 고정
- currentView에서 ContainerView에서 제네릭 타입이 Text로 지정되었지만 밑에서 Button을 넣어서 컴파일 오류가 발생
struct ContentView: View {
@State private var isTextView = true
@State private var currentView = ContainerView(content: Text("초기 뷰"))
var body: some View {
VStack {
currentView
Button("뷰 변경") {
if isTextView {
currentView = ContainerView(content: Button("버튼입니다") {})
// ❌ 컴파일 오류: Text와 Button은 서로 다른 타입
} else {
currentView = ContainerView(content: Text("다시 텍스트 뷰"))
}
isTextView.toggle()
}
}
}
}
- 주의)
- if-else는 서로 다른 뷰 트리이므로 아래처럼 사용하면 컴파일 에러 발생 x
struct ContentView: View {
@State private var isTextView = true
var body: some View {
VStack {
if isTextView {
ContainerView(content: Text("이것은 텍스트 뷰"))
} else {
ContainerView(content: Button("버튼입니다") {})
}
Button("뷰 변경") {
isTextView.toggle()
}
.padding()
}
}
}
cf) @ViewBuilder와 같이 사용하는패턴
- 아래 setView2처럼 파라미터에 @ViewBuilder를 선언하고 제네릭을 반환하는데 이때 @ViewBuilder는 이 함수를 사용하는 쪽에서 선언적으로 사용할 수 있는 기능
struct MyView<Content: View>: View {
let content: Content?
var body: some View {
content
}
init(content: (() -> Content)? = nil) {
self.content = content?()
}
func getView1(_ view: () -> Content) -> some View {
VStack {
Text("")
view()
}
}
func getView2(@ViewBuilder _ view: () -> Content) -> some View {
VStack {
Text("")
view()
}
}
}
// 사용하는 쪽
struct ContentView: View {
var body: some View {
MyView().getView1 {
Text("1")
}
MyView().getView2 {
Text("1")
Text("2")
}
}
}
2. AnyView 방법
- AnyView는 뷰 타입들을 지워버려서 사용할때 편리하게 할 수 있는 효과가 있으나, 단점이 여러가지가 존재
- SwiftUI 내부적으로 뷰 트리 구조 파악이 어려워서 재랜더링되는 효과
- 타입 정보를 잃어버려서 성능 저하의 위험이 존재
struct ContentView: View {
@State private var currentView: AnyView = AnyView(Text("초기 텍스트 뷰"))
var body: some View {
VStack {
currentView
Button("뷰 변경") {
if currentView is AnyView, currentView.body is Text {
currentView = AnyView(Button("버튼 뷰") {})
} else {
currentView = AnyView(Text("다시 텍스트 뷰"))
}
}
.padding()
}
}
}
결론
- 뷰를 주입받을 땐 성능을 생각하여 AnyView 사용을 지양하고 generics를 사용할 것
'iOS 응용 (SwiftUI)' 카테고리의 다른 글
[iOS - SwiftUI] Spacer(minLength:)를 사용할때 주의할 점 (0) | 2025.03.05 |
---|---|
[iOS - SwiftUI] 그라데이션 넣는 방법 (Linear Gradation) (0) | 2025.02.26 |
[iOS - SwiftUI] 뷰에서 if, else 구분해야할때 id 활용하는 방법 (SwiftUI의 id) (0) | 2025.02.19 |
[iOS - SwiftUI] 키보드 높이 구하는 방법 (#combine) (0) | 2025.02.12 |
[iOS - SwiftUI] HStack, VStack에서의 alignment 개념 (center, top, bottom, firstTextBaseline, lastTextBaseline) (0) | 2025.01.28 |
Comments