본문 바로가기

프로젝트/ReminderApp clone

06. ListView 구성하기, Preview Data 구성하기

지금은 데이터가 CoreData에 잘 저장되는지 확인하기 위한 임시 출력하고 있을 뿐, 어떤 동작도 하지 않고, 모든 데이터를 포함하고 있지도 않다.

폴더 기능까지는 그렇다 치더라도 아이콘과 이름 등을 알맞게 표시하면 좋을 것 같다.

//
//  MyListCellView.swift
//  ReminderApp
//

import SwiftUI

struct MyListCellView: View {
    
    let myList: MyList
    
    var body: some View {
        HStack {
            Image(systemName: "line.3.horizontal.circle.fill")
                .foregroundColor(Color(myList.color))
            
            Text(myList.name)
            
            Spacer()
            
            Image(systemName: "chevron.right")
                .foregroundColor(.gray)
                .opacity(0.4)
                .padding([.trailing], 10)
        }
    }
}

이건 List의 Cell이다.
fetch로 가져온 MyList를 넘겨 받아 color와 name으로 cell을 구성한다.

이런 모습이 된다.

//
//  MyListView.swift
//  ReminderApp
//

import SwiftUI

struct MyListView: View {
    
    let myLists: FetchedResults<MyList>
    
    var body: some View {
        NavigationStack {
            if myLists.isEmpty {
                Text("No Reminders found")
            } else {
                ForEach(myLists) { myList in
                    VStack {
                        MyListCellView(myList: myList)
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding([.leading], 10)
                            .font(.title3)
                        
                        Divider()
                    }
                }
                
//                VStack {
//                    ForEach(myLists) { myList in
//                        MyListCellView(myList: myList)
//                            .frame(maxWidth: .infinity, alignment: .leading)
//                            .padding([.leading], 10)
//                            .font(.title3)
//                        
//                        Divider()
//                    }
//                }
            }
        }
    }
}

이건 ListCell을 표시할 MyListView이다.
ForEach를 사용해서 myLists의 데이터를 순회하며 Cell을 표시하는데 방법은 일단 두 가지가 있다.

첫 번째는 각각의 Cell과 Divider를 하나로 묶는 방법, 두 번째는 모든 Cell과 Divider 전체를 하나로 묶는 방법이다.
둘 다 미관상 동일하지만 업데이트 주기나 효율 면에서는 차이가 있으니 취향과 목적에 맞게 사용하다.

//
//  ContentView.swift
//  ReminderApp
//

import SwiftUI

struct HomeView: View {
    
    @FetchRequest(sortDescriptors: [])
    private var myListResults: FetchedResults<MyList>
    
    @State private var isPresented: Bool = false

    var body: some View {
        NavigationStack {
            VStack {
                MyListView(myLists: myListResults)
                                
                HStack {
                    Button {
                        isPresented = true
                    } label: {
                        Text("Add List")
                            .font(.headline)
                    }
                    .padding()
                }
                .frame(maxWidth: .infinity, alignment: .bottomTrailing)
            }
            .sheet(isPresented: $isPresented, content: {
                NavigationStack {
                    AddNewListView { name, color in
                        do {
                            try ReminderService.saveMyList(name, color)
                        } catch {
                            print(error.localizedDescription)
                        }
                    }
                }
            })
        }
        .padding()
    }
}

그리고 이 MyListView를 HomeView에 추가하면 끝난다.

View들이 많아지면서 Preview에데이터를 전달해 줘야 하는 경우가 생긴다.

#Preview {
    ColorPickerView(selectedColor: .constant(.yellow))
}

보통은 이런 형태로 해당 View에 맞는 데이터들을 직접 지정해 주는 경우가 많지만, 지금처럼 CoreData에서 데이터를 가져와야 하는 경우엔 여의치 않다.

프로젝트의 Preview Content 포더에는 PreViewData라는 파일을 하나 만들어 주고

//
//  PreviewData.swift
//  ReminderApp
//

import Foundation
import CoreData

class PreviewData {
    static var myList: MyList {
        let viewContext = CoreDataProvider.shared.persistentContainer.viewContext
        let request = MyList.fetchRequest()
        return (try? viewContext.fetch(request).first) ?? MyList()
    }
}

이 안에서 fetch를 실행하도록 구현한다.

#Preview {
    MyListCellView(myList: PreviewData.myList)
}

Preview에는 위와 같이 전달하면 된다.