.sheet가 동작하는 과정 완전 이해하기SwiftUI로 앱을 만들다 보면, 리스트에서 아이템을 선택하면 상세 화면이 뜨는 패턴을 자주 구현하게 된다.
이번에는 FrameworkGridView, FrameworkGridViewModel, FrameworkDetailView 세 가지를 기준으로 .sheet가 어떻게 동작하는지 하나씩 뜯어보자.
@StateObject var viewModel = FrameworkGridViewModel()
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(MockData.frameworks, id: \.id) { framework in
FrameworkTitleView(framework: framework)
.onTapGesture {
viewModel.selectedFramework = framework
}
}
}
}
.navigationTitle("🍎 Frameworks")
.sheet(isPresented: $viewModel.isShowingDetailView) {
FrameworkDetailView(
frameWork: viewModel.selectedFramework!,
isShowingDetailView: $viewModel.isShowingDetailView
)
}
}
}
final class FrameworkGridViewModel: ObservableObject {
var selectedFramework: Framework? {
didSet {
isShowingDetailView = true
}
}
@Published var isShowingDetailView = false
}
struct FrameworkDetailView: View {
var frameWork: Framework
@Binding var isShowingDetailView: Bool
var body: some View {
VStack {
HStack {
Spacer()
Button {
isShowingDetailView = false
} label: {
Image(systemName: "xmark")
.foregroundColor(Color(.label))
.imageScale(.large)
.frame(width: 44, height: 44)
}
}
.padding()
Spacer()
FrameworkTitleView(framework: frameWork)
Text(frameWork.description)
.font(.body)
.padding()
Spacer()
Button {
// 더 알아보기 로직
} label: {
AFButton(title: "더 알아보기")
}
}
}
}
onTapGesture로 selectedFramework 주입FrameworkGridView에서 사용자가 특정 아이템을 탭하면
viewModel.selectedFramework에 선택한 framework가 할당된다.
.onTapGesture {
viewModel.selectedFramework = framework
}
즉, FrameworkGridView는 이미 생성된 ViewModel 인스턴스를 사용하며,
선택한 데이터를 ViewModel에 넘겨주는 역할만 한다.
didSet을 통한 isShowingDetailView 변경FrameworkGridViewModel의 핵심 로직은 selectedFramework의 didSet이다.
var selectedFramework: Framework? {
didSet {
isShowingDetailView = true
}
}
isShowingDetailView가 true로 바뀐다.즉, 선택과 동시에 상세 화면을 띄우라는 신호를 보내는 구조다.
@Published와 .sheet의 반응isShowingDetailView는 @Published이기 때문에,
값이 변경되면 이 값을 감시하고 있는 View가 즉시 반응한다.
.sheet(isPresented: $viewModel.isShowingDetailView) {
FrameworkDetailView(
frameWork: viewModel.selectedFramework!,
isShowingDetailView: $viewModel.isShowingDetailView
)
}
즉,
isShowingDetailView = true → .sheet가 뜬다isShowingDetailView = false → .sheet가 닫힌다FrameworkDetailView 내부에서 xmark 버튼을 눌러 isShowingDetailView = false로 바꾸면,
자동으로 시트가 사라지는 이유도 여기에 있다.
.sheet가 실행될 때, selectedFramework에 저장된 데이터가 그대로 전달된다.
FrameworkDetailView(
frameWork: viewModel.selectedFramework!,
isShowingDetailView: $viewModel.isShowingDetailView
)
즉, FrameworkDetailView가 보여줄 데이터는
onTapGesture에서 선택된 그 데이터 그대로다.
selectedFramework에 선택된 데이터가 주입된다.didSet 실행 → isShowingDetailView가 true로 변경된다..sheet가 자동으로 뜬다.selectedFramework가 FrameworkDetailView에 전달되어 View에 표시된다.isShowingDetailView = false로 변경되어 .sheet가 닫힌다.| 단계 | 설명 |
|---|---|
| 데이터 선택 | onTapGesture로 ViewModel의 selectedFramework가 갱신된다 |
| 상태 변화 | didSet으로 isShowingDetailView가 true로 바뀐다 |
| UI 반응 | @Published가 변화를 감지해 .sheet를 띄운다 |
| 데이터 전달 | .sheet에서 선택된 Framework를 상세 화면에 넘겨준다 |
즉, View → ViewModel → View의 단방향 흐름으로 동작하며,
상태 변경은 ViewModel이 책임지고, View는 그 상태를 구독하며 UI를 업데이트한다.
