iOS/App

Scrumdinger 분석하기 03 /scrum/DetailView.swift

태애니 2025. 4. 28. 16:59
728x90
import SwiftUI
import SwiftData

struct DetailView: View {
    let scrum: DailyScrum // swiftData
    
    @State private var isPresentingEditView = false //add a Boolean @State property named isPresentingEditView.  
    @State private var errorWrapper: ErrorWrapper? // error
    
    var body: some View {
        List {
            Section(header: Text("Meeting Info")) {
                // 3depth
                NavigationLink(destination: MeetingView(scrum: scrum, errorWrapper: $errorWrapper)) { //errorWrapper 추가
                    Label("Start Meeting", systemImage: "timer")
                        .font(.headline)
                        .foregroundColor(.accentColor)
                }
                
                HStack {
                    Label("Length", systemImage: "clock")
                    Spacer()
                    Text("\(scrum.lengthInMinutes) minutes")
                }
                .accessibilityElement(children: .combine)
                /**
                 VoiceOver 사용 시 설정
                 
                 .accessibilityElement
                 .accessibilityLabel("내 맘대로 정의") // 사용자가 정의한 라벨 적용
                 SwiftUI에서 접근성(VoiceOver 등)이 특정 UI 요소를 어떻게 인식할지 정의
                 
                 .ignore : 접근성 요소로 인식하지 않음
                 .combine : 하나의 요소로 읽음
                 .contain : 개별로 읽음
                 */
                
                HStack {
                    Label("Theme", systemImage: "paintpalette")
                    Spacer()
                    Text(scrum.theme.name)
                        .padding(4)
                        .foregroundColor(scrum.theme.accentColor)
                        .background(scrum.theme.mainColor)
                        .cornerRadius(4)
                }
                .accessibilityElement(children: .combine)
            }
            
            // 참석자
            Section(header: Text("Attendees")) {
                ForEach(scrum.attendees) { attendee in
                    Label(attendee.name, systemImage: "person")
                }
            }
            
            // history
            Section(header: Text("회의내역")) {
                if scrum.history.isEmpty {
                    Label("회의 내역이 없습니다.", systemImage: "calendar.badge.exclamationmark")
                        .foregroundColor(.gray)
                }
                
                ForEach(scrum.history) { history in
                    NavigationLink(destination: HistoryView(history: history)) {
                        HStack {
                            Image(systemName: "calendar")
                            Text(history.date, style: .date)
                        }
                    }
                }
            }
        }
        .navigationTitle(scrum.title)
        .toolbar {
            Button("수정") {
                isPresentingEditView = true
            }
        }
        .sheet(isPresented: $isPresentingEditView) {
            NavigationStack{
                DetailEditView(scrum: scrum)
                    .navigationTitle(scrum.title)
            }
        }
        .sheet(item: $errorWrapper, onDismiss: nil) { wrapper in //error
            ErrorView(errorWrapper: wrapper)
        }
    }
}

#Preview {
    NavigationStack {
        DetailView(scrum: DailyScrum.sampleData[0])
    }
}

 

 

let scrum: DailyScrum // 읽기전용 

 

List 스크롤이 가능한 목록을 자동으로 만들어주는 컴포넌트로, 테이블 뷰의 형태를 하고 있다.

 

List 에는 선택할 수 있는게 자동 기능이 있다. 와우

@State private var multiSelection = Set<UUID>()


var body: some View {
    NavigationView {
        List(oceans, selection: $multiSelection) {
            Text($0.name)
        }
        .navigationTitle("Oceans")
        .toolbar { EditButton() }
    }
    Text("\(multiSelection.count) selections")
}

 

리스트 땡겼다가 놨을 때 새로고침하는 인스턴스 메소드도 추가 가능하다.

https://developer.apple.com/documentation/swiftui/view/refreshable(action:)

 

refreshable(action:) | Apple Developer Documentation

Marks this view as refreshable.

developer.apple.com

refreshable(action:)

 

 

https://developer.apple.com/documentation/swiftui/displaying-data-in-lists

 

Displaying data in lists | Apple Developer Documentation

Visualize collections of data with platform-appropriate appearance.

developer.apple.com

 

 

Supporting multidimensional lists

 

List | Apple Developer Documentation

A container that presents rows of data arranged in a single column, optionally providing the ability to select one or more members.

developer.apple.com

 

 

 

여기서 또 하나 찾게 된 키워드는 ScrollViewReader

 

https://developer.apple.com/documentation/swiftui/scrollviewreader

 

ScrollViewReader | Apple Developer Documentation

A view that provides programmatic scrolling, by working with a proxy to scroll to known child views.

developer.apple.com

 

ScrollViewProxy를 사용해서 특정 ID를 가진 뷰로 이동할 수 있다.

 

 

 

 

 

 

 

[사용자 액션]
   ↓
[View의 이벤트 발생 (버튼 클릭 등)]
   ↓
[dismiss 호출]                 [context 사용: insert/save 등]
   ↓                                ↓
[현재 View를 스택/시트에서 제거]   [메모리에 추가/변경 기록]
                                    ↓
                           [context.save() 호출]
                                    ↓
                           [SwiftData 저장소에 반영 (디스크 기록)]

 

 

 

 

dismiss 호출 시

 

  • 메모리 안에서 뷰 인스턴스 자체가 사라짐.
  • View 관련 State들도 해제됨 (@State, @Binding, @Environment 모두 메모리에서 정리)
  • 저장할 데이터가 있다면, View 닫기 전에 따로 저장

 

 

 

 

사용자가 TextField 입력 or Slider 조정
   ↓
@State 변수들(title, attendees, theme 등)이 변함
   ↓
사용자가 "Done" 버튼 클릭
   ↓
saveEdits() 호출
   ↓
scrum.title 등 모델 프로퍼티 업데이트
   ↓
context.insert(scrum) or 기존 데이터 수정
   ↓
(아직 메모리 안에서만 반영된 상태)
   ↓
try context.save() 호출
   ↓
SwiftData 엔진이:
  - 메모리 안 context 변경사항을 
  - 디스크에 영구 저장 (sqlite 기반 데이터베이스 등)
   ↓
저장이 성공하면 완료
(에러 나면 ErrorWrapper로 잡음)

 

 

728x90