그동안 내가 궁금해했던 CRUD중 data를 Update하는 파트를 정리해보자!
우선 흐름을 먼저 잡아보자
- DetailEditView 생성
- 기존 data 불러와서 DetailEditView에도 띄우기
- 데이터 수정
- 수정된 데이터 저장
- 기존 View에 갱신된 데이터로 띄우기 (사실 4까지 잘 되면 5는 자동으로 됨. 그냥 저장된 데이터를 불러오는 코드이므로)
각 흐름에 대한 key를 정리해보자
- 쓰이는 코드들 : [ TextField, 이미 만들어놓은 PickerView, Slider의 value값, 새로운 멤버 append ]
- DetailView에서 @State로 선언된 data -> DetailEditView에서 @Binding wrapper로 받는다 (& 데이터 타입 선언)
- $(바인딩)으로 각각 value 지칭해준다 (불러온 값이 곧 수정 값이고, 수정하는 값이 곧 불러온 값)
- 변경된 거 저장하자! -> data 파일에서 mutating func update() 이용
자자 이제 이를 코드로 구체적으로 적용해보자고
1. DetailEditView & 2. 기존 data 불러와서 DetailEditView에도 띄우기 & 3. 데이터 수정
코드를 보자
주석으로 설명 달아놨데이
//DetailEditView.swift
import SwiftUI
//변경된 값은 Data property에 저장
//이때 @State wrapper 사용. 왜냐하면, 변경된 값 인식해서 View를 바로 그릴 것.
struct DetailEditView: View {
@Binding var data : DailyScrum.Data
//@Binding wrapper를 사용했다 = DetailView에서 던져준 data를 받았다
//여기서는 data: DailyScrum.Data 이렇게 데이터 타입만 선언
@State private var newAttendeeName: String = ""
var body: some View {
Form {
Section(header:Text("meeting info")){
TextField("Title", text: $data.title)
//여기에 있는 값(text)이 data의 title임($data.title)
//그리고 TextField이므로 사용자가 직접 입력하여 변경함
HStack{
Slider(value: $data.lengthInMinutes, in: 5...30, step: 1) {
//여기에 있는 값(value)이 data의 lengthInMinutes임($data.lengthInMinutes)
//그리고 Slider이므로 사용자가 직접 움직이며 변경함
Text("Length")
}
Spacer()
Text("\(Int(data.lengthInMinutes)) Minutes")
}
ThemePicker(selection: $data.theme)
}
Section(header: Text("Attendees")){
ForEach(data.attendees) { attendee in
Text(attendee.name)
}
//Delete하는 코드
.onDelete{ indicies in
data.attendees.remove(atOffsets: indicies)
}
HStack{
TextField("New Attendee", text:$newAttendeeName) //여기 있는 값(text)을 newattendeeName이라고 할거고
Button(action: {
withAnimation { //Button을 클릭하면 animation과 함께 밑에 있는 코드를 수행할 거다
//밑에 있는 코드가 뭐냐면 data의 attendees에 newAttendeeName을 추가하는 거
let newAttendee = DailyScrum.Attendee(name: newAttendeeName)
data.attendees.append(newAttendee)
newAttendeeName = "" //넣어주고 나면 값을 초기화시켜줘서 나중에도 쓸 수 있게 한다(컵 또 쓰게 씻는 느낌. 설거지!)
}
}) {
Image(systemName: "plus.circle.fill")
}
.disabled(newAttendeeName.isEmpty) //빈값이면 버튼 비활성화
}
}
}
}
}
4. 수정된 데이터 저장
코드를 바로 보자. 그 전에 여기서 기억해야할 것이 있다.

DetailView에서 DetailEditView로 넘어가는 것은 Sheet으로 구현했다. 그렇기 때문에 Edit View 상단 양쪽에 위치한 Cancel, Done 버튼은 toolbar로 적용하는 건데, 이 toolbar의 위치가 DetailView에서 설정한다. (Sheet에 관한 설정을 DetailView에서 하는 것처럼)
//DetailView.swift
import SwiftUI
struct DetailView: View {
@Binding var scrum: DailyScrum
@State private var data = DailyScrum.Data()
@State private var isPresentingEditView = false
var body: some View {
List {
Section(header: Text("Meeting Info")){
NavigationLink(destination: MeetingView()) {
Label("Starting Meeting", systemImage: "timer")
.font(.headline)
.foregroundColor(.accentColor)
}
HStack {
Label("Length", systemImage: "clock")
Spacer()
Text("\(scrum.lengthInMinutes) Minutes")
}
.accessibilityElement(children: .combine)
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("Edit") {
isPresentingEditView = true
data = scrum.data //Edit 버튼 누르면 scrum.data를 data에 담아준다
}
}
.sheet(isPresented: $isPresentingEditView){
NavigationView{
DetailEditView(data: $data)
//navigationTitle 위치!
.navigationTitle(scrum.title)
.toolbar{ //이게 DetailEditView 상단에 위치할 toolbar
//placement가 .cancellationAction! 아주 직관적인 위치 명시
ToolbarItem(placement:.cancellationAction){
Button("Cancel"){
isPresentingEditView = false
}
}
//placement가 .confirmationAction! 아주 직관적인 위치 명시
ToolbarItem(placement: .confirmationAction){
Button("Done"){
isPresentingEditView = false
//이게 바로 update하라는 명령. 이는 함수를 의미하는 것이고
//DailyScrum.swift 파일에 담겨있다
scrum.update(from: data)
}
}
}
}
}
}
}
func update 잡으러 가자
//DailyScrum.swift
import Foundation
struct DailyScrum: Identifiable {
let id: UUID //인스턴스 하나씩 다루기 위해
var title: String
var attendees: [Attendee] //String으로 이루어진 List라는 의미를 '[String]'으로 표기
var lengthInMinutes: Int
var theme: Theme //Theme.swift에서 선언해준 enum 타입
//이거 custom initializer임
//매개변수 이니셜라이저
init(id: UUID = UUID(), title: String, attendees: [String], lengthInMinutes: Int, theme: Theme){
self.id = id
self.title = title
self.attendees = attendees.map{Attendee(name:$0)}
self.lengthInMinutes = lengthInMinutes
self.theme = theme
}
}
extension DailyScrum {
struct Attendee: Identifiable {
let id: UUID
var name: String
init(id: UUID = UUID(), name: String) {
self.id = id
self.name = name
}
}
//이런 식으로 default값 지정해주면, 나중에 Data()라고 선언하면서 바로 쓸 수 있음
struct Data {
var title: String = ""
var attendees: [Attendee] = []
var lengthInMinutes: Double = 5
var theme: Theme = .seafoam
}
var data: Data {
//밑에 코드 무슨 말이지 ..
Data(title: title, attendees: attendees, lengthInMinutes: Double(lengthInMinutes), theme: theme)
}
//이게 바로 update하는 함수다
mutating func update(from data: Data) { //from : 매개변수, data : 전달인자, Data : 전달인자 타입
title = data.title //전달인자의 title 프로퍼티를 DailyScrum의 title에 할당
attendees = data.attendees //전달인자의 attendees 프로퍼티를 DailyScrum의 attendees에 할당
lengthInMinutes = Int(data.lengthInMinutes) //전달인자의 lengthInMinutes 프로퍼티를 DailyScrum의 lengthInMinutes에 할당
theme = data.theme //전달인자의 theme 프로퍼티를 DailyScrum의 theme에 할당
//새로운 값들을 본체에 할당!
}
}
//provide sample data
extension DailyScrum {
static let sampleData: [DailyScrum] =
[
DailyScrum(title: "Design", attendees: ["Cathy", "Daisy", "Simon", "Jonathan"], lengthInMinutes: 10, theme: .yellow),
DailyScrum(title: "App Dev", attendees: ["Katie", "Gray", "Euna", "Luis", "Darla"], lengthInMinutes: 5, theme: .orange),
DailyScrum(title: "Web Dev", attendees: ["Chella", "Chris", "Christina", "Eden", "Karla", "Lindsey", "Aga", "Chad", "Jenn", "Sarah"], lengthInMinutes: 5, theme: .poppy)
]
}
오케이 특별히 이거 중요한 거니까 func update 부분만 따로 빼주겠음
//이게 바로 update하는 함수다
mutating func update(from data: Data) { //from : 매개변수, data : 전달인자, Data : 전달인자 타입
title = data.title //전달인자의 title 프로퍼티를 DailyScrum의 title에 할당
attendees = data.attendees //전달인자의 attendees 프로퍼티를 DailyScrum의 attendees에 할당
lengthInMinutes = Int(data.lengthInMinutes) //전달인자의 lengthInMinutes 프로퍼티를 DailyScrum의 lengthInMinutes에 할당
theme = data.theme //전달인자의 theme 프로퍼티를 DailyScrum의 theme에 할당
//새로운 값들을 본체에 할당!
}
그리고 이거를 toolbar의 Done을 눌렀을 때 action으로 이 함수를 실행시키면 update된다!
//DetailView.swift
ToolbarItem(placement: .confirmationAction){
Button("Done"){
isPresentingEditView = false
scrum.update(from: data)
}
}
끝
'iOS > SwiftUI' 카테고리의 다른 글
| [SwiftUI] Core Data를 이용한 데이터 CRUD (iCalories) (0) | 2022.05.17 |
|---|---|
| [SwiftUI] Making Classes Observable (0) | 2022.05.11 |
| [SwiftUI] Managing Data Flow Between Views (0) | 2022.05.10 |
| [SwiftUI] Design Pattern : MVVM (0) | 2022.04.27 |