포스트

To Do List (3)

CellView 적용하기

1
2
3
4
List(todoLists, id: \.self) { list in
                    CellView(isOn: list.isCompleted,
                             title: list.title)
                }

MainView에서 List에 다음과 같이 CellView를 적용시켜준다.

Toggle 버튼 클릭시 적용하기.

On/Off에 따라 AttributedText가 적용되게 해보자.

SwiftUI에서는 Text에 바로 stirkethrough Modifier가 있다.

isOn이 toggle에 따라 true/ false가 바뀌므로

1
2
3
4
5
6
7
HStack {
            Text(title)
                .strikethrough(isOn, color: .black)
                .padding(.leading, 30)
            Toggle("", isOn: $isOn)
                .padding(.trailing, 30)
        }

이렇게 해주면 작동이 된다.

LV 2 까지 완료.

Swipe를 이용한 삭제기능 구현

여기는 LV3이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
VStack {
                List(todoLists, id: \.self) { list in
                    CellView(isOn: list.isCompleted,
                             title: list.title)
                    .swipeActions(edge: .trailing) {
                        Button(action: {
                            modelContext.delete(list)
                        }) {
                            Image(systemName: "trash")
                        }
                    }
                }
            }

그리고 Button에 빨간색 배경을 주고 싶었는데 거기에 하는게 아니라,

SwipeAction의 마무리에 tint를 사용해서 적용해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
VStack {
                List(todoLists, id: \.self) { list in
                    CellView(isOn: list.isCompleted,
                             title: list.title)
                    .swipeActions(edge: .trailing) {
                        Button(action: {
                            modelContext.delete(list)
                        }) {
                            Image(systemName: "trash")
                        }
                    }
                    .tint(.red)
                }
            }

LV 3 완료.

이떄 tint red로 인해

CleanShot 2024-10-22 at 13 54 06

이렇게 toggle까지 변하게 된다.

그래서 이부분은 초록색으로 유지하기 위해서

1
2
3
4
5
6
7
8
9
HStack {
            Text(title)
                .strikethrough(isOn, color: .black)
                .padding(.leading, 30)
                .lineLimit(0)
            Toggle("", isOn: $isOn)
                .padding(.trailing, 10)
                .tint(.green)
        }

CellView에도 tint를 적용시켜준다.

CleanShot 2024-10-22 at 14 18 18

적용 완료.

List를 id순으로 정렬하기

현재는 등록시 규칙없이 추가가 되는듯 하다.

@Query(sort: \TodoModel.id) private var todoLists: [TodoModel]

sort를 붙여주면 해결된다.

id 중복 수정하기.

현재 id 가

CleanShot 2024-10-22 at 15 23 31

이렇게 순서대로 정렬이 되지만

1
2
3
4
5
6
func addList() {
        let id: Int = todoLists.count
        let todoModel = TodoModel(id: id + 1, title: title, isCompleted: false)
        modelContext.insert(todoModel)
        title = ""
    }

이렇게 현재 갯수에서 1씩 증가를 하는 매커니즘으로 되어있다.

중복을 방지하기위해 현재 리스트에 있는 값에서 최대값을 구한뒤 거기서 1을 더하게 한다.

1
2
3
4
5
6
7
func chkId(_ currentId: Int) -> Int {
        var id = currentId
        if let maxId = todoLists.map({ $0.id }).max() {
               id = maxId + 1
           }
        return id
    }

전부 지우기

사이트 를 참고한다.

1
2
3
4
5
6
7
8
9
10
11
12
ToolbarItem(id: "DeleteAll",
                            placement: .navigationBarLeading) {
                    Button("DeleteAll",
                           systemImage: "folder.badge.minus") {
                        // action
                        do {
                            try modelContext.delete(model: TodoModel.self)
                        } catch {
                            print("Error: \(error.localizedDescription)")
                        }
                    }
                }

이때 하나의 row를 삭제할때는 try - catch 구문이 없었는데

전체삭제는 해줘야한다.

Coredata와 유사하다.

회고.

1. Comparable vs Equatable

  1. Comparable CleanShot 2024-10-23 at 10 35 52
  • 역할
    • Comparable은 객체 간에 순서를 비교할 수 있게 한다. 이를 통해 두 객체가 크거나 작은지(<, <=, >, >=)를 비교하여 정렬이 가능해진다.
  • 필요성
    • 정렬이 필요한 경우, 예를 들어 배열을 오름차순 또는 내림차순으로 정렬할 때 Comparable이 사용된다. 이 프로토콜은 객체 간의 대소 비교를 위한 연산자를 제공한다.
  • 구현 방법
    • Comparable을 구현하려면 < 연산자를 구현해야 한다. 그 외의 연산자(>, <=, >=)는 기본적으로 제공된다. Comparable을 준수하려면 Equatable도 함께 구현해야 하므로, 객체 간의 동등성과 순서를 모두 정의할 수 있다.

ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Person: Comparable {
    let name: String
    let age: Int

    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.age < rhs.age
    }

    // Equatable의 구현도 필요함
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}

let person1 = Person(name: "Alice", age: 30)
let person2 = Person(name: "Bob", age: 25)

print(person1 < person2)   // false
print(person1 > person2)   // true

let sortedPeople = [person1, person2].sorted()
print(sortedPeople)  // [Person(name: "Bob", age: 25), Person(name: "Alice", age: 30)]
  1. Equatable CleanShot 2024-10-23 at 10 36 04
  • 역할
    • Equatable은 객체가 서로 같은지 여부를 비교하는 데 사용된다. 이를 통해 두 객체가 동등한지(==) 또는 동등하지 않은지(!=)를 알 수 있다.
  • 필요성
    • Equatable은 데이터가 같은지 비교할 수 있어야 하는 여러 곳에서 필수적이다. 예를 들어, 배열에서 특정 객체를 찾거나, 두 객체가 같은지 비교할 때 사용된다.
  • 구현 방법
    • Equatable을 준수하려면 == 연산자를 구현해야 한다. 두 객체가 같은지 확인하기 위해, 각 객체의 속성을 비교한다.

ex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Person: Equatable {
    let name: String
    let age: Int

    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

let person1 = Person(name: "Alice", age: 30)
let person2 = Person(name: "Alice", age: 30)
let person3 = Person(name: "Bob", age: 25)

print(person1 == person2)  // true
print(person1 == person3)  // false

결론

Equatable은 두 객체가 동일한지 확인하는 데 사용되고, Comparable은 객체들 간의 순서 비교와 정렬을 할 때 사용된다. Comparable을 구현하려면 반드시 Equatable을 함께 구현해야 하므로, Comparable을 준수하는 객체는 동등성(==)과 순서 비교(<) 모두 가능하다.

이 두 프로토콜을 잘 활용하면 데이터 비교와 정렬 작업을 효율적으로 처리할 수 있다.

프로토콜주요 연산자목적필수 메서드사용 예시
Equatable==, !=두 객체가 동일한지 비교static func ==(lhs:rhs:) -> Bool객체가 같은지 비교할 때 (Array.contains())
Comparable<, <=, >, >=두 객체 간 순서를 비교static func <(lhs:rhs:) -> Bool배열 정렬, 대소 비교 (Array.sorted())
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.