화면이 2개인데 데이터를 어떻게 공유하는가
struct RootView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
List {
NavigationLink("프로필",
value: "profile")
NavigationLink("설정",
value: "settings")
}
.navigationTitle("메뉴")
.navigationDestination(
for: String.self
) { value in
Text("\(value) 화면")
}
}
}
}
NavigationLink(destination:)과 .navigationDestination(for:)의 차이는 무엇인가?NavigationPath를 @State로 관리하면 어떤 것이 가능해지는가?struct ModalDemo: View {
@State private var showSheet = false
@State private var showFull = false
var body: some View {
VStack(spacing: 16) {
Button("Sheet 열기") {
showSheet = true
}
Button("FullScreen 열기") {
showFull = true
}
}
.sheet(isPresented: $showSheet) {
Text("Sheet!")
.presentationDetents(
[.medium, .large])
}
.fullScreenCover(
isPresented: $showFull
) {
Text("Full Screen!")
}
}
}
.sheet()과 .fullScreenCover()는 시각적으로 어떻게 다른가?.presentationDetents([.medium, .large])는 무엇을 조절하는가?class TodoStore: ObservableObject {
@Published var items: [String] = []
func add(_ item: String) {
items.append(item)
}
}
struct ParentView: View {
@StateObject var store = TodoStore()
var body: some View {
ChildView(store: store)
}
}
struct ChildView: View {
@ObservedObject var store: TodoStore
var body: some View {
List(store.items, id: \.self) {
Text($0)
}
}
}
@StateObject와 @ObservedObject는 언제 각각 사용하는가?@StateObject를 쓰면 왜 위험한가?ObservableObject의 @Published 프로퍼티가 변경되면 어떤 일이 일어나는가?// iOS 17+ 방식
@Observable
class UserSettings {
var username = "개발자리"
var isDarkMode = false
}
struct AppRoot: View {
@State var settings = UserSettings()
var body: some View {
SettingsView()
.environment(settings)
}
}
struct SettingsView: View {
@Environment(UserSettings.self)
var settings
var body: some View {
Text(settings.username)
}
}
@Observable 매크로(iOS 17+)를 쓰면 @Published가 왜 필요 없어지는가?@EnvironmentObject는 어떤 문제를 해결하기 위해 존재하는가? (prop drilling이란?)@Environment(\.dismiss)에서 \.dismiss는 어디에 정의되어 있는가?struct MainTabView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Label("홈",
systemImage: "house")
}
SearchView()
.tabItem {
Label("검색",
systemImage:
"magnifyingglass")
}
ProfileView()
.tabItem {
Label("프로필",
systemImage: "person")
}
}
}
}
TabView(selection: $selectedTab)으로 프로그래밍 방식 탭 전환이 필요한 경우는 언제인가?.tabItem { }에 넣을 수 있는 View의 종류에 제한이 있는가?struct MainTabView: View {
var body: some View {
TabView {
NavigationStack {
List {
NavigationLink("공지사항") {
Text("공지 상세")
}
NavigationLink("이벤트") {
Text("이벤트 상세")
}
}
.navigationTitle("홈")
}
.tabItem {
Label("홈",
systemImage: "house")
}
NavigationStack {
Text("검색 화면")
.navigationTitle("검색")
}
.tabItem {
Label("검색",
systemImage: "magnifyingglass")
}
NavigationStack {
Text("내 프로필")
.navigationTitle("프로필")
}
.tabItem {
Label("프로필",
systemImage: "person")
}
}
}
}
@Observable
class AppData {
var favorites: [String] = []
func toggle(_ item: String) {
if favorites.contains(item) {
favorites.removeAll { $0 == item }
} else {
favorites.append(item)
}
}
}
struct AppRoot: View {
@State var data = AppData()
var body: some View {
TabView {
ItemListView()
.tabItem { Label("목록",
systemImage: "list.bullet") }
FavoritesView()
.tabItem { Label("즐겨찾기",
systemImage: "heart") }
}
.environment(data)
}
}
struct ItemListView: View {
@Environment(AppData.self) var data
let items = ["사과", "바나나", "포도"]
var body: some View {
List(items, id: \.self) { item in
HStack {
Text(item)
Spacer()
Image(systemName:
data.favorites.contains(item)
? "heart.fill" : "heart")
.foregroundStyle(.red)
.onTapGesture {
data.toggle(item)
}
}
}
}
}
struct FavoritesView: View {
@Environment(AppData.self) var data
var body: some View {
List(data.favorites, id: \.self) {
Text($0)
}
}
}
struct Memo: Identifiable {
let id = UUID()
var title: String
var body: String
var date = Date()
}
class MemoStore: ObservableObject {
@Published var memos: [Memo] = [
Memo(title: "첫 번째 메모",
body: "SwiftUI 공부 시작!")
]
}
struct MemoListView: View {
@StateObject var store = MemoStore()
@State private var showNew = false
var body: some View {
NavigationStack {
List {
ForEach(store.memos) { memo in
NavigationLink {
MemoDetailView(memo: memo)
} label: {
VStack(alignment: .leading) {
Text(memo.title)
.font(.headline)
Text(memo.body)
.font(.caption)
.foregroundStyle(
.secondary)
.lineLimit(1)
}
}
}
}
.navigationTitle("메모")
.toolbar {
Button { showNew = true } label: {
Image(systemName: "plus")
}
}
.sheet(isPresented: $showNew) {
NewMemoView(store: store)
}
}
}
}
struct MemoDetailView: View {
let memo: Memo
var body: some View {
ScrollView {
Text(memo.body)
.padding()
}
.navigationTitle(memo.title)
}
}
struct NewMemoView: View {
@ObservedObject var store: MemoStore
@Environment(\.dismiss) var dismiss
@State private var title = ""
@State private var body = ""
var body_view: some View {
NavigationStack {
Form {
TextField("제목", text: $title)
TextEditor(text: $body)
.frame(minHeight: 120)
}
.navigationTitle("새 메모")
.toolbar {
Button("저장") {
store.memos.append(
Memo(title: title,
body: body))
dismiss()
}
}
}
}
}