iOS/App

Scrumdinger 개발 04 - Passing data with bindings

태애니 2025. 4. 3. 22:13
728x90

 

 

Add a theme view

import SwiftUI
import ThemeKit

struct ThemeView: View {
    let theme : Theme
    
    var body: some View {
        Text(theme.name)
            .padding(4)
            .frame(maxWidth: .infinity) //Make the text view span the width of the parent view
            .background(theme.mainColor)
            .foregroundColor(theme.accentColor) //mainColor와의 대비를 최대로 하기 위해 검정 또는 흰색을 반환하게 된다.
            .clipShape(RoundedRectangle(cornerRadius: 4))
    }
}

#Preview {
    ThemeView(theme: .lavender)
}

 

 

Add a theme picker 

import SwiftUI
import ThemeKit

struct ThemePicker: View {
    @Binding var selection: Theme
    
    var body: some View {
        Picker("테마컬러", selection: $selection) {
            ForEach(Theme.allCases) { theme in
                ThemeView(theme: theme)
                    .tag(theme)
            }
        }
        .pickerStyle(.navigationLink)
    }
}

#Preview {
    @Previewable @State var theme = Theme.seafoam
    //Previewable variables are useful for prototyping your app’s user interface.
    ThemePicker(selection: $theme)
}
 

 

 

Pass the edit view a binding to data 

 

 

여기서 여기저기 바뀐게 많은데, 알아둘점은

@State 선언 후 여기저기 하위 뷰들에서 @Binding 받아서 가져다쓰면 된다는 것이다.

 

Preview 에는 @Previewable

 

 

바뀐 부분은 데이터를 주고받는 부분들인데, 일단 전체 코드 첨부!!

 

import SwiftUI

struct DetailEditView: View {
    @Binding var scrum: DailyScrum // 상위 뷰에서 받는 값
//    @State private var scrum = DailyScrum.emptyScrum
    
    @State private var attendeeName = "" // input 받을 참석자명
    
    var body: some View {
        Form {
            Section(header: Text("Meeting Info")) {
                TextField("회의명", text: $scrum.title)
                /**
                 TextField takes a binding to a String. You can use the $ syntax to create a binding to scrum.title. The current view manages the state of the data property.
                 */
                HStack {
                    Slider(value: $scrum.lengthInMinutesAsDouble, in: 5...30, step: 1) {
                        Text("Length")
                        //the slider and the label stay in sync.
                    }
                    .accessibilityValue("\(scrum.lengthInMinutes) minutes") // 접근성
                    Spacer()
                    Text("\(scrum.lengthInMinutes) minutes")
                        .accessibilityValue("\(scrum.lengthInMinutes) minutes")
                        .accessibilityHidden(true) //중복이므로 hidden
                }
                ThemePicker(selection: $scrum.theme)
            }
            Section(header: Text("참석자 목록")) {
                ForEach(scrum.attendees) { attendee in
                    Text(attendee.name)
                }
                .onDelete { indices in
                    scrum.attendees.remove(atOffsets: indices)
                    //onDelete when the user swipes to delete a row
                }
                
                HStack {
                    TextField("새로운 참석자", text: $attendeeName)
                    Button(action: {
                        withAnimation {
                            //setting the value to the empty string also clears the contents of the text field.
                            let attendee = DailyScrum.Attendee(name: attendeeName)
                            scrum.attendees.append(attendee)
                            
                            // binding to attendeeName, setting the value to the empty string also clears the contents of the text field
                            // animation block 안에서 처리!
                            attendeeName = ""
                        }
                    }) {
                        Image(systemName: "plus.circle.fill")
                            .accessibilityLabel("참석자 추가")
                    }
                    .disabled(attendeeName.isEmpty)
                }
            }
        }
    }
}

#Preview {
    @Previewable @State var scrum = DailyScrum.sampleData[0]
    DetailEditView(scrum: $scrum)
}
import SwiftUI

struct DetailView: View {
//    let scrum: DailyScrum
    @Binding var scrum: DailyScrum // binding 받아서 보여준다.
    
    @State private var isPresentingEditView = false //add a Boolean @State property named isPresentingEditView.
    
    
    @State private var editingScrum = DailyScrum.emptyScrum
    
    var body: some View {
        List {
            Section(header: Text("Meeting Info")) {
                //                Label("Start Meeting", systemImage: "timer")
                //                    .font(.headline)
                //                    .foregroundColor(.accentColor)
                
                // 3depth
                NavigationLink(destination: MeetingView()) {
                    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")
                }
            }
        }
        .navigationTitle(scrum.title)
        .toolbar {
            Button("수정") {
                isPresentingEditView = true
            }
        }
        .sheet(isPresented: $isPresentingEditView) {
            NavigationStack{
                DetailEditView(scrum: $editingScrum)// 3depth
                // sheet 에 보여질 내용들을 아래에 구성한다
                    .navigationTitle(scrum.title)
                    .toolbar{
                        ToolbarItem(placement: .cancellationAction) {
                            Button("취소") {
                                isPresentingEditView = false
                            }
                        }
                        ToolbarItem(placement: .confirmationAction) {
                            Button("저장") {
                                isPresentingEditView = false
                            }
                        }
                    }
            }
        }
    }
}

#Preview {
    //Wrap DetailView in a NavigationStack to preview navigation elements on the canvas.
//    NavigationStack {
//        DetailView(scrum: DailyScrum.sampleData[0])
//    }
    
    
    @Previewable @State var scrum = DailyScrum.sampleData[0]
    NavigationStack {
        DetailView(scrum: $scrum)
    }
}

 

import SwiftUI

struct ScrumsView: View {
    //    let scrums: [DailyScrum]
    @Binding var scrums: [DailyScrum]
    
    var body: some View {
        // Identifiable protocol, you can simplify the List initializer.
        //        List(scrums) { scrum in
        //            CardView(scrum: scrum)
        //                .listRowBackground(scrum.theme.mainColor)
        //        }
        
        // NavigationStack
        NavigationStack {
            List($scrums) { $scrum in
                NavigationLink(destination: DetailView(scrum: $scrum)) {
                    //Pass a Binding<DailyScrum> to the DetailView initializer.
                    
                    CardView(scrum: scrum)
                }
                .listRowBackground(scrum.theme.mainColor)
            }
            .navigationTitle("Daily Scrums")
            .toolbar {
                //Pass an empty action to the button for now.
                Button(action: {}) {
                    Image(systemName: "plus")
                }
                .accessibilityLabel("New Scrum")
            }
        }
    }
}

#Preview {
    //    ScrumsView(scrums: DailyScrum.sampleData)
    
    @Previewable @State var scrums = DailyScrum.sampleData
    ScrumsView(scrums: $scrums)
}

 

@main
struct ScrumdingerApp: App {
    //add a private @State property named scrums.
    @State private var scrums = DailyScrum.sampleData
    
    var body: some Scene {
        WindowGroup {
            ScrumsView(scrums: $scrums)
        }
    }
}
728x90