ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 웹개발자의 iOS 개발기(6) - [SwiftUI] 리스트
    iOS 2025. 1. 25. 14:16

    리스트에 대하여 공부해 보았습니다.

    01. 리스트와 반복

    import SwiftUI
    
    struct _1_ListLoop: View {
        
        var fruits = ["Apple", "Banana", "Cherry", "Double Kiwi", "Elder berry"]
        var price = ["1000", "3000", "4000", "2400", "8000"]
        var count = 0
        
        var body: some View {
            NavigationStack {
                List {
                    ForEach(fruits, id: \\.self) { fruit in
                        HStack {
                            Text(fruit)
                            Text(price[count])
                        }
                        // count = count + 1  // 에러 발생
                    }
                }
                .navigationTitle("Fruit List")
            }
            
        }
    }
    
    #Preview {
        _1_ListLoop()
    }
    

    리스트 구성요소에 다양한 정보를 담고 싶어 위와 같이 코드를 짜보았지만 오류가 발생하는 코드.

    02. 데이터 모델링

    struct를 이용하여 해결

    import SwiftUI
    
    struct Fruit: Hashable {
        let name: String
        let matchFruitName: String
        let price: Int
    }
    
    struct _1_ListLoop: View {
        
    //    var fruits = ["Apple", "Banana", "Cherry", "Double Kiwi", "Elder berry"]
    //    var matchFruits = ["Banana", "Banana", "Double Kiwi", "Elder berry", "Double Kiwi"]
    //    var price = ["1000", "3000", "4000", "2400", "8000"]
    //    var count = 0
        
        var favoriteFruit = [
            Fruit(name: "Apple", matchFruitName: "Banana", price: 1000),
            Fruit(name: "Banana", matchFruitName: "Banana", price: 3000),
            Fruit(name: "Cherry", matchFruitName: "Double Kiwi", price: 4000),
            Fruit(name: "Double Kiwi", matchFruitName: "Elder berry", price: 2400),
            Fruit(name: "Elder berry", matchFruitName: "Double Kiwi", price: 8000),
        ]
        
        var body: some View {
    //        NavigationStack {
    //            List {
    //                ForEach(fruits, id: \\.self) { fruit in
    //                    HStack {
    //                        Text(fruit)
    //                        Text(price[count])
    //                    }
    //                    // count = count + 1  // 에러 발생
    //                }
    //            }
    //            .navigationTitle("Fruit List")
    //        }
            
            NavigationStack {
                List {
                    ForEach(favoriteFruit, id: \\.self) { fruit in
                        VStack(alignment: .leading) {
                            Text("name: \\(fruit.name)")
                            Text("machFruitName: \\(fruit.matchFruitName)")
                            Text("price: \\(fruit.price)")
                        }
                    }
                }
                .navigationTitle("Fruit List")
            }
        }
    }
    
    #Preview {
        _1_ListLoop()
    }
    

    Hashable

    값을 고유하게 식별하고 비교하기 위함

    03. 리스트의 추가와 삭제

    State

    • 데이터의 상태를 나타내는 State
    • struct이기에 필요한 기능 → View가 struct인데, struct는 생성 시에 멤버변수를 초기화한다. 화면 재로딩 시 변수의 변경사항을 기억하지 않는다.
    • @State를 변수에 사용해주면 해당 변수는 다른 스토리지에 저장해 두었다가 불러온다.
    • @State가 사용된 변수의 값이 변경되면 화면 재로딩

    Binding

    • State에 $를 붙이면 Binding
    • State가 붙잡고 있는 상태를 연결해줄 때
    • 두 State가 연결된다고 생각
    import SwiftUI
    
    struct Fruit: Hashable {
        let name: String
        let matchFruitName: String
        let price: Int
    }
    
    struct _1_ListLoop: View {
        
    //    var fruits = ["Apple", "Banana", "Cherry", "Double Kiwi", "Elder berry"]
    //    var matchFruits = ["Banana", "Banana", "Double Kiwi", "Elder berry", "Double Kiwi"]
    //    var price = ["1000", "3000", "4000", "2400", "8000"]
    //    var count = 0
        
        @State var favoriteFruit = [
            Fruit(name: "Apple", matchFruitName: "Banana", price: 1000),
            Fruit(name: "Banana", matchFruitName: "Banana", price: 3000),
            Fruit(name: "Cherry", matchFruitName: "Double Kiwi", price: 4000),
            Fruit(name: "Double Kiwi", matchFruitName: "Elder berry", price: 2400),
            Fruit(name: "Elder berry", matchFruitName: "Double Kiwi", price: 8000),
        ]
        
        @State var fruitName = ""
        
        var body: some View {
    //        NavigationStack {
    //            List {
    //                ForEach(fruits, id: \\.self) { fruit in
    //                    HStack {
    //                        Text(fruit)
    //                        Text(price[count])
    //                    }
    //                    // count = count + 1  // 에러 발생
    //                }
    //            }
    //            .navigationTitle("Fruit List")
    //        }
            
            NavigationStack {
                
                VStack {
                    
                    HStack {
                        TextField("insert fruit name", text: $fruitName)
                        Button {
                            favoriteFruit.append(Fruit(name: fruitName, matchFruitName: "Apple", price: 1000))
                        } label: {
                            Text("insert")
                                .padding()
                                .background(.blue)
                                .foregroundColor(.white)
                                .cornerRadius(10)
                        }
    
                    }
                    .padding()
                    
                    List {
                        ForEach(favoriteFruit, id: \\.self) { fruit in
                            VStack(alignment: .leading) {
                                Text("name: \\(fruit.name)")
                                Text("machFruitName: \\(fruit.matchFruitName)")
                                Text("price: \\(fruit.price)")
                            }
                        }.onDelete { IndexSet in
                            favoriteFruit.remove(atOffsets: IndexSet)
                        }
                    }
                    .navigationTitle("Fruit List")
                }
            }
        }
    }
    
    #Preview {
        _1_ListLoop()
    }
    

     

     

    04. 간단한 리스트를 사용한 앱

    import SwiftUI
    
    struct SettingInfo: Hashable {
        let title: String
        let systemName: String
        let backgroundColor: Color
        let foregroudColor: Color
    }
    
    struct _4_List_Setting_: View {
        
        let data: [[SettingInfo]] = [
            [SettingInfo(title: "스크린 타임",         systemName: "hourglass",          backgroundColor: .purple, foregroudColor: .white)],
            [SettingInfo(title: "일반",              systemName: "gear",               backgroundColor: .gray,   foregroudColor: .white),
            SettingInfo(title: "손쉬운 사용",         systemName: "person.crop.circle", backgroundColor: .blue,   foregroudColor: .white),
            SettingInfo(title: "개인정보 보호 및 보안", systemName: "hand.raised.fill",   backgroundColor: .blue,   foregroudColor: .white)],
            [SettingInfo(title: "암호",              systemName: "key.fill",           backgroundColor: .gray,   foregroudColor: .white)],
        ]
        
        var body: some View {
            
            
            NavigationStack {
                List {
                    ForEach(data, id: \\.self) { section in
                        Section {
                            ForEach(section, id: \\.self) { item in
                                Label {
                                    Text(item.title)
                                } icon: {
                                    Image(systemName: item.systemName)
                                        .resizable()
                                        .scaledToFit()
                                        .frame(width: 20, height: 20)
                                        .padding(.all, 7)
                                        .background(item.backgroundColor)
                                        .foregroundColor(item.foregroudColor)
                                        .cornerRadius(6)
                                }
                            }
                        }
                    }
                    
    //                Section {
    //                    Label {
    //                        Text("스크린 타임")
    //                    } icon: {
    //                        Image(systemName: "hourglass")
    //                            .resizable()
    //                            .scaledToFit()
    //                            .frame(width: 20, height: 20)
    //                            .padding(.all, 7)
    //                            .background(.purple)
    //                            .foregroundColor(.white)
    //                            .cornerRadius(6)
    //                    }
    //                }
    //                
    //                
    //                Section {
    //                    Label {
    //                        Text("일반")
    //                    } icon: {
    //                        Image(systemName: "gear")
    //                            .resizable()
    //                            .scaledToFit()
    //                            .frame(width: 20, height: 20)
    //                            .padding(.all, 7)
    //                            .background(.gray)
    //                            .foregroundColor(.white)
    //                            .cornerRadius(6)
    //                    }
    //                    
    //                    Label {
    //                        Text("손쉬운 사용")
    //                    } icon: {
    //                        Image(systemName: "person.crop.circle")
    //                            .resizable()
    //                            .scaledToFit()
    //                            .frame(width: 20, height: 20)
    //                            .padding(.all, 7)
    //                            .background(.blue)
    //                            .foregroundColor(.white)
    //                            .cornerRadius(6)
    //                    }
    //                    
    //                    Label {
    //                        Text("개인정보 보호 및 보안")
    //                    } icon: {
    //                        Image(systemName: "hand.raised.fill")
    //                            .resizable()
    //                            .scaledToFit()
    //                            .frame(width: 20, height: 20)
    //                            .padding(.all, 7)
    //                            .background(.blue)
    //                            .foregroundColor(.white)
    //                            .cornerRadius(6)
    //                    }
    //                }
    //                    
    //                
    //                Section {
    //                    Label {
    //                        Text("암호")
    //                    } icon: {
    //                        Image(systemName: "key.fill")
    //                            .resizable()
    //                            .scaledToFit()
    //                            .frame(width: 20, height: 20)
    //                            .padding(.all, 7)
    //                            .background(.gray)
    //                            .foregroundColor(.white)
    //                            .cornerRadius(6)
    //                    }
    //                }
    //                
    //                
    //                
                }
            }.navigationTitle("설정")
            
        }
    }
    
    #Preview {
        _4_List_Setting_()
    }
    

     

     

    결과물은 위와 같은 구조의 리스트이다.

    이런 Section 별 구조의 리스트를 그리기 위해서는 데이터 모델링이 중요한 것 같다.

    이 예제에서는 2차원배열 ‘[[ ]]’ 을 이용해서 해당 구조를 그렸다.

    let data: [[SettingInfo]] = [
            [SettingInfo(title: "스크린 타임",         systemName: "hourglass",          backgroundColor: .purple, foregroudColor: .white)],
            [SettingInfo(title: "일반",              systemName: "gear",               backgroundColor: .gray,   foregroudColor: .white),
            SettingInfo(title: "손쉬운 사용",         systemName: "person.crop.circle", backgroundColor: .blue,   foregroudColor: .white),
            SettingInfo(title: "개인정보 보호 및 보안", systemName: "hand.raised.fill",   backgroundColor: .blue,   foregroudColor: .white)],
            [SettingInfo(title: "암호",              systemName: "key.fill",           backgroundColor: .gray,   foregroudColor: .white)],
        ]
    
    NavigationStack {
                List {
                    ForEach(data, id: \\.self) { section in
                        Section {
                            ForEach(section, id: \\.self) { item in
                                Label {
                                    Text(item.title)
                                } icon: {
                                    Image(systemName: item.systemName)
                                        .resizable()
                                        .scaledToFit()
                                        .frame(width: 20, height: 20)
                                        .padding(.all, 7)
                                        .background(item.backgroundColor)
                                        .foregroundColor(item.foregroudColor)
                                        .cornerRadius(6)
                                }
                            }
                        }
                    }
                }
            }.navigationTitle("설정")
    
Designed by Tistory.