티스토리 뷰
오늘은 swiftUI에서 제공하는 List를 활용한 TodoList를 만들어보자!
핵심기능
- + 버튼을 누르면 팝업 창 띄워서 할 일 입력받기
- 입력받은 할 일을 토글 형태로 리스트에 추가
UI
팝업창 띄우기
위에 UI를 보면 + 버튼을 눌렀을 때 할 일을 입력할 수 있는 textfield가 들어있는 팝업창을 띄우도록 해보겠다.
처음엔 버튼을 만들고 Alert를 활용해 구현해보려고 하였으나 Button(action: {}, label:{})을 활용해서 구현하였는데 alert창에서 textfield를 넣으려고 하면 자꾸 오류가 생겼다.
그래서 버튼을 눌렀을 때 다른 뷰로 이동하게 만들었고, PopUpView라는 파일을 하나 더 만들어 팝업창에 대한 기능만 구현하도록 했다.
struct TodoItem: Identifiable {
let id: UUID = UUID()
var title : String
var isCompleted: Bool = false //토글 완료 상태
}
struct PopUpView: View {
@State var todo = ""
@Binding var showing: Bool
@State var toggleOn = false
@Binding var items : [TodoItem]
var body: some View {
VStack {
TextField("입력하세요.", text: $todo)
.padding()
Button(action: {
AddData()
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 20)
Text("next")
.font(.title3)
.foregroundColor(.white)
.padding()
}
})
}
.fixedSize(horizontal: false, vertical: true)
.padding()
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 20))
.shadow(radius: 30)
}
func AddData() {
let newItem = TodoItem(title: todo, isCompleted: false)
items.append(newItem)
todo = ""
print(items)
showing = false
}
}
- @State var todo = "" : textfield값을 저장할 문자열 형식의 변수
- @State var toggleOn = false : 토글의 on/off를 식별할 bool 타입 변수 선언
- @Binding var items : [TodoItem] : PopUpView에서의 items 배열을 Binding 프로퍼티로 받아 사용자가 항목을 추가할 때 ContentView의 배열을 업데이트하기 위해 사용
- @Binding var showing: Bool : 팝업창을 띄우고 숨기기 위해 Bool 타입으로 showing 변수 선언
- ContentView에서 + 버튼을 누르면 PopUpView의 팝업이 띄워지므로 @Binding 프로퍼티 사용
struct TodoItem 구조체
할 일 항목을 추가 할 때 완료되었으면 토글 상태를 변경시키는 등 TodoItem 인스턴스를 사용해야 한다. 뷰가 렌더링 되고 업데이트할 때
TodoItem 안에 구조체를 식별할 수 있게 하기 위해 Identifiable 프로토콜을 채택하여 구조체를 선언하였다.
- let id : UUID = UUID() : UUID는 범용 고유 식별자로, 실행할 때마다 고유한 값을 생성한다. 배열에 들어간 각 할 일 목록을 고유한 id 식별자로 나타내기 위해 사용하였다.
- var title: String : 할 일의 내용 문자열 형식으로 저장하기 위한 변수 선언
- var isCompleted: Bool = false : 토글의 완료 상태를 나타내기 위해 bool 값으로 변수 선언
ContentView
import SwiftUI
struct ContentView: View {
@State private var showing = false
@State private var todoItems: [TodoItem] = []
var body: some View {
ZStack {
VStack {
VStack {
Button(action: {
showing = true
}, label: {
Image(systemName: "plus.circle")
.aspectRatio(contentMode: .fill)
.foregroundColor(Color.red)
.font(.title2)
})
}
.frame(width: 350, height: 30, alignment: .trailing)
VStack(alignment: .leading, spacing: 0) {
Text("Todo List")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.padding()
List($todoItems) { $item in
Toggle(isOn: $item.isCompleted) {
Text(item.title)
}
}
}
}
if showing {
PopUpView(showing: $showing, items: $todoItems)
}
}
}
}
#Preview {
ContentView()
}
ContentView에서 + 버튼을 눌렀을 때 팝업창이 나오도록 상태를 관리할 수 있는 showingPop 변수를 초기값으로 false로 선언해 주었고, 버튼을 누를 때마다 true값이 되게 버튼에 action으로 넣어주었다.
Todo 항목을 표시할 수 있는 List값에 Todo항목을 저장할 수 있는 배열을 추가하여 todoItems 배열의 각 요소를 반복하여 리스트 형식으로 목록을 생성하도록 하였고, List 내부에서 todoItems 배열의 요소들에 대한 바인딩을 생성하여 데이터의 변경사항이 배열에 반영되도록 하였다.
리스트가 추가되는 형태가 토글형태가 되도록 토글 스위치를 생성해 주고 Toggle(isOn: $item.isCompleted) { Text(item.title) }를 통해 TodoItem의 isCompledted속성에 바인딩되도록 하여 토글스위치의 상태변경을 직접 반영하도록 해주었다.
느낀 점
처음에 구현하려고 할 때 꽤 쉬운거 같은데..?라는 생각이 들었지만 + 버튼을 통해 팝업창을 띄우고 거기서 입력한 값을 리스트에 실시간으로 불러오는 것과, 파일 두 개를 연동해서 작업하는 게 생각보다 어려워서 블로그도 많이 찾아보고 유튜브 자료도 많이 찾아본 후에 구현할 수 있었다..
Binding, $, @Observed.. 데이터 바인딩에 대한 개념에 대해 잘 모르고 개발을 시작하다 보니 삽질하는 시간이 길어졌던 거 같다..
그래도 이번 과제를 통해 데이터 바인딩에 대한 개념은 확실히 이해한 거 같다!
'iOS > SwiftUI' 카테고리의 다른 글
[iOS/SwiftUI] Preview Crashed 해결방법 (0) | 2024.03.30 |
---|---|
[iOS/SwiftUI] DatePicker를 이용한 TaskEditor 만들기 (1) | 2024.02.29 |
[iOS/SwiftUI] LazyVGrid를 활용하여 뷰 만들기 (1) | 2024.02.25 |
[iOS/SwiftUI] imagePicker 사용해서 프로필 만들기 (0) | 2024.02.02 |
[iOS/SwiftUI] stopwatch 구현하기 (0) | 2024.01.22 |
- Total
- Today
- Yesterday
- unstructed task
- Task
- closure
- 프로그래머스
- 16173
- imagepicker
- securefield
- swiftUI
- ios
- foundation models
- Fastlane
- rxswift
- 백준
- detached task
- CoreData
- 스위프트
- UIKit
- wwdc25
- mergeconflict
- 병합충돌
- 코딩테스트
- ObservableObject
- group tasks
- 클로저
- SWIFT
- XCTest
- asyne-let
- combine
- Xcode
- mlmodel
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |