일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 데이터셋 만들기
- 카카오 2021
- 머신러닝
- 날씨 앱
- Siwft
- SwiftUI
- supervisely
- swift 배열
- kakao 2018
- swift
- 카카오 2019
- ios 개발 시작
- c언어
- 소수
- coco 데이터셋
- 카카오
- 파이썬
- 프로그래머스
- swift 시작
- Python
- 최솟값 만들기
- 카카오 2020
- 스위프트
- Kakao
- 이미지학습
- fast.ai
- 문제
- 프로그래머스 답
- 카카오 2018
- roboflow
Archives
- Today
- Total
잡초의 일지
[Swift] [SwiftUI] Todo - List 만들기 본문
728x90
반응형
SMALL
개요
온전한 내 힘으로 MVVM 패턴을 적용한 무언가를 만들고 싶다는 생각이 들었다.
가장 쉬운 todolist를 만들어보기로 했다.
완성본
이 영상을 올리려고 처음으로 유튜브에 동영상을 올려봤다..ㅎㅎ
디자인 & 기능
아이디어의 러프 스케치는 아래와 같다.
알람이나 날짜 기록 등의 기능은 제외하고, 주요 기능만 추려 단순하게 만들었다.
코드 내용
MVVM 패턴으로 구현하려고 노력하였다.
완료한 일을 체크하는 코드를 짤 때, 이미 Todo객체로 만들어진 것을 어떻게 수정해야 할 지 고민이었다. 각 todo를 하나의 view로 표현하여 여러 셀들로 표현해야 하나 생각했다. 하지만, 이것은 기존의 코드를 많이 수정해야 했다. 따라서, modifyTodoDone이라는 함수를 추가하였다. 기존 TodoList에서 unique한 id값을 매개변수로 하여 인덱스를 찾고, 해당 todo에 대해서 replaceSubrange를 사용하여 변경된 값을 가진 새로운 Todo객체로 변경하였다. 배열로 표현된 TodoList의 크기가 크지 않을것이라고 판단하여,(투두리스트에 적는 일이 아주 많지 않을것이다..) for문을 사용하였다.
클릭 시 버튼의 label이나 글자색이 변하는 등의 변화를 표현하기 위해, 삼항연산자를 사용하였다.
Setting 뷰에 대한 것은 아직 구상중이다.
+) TodoList에 'delete all' 같은 버튼을 만들어주면, 일일이 지우지 않아도 되기 때문에 더 편할것 같다.
<View>
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
TodoView()
.tabItem {
Image(systemName: "checkmark.circle.fill")
Text("Todo")
}
SettingView()
.tabItem {
Image(systemName: "gear")
Text("Setting")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SwiftUI
import Combine
struct TodoView: View {
@ObservedObject var todoStore = TodoStore()
@State var newTodo: String = ""
@State var isImportant: Bool = false
struct ImportantTodo: ViewModifier {
var importantTodoColor = Color.pink
func body(content: Content) -> some View {
return content
.padding(.vertical, 5)
.foregroundColor(importantTodoColor)
}
}
var addTodoBar: some View{
HStack{
Button(action: {
isImportant.toggle()
}, label: {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(Color.orange)
})
TextField("Enter new tasks", text: self.$newTodo)
.padding(5)
.background(RoundedRectangle(cornerRadius: 5).fill(isImportant ? Color.orange : Color(red: 0.0, green: 0.0, blue: 0.0, opacity: 0.2)))
Button(action: {
todoStore.addNew(newTodo, isImportant)
newTodo = ""
isImportant = false
}, label: {
Image(systemName: "plus.circle.fill")
.foregroundColor(Color.red)
})
}
}
var body: some View {
NavigationView {
VStack{
addTodoBar
.padding()
List{
ForEach(self.todoStore.todos) { todo in
HStack{
Button(action: {
todoStore.modifyTodoDone(todo.id)
}, label: {
Image(systemName: todo.done ? "largecircle.fill.circle" : "circle")
.foregroundColor(Color.gray)
})
Text(todo.todoItem)
.modifier(todo.done ? ImportantTodo(importantTodoColor: Color.gray) : (todo.important ? ImportantTodo() : ImportantTodo(importantTodoColor: Color.primary)))
}
}
.onMove(perform: self.todoStore.move)
.onDelete(perform: self.todoStore.delete)
}
}
.navigationBarTitle("Todo List")
.navigationBarItems(trailing: EditButton())
}
.onAppear(perform: todoStore.loadTodos)
}
}
struct TodoView_Previews: PreviewProvider {
static var previews: some View {
TodoView()
}
}
import SwiftUI
struct SettingView: View {
var body: some View {
VStack(alignment: .leading){
Text("Setting")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.padding(5)
}
}
}
struct SettingView_Previews: PreviewProvider {
static var previews: some View {
SettingView()
}
}
<ViewModel>
import SwiftUI
import Combine
class TodoStore: ObservableObject{
@Published var todos = [Todo]()
private let todosKey = "TodoListKey"
func addNew(_ newTodo:String, _ important:Bool){
guard !newTodo.isEmpty else { return }
if important{
self.todos.insert(Todo(todoItem: newTodo, important: important), at: 0)
}else{
self.todos.append(Todo(todoItem: newTodo, important: important))
}
saveTodos()
}
func modifyTodoDone(_ id:UUID){
for i in todos.indices {
if (todos[i].id==id){
let newDone:Bool = todos[i].done ? false : true
let modifiedTodo = Todo(id: todos[i].id, todoItem: todos[i].todoItem, important: todos[i].important, done: newDone)
todos.replaceSubrange(i...i, with: repeatElement(modifiedTodo, count: 1))
break
}
}
saveTodos()
}
func move(from source: IndexSet, to destination: Int){
self.todos.move(fromOffsets: source, toOffset: destination)
}
func delete(at offests: IndexSet){
self.todos.remove(atOffsets: offests)
saveTodos()
}
// MARK:- Store TodoList data using UserDefaults
func saveTodos() {
UserDefaults.standard.set(try? PropertyListEncoder().encode(self.todos), forKey: todosKey)
}
func loadTodos() {
if let todoData = UserDefaults.standard.value(forKey: todosKey) as? Data {
if let todoList = try? PropertyListDecoder().decode(Array<Todo>.self, from: todoData) {
self.todos = todoList
}
}
}
}
<Model>
import SwiftUI
struct Todo: Identifiable, Codable{
var id = UUID()
let todoItem: String
let important: Bool
var done: Bool = false
}
참고
728x90
반응형
LIST
'Just for fun > iOS' 카테고리의 다른 글
[Swift] [SwiftUI] 기상청 API 이용한 날씨 앱 [정리본] (0) | 2021.01.10 |
---|---|
[Swift] [SwiftUI] 기상청 API 이용한 날씨 앱 만드는 일지 (0) | 2020.08.10 |
[Swift] [SwiftUI] SwiftUI를 사용한 간단한 카운팅 앱. 카운터 만들기.Simple counter (counting app) using SwiftUI. (0) | 2020.02.10 |
Comments