簡體   English   中英

SwiftUI:使用 NavigationLink 從一個視圖更改為另一個視圖時復選標記消失

[英]SwiftUI: Checkmarks disappear when changing from one view to another using NavigationLink

我正在嘗試制作一個應用程序,該應用程序根據單擊的 NavigationLink 顯示帶有選擇/復選標記的列表。 我遇到的問題是,當我 go 返回主視圖然后我再次在 NavigationLink 中 go 時,我的選擇消失了。 我正在嘗試在 UserDefaults 中保存切換值,但它沒有按預期工作。 下面我將粘貼詳細的主要內容視圖。

第二種觀點:

struct CheckView: View {
    
    @State var isChecked:Bool = false
    @EnvironmentObject var numofitems: NumOfItems
    
    var title:String
    var count: Int=0
    
    var body: some View {
        HStack{
            ScrollView {
                Toggle("\(title)", isOn: $isChecked)
                    .toggleStyle(CheckToggleStyle())
                    .tint(.mint)
                    .onChange(of: isChecked) { value in
                        
                        if isChecked {
                            numofitems.num += 1
                            print(value)
                        } else{
                            numofitems.num -= 1
                        }
                        UserDefaults.standard.set(self.isChecked, forKey: "locationToggle")
                    }.onTapGesture {
                        
                    }
                    .onAppear {
                        self.isChecked = UserDefaults.standard.bool(forKey: "locationToggle")
                    }
                Spacer()
            }.frame(maxWidth: .infinity,alignment: .topLeading)
        }
    }
}

主視圖:

struct CheckListView: View {
    
    @State private var menu = Bundle.main.decode([ItemsSection].self, from: "items.json")
    
    
    var body: some View {
        NavigationView{
            List{
                ForEach(menu){
                    section in
                    NavigationLink(section.name) {
                        VStack{
                            ScrollView{
                                ForEach(section.items) { item in
                                    CheckView( title: item.name)
                                }
                            }
                        }
                    }
                }
            }
        }.navigationBarHidden(true)
            .navigationViewStyle(StackNavigationViewStyle())
            .listStyle(GroupedListStyle())
            .navigationViewStyle(StackNavigationViewStyle())
    }
}

物品欄目:

[
    {
        "id": "9DC6D7CB-B8E6-4654-BAFE-E89ED7B0AF94",
        "name": "Africa",
        "items": [
            {
                "id": "59B88932-EBDD-4CFE-AE8B-D47358856B93",
                "name": "Algeria"
            },
            {
                "id": "E124AA01-B66F-42D0-B09C-B248624AD228",
                "name": "Angola"
            }

Model:

struct ItemsSection: Codable, Identifiable, Hashable {
    var id: UUID = UUID()
    var name: String
    var items: [CountriesItem]
}

struct CountriesItem: Codable, Equatable, Identifiable,Hashable {
    var id: UUID = UUID()
    var name: String
}

正如評論中已經提到的,我看不到您從 UserDefaults 中讀取的位置,因此無論存儲在那里,您都不會閱讀它。 但即便如此,每個 Toggle 都使用相同的鍵,因此您正在覆蓋該值。

我不使用僅在本地使用的@State var isChecked ,而是創建另一個結構項,該結構項從 json 獲取標題,並包含一個 boolean 初始化為 false。

據我了解,我認為解決方案可能類似於以下代碼。 只是幾件事:

  1. 我不確定您的 json 的外觀如何,所以我沒有從 json 加載,我添加了帶有標題和隨機數量項目(實際上只是標題)的 ItemSections 對象和 ZC1C425268E68385D1AB5074C14
  2. 我在 UI 上添加了一個文本 output ,而不是帶有已檢查切換數量的打印。 它在第一頁向您顯示所有已檢查切換的數量。
  3. 我沒有使用 UserDefaults,而是使用了 @AppStorage。 要完成這項工作,您必須使Array符合RawRepresentable ,您可以使用以下代碼/擴展來實現這一點(只需在項目中的某個位置添加一次)
  4. 也許你應該考慮一個 ViewModel(例如 ItemSectionViewModel),從 json 加載數據並將其作為 @ObservableObject 提供給視圖。

視圖的代碼:

//
//  CheckItem.swift
//  CheckItem
//
//  Created by Sebastian on 24.08.22.
//

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        VStack() {
            CheckItemView()
        }
    }
}

struct CheckItemView: View {
    
    let testStringForTestData: String = "Check Item Title"
    
    @AppStorage("itemSections") var itemSections: [ItemSection] = []
    
    func addCheckItem(title: String, numberOfItems: Int) {
        var itemArray: [Item] = []
        for i in 0...numberOfItems {
            itemArray.append(Item(title: "item \(i)"))
        }
        itemSections.append(ItemSection(title: title, items: itemArray))
    }
    
    func getSelectedItemsCount() -> Int{
        var i: Int = 0
        for itemSection in itemSections {
            let filteredItems = itemSection.items.filter { item in
                return item.isOn
            }
            i = i + filteredItems.count
        }
        return i
    }
    
    var body: some View {
        NavigationView{
            VStack() {
                List(){
                    ForEach(itemSections.indices, id: \.self){ id in
                        NavigationLink(destination: ItemSectionDetailedView(items: $itemSections[id].items)) {
                            Text(itemSections[id].title)
                        }
                        .padding()
                    }
                }
                Text("Number of checked items:  \(self.getSelectedItemsCount())")
                    .padding()
                
                Button(action: {
                    self.addCheckItem(title: testStringForTestData, numberOfItems: Int.random(in: 0..<4))
                }) {
                    Text("Add Item")
                }
                .padding()
            }
        }
    }
}

struct ItemSectionDetailedView: View {
    
    @Binding var items: [Item]
    
    var body: some View {
        ScrollView() {
            ForEach(items.indices, id: \.self){ id in
                Toggle(items[id].title, isOn: $items[id].isOn)
                    .padding()
            }
        }
    }
}

struct ItemSection: Identifiable, Hashable, Codable  {
    var id: String = UUID().uuidString
    var title: String
    var items: [Item]
}

struct Item: Identifiable, Hashable, Codable  {
    var id: String = UUID().uuidString
    var title: String
    var isOn: Bool = false
}

這里是使用@AppStorage 的調整:

extension Array: RawRepresentable where Element: Codable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode([Element].self, from: data)
        else {
            return nil
        }
        self = result
    }
    
    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
              let result = String(data: data, encoding: .utf8)
        else {
            return "[]"
        }
        return result
    }
}

正如評論中所述,您必須將isChecked屬性與CountryItem本身相關聯。 為了讓它工作,我改變了 model 並添加了一個isChecked屬性。 如果 JSON allread 存在,您需要手動將其添加到 JSON 中

struct CheckView: View {
    
    @EnvironmentObject var numofitems: NumOfItems
    //use a binding here as we are going to manipulate the data coming from  the parent
    //and pass the complete item not only the name
    @Binding var item: CountriesItem
        
    var body: some View {
        HStack{
            ScrollView {
                //use the name and the binding to the item itself
                Toggle("\(item.name)", isOn: $item.isChecked)
                    .toggleStyle(.button)
                    .tint(.mint)
                // you now need the observe the isChecked inside of the item
                    .onChange(of: item.isChecked) { value in
                        if value {
                            numofitems.num += 1
                            print(value)
                        } else{
                            numofitems.num -= 1
                        }
                    }.onTapGesture {
                        
                    }
                Spacer()
            }.frame(maxWidth: .infinity,alignment: .topLeading)
        }
    }
}

struct CheckListView: View {
    
    @State private var menu = Bundle.main.decode([ItemsSection].self, from: "items.json")
    
    var body: some View {
        NavigationView{
            List{
                ForEach($menu){ // from here on you have to pass a binding on to the decendent views
                    // mark the $ sign in front of the property name
                    $section in
                    NavigationLink(section.name) {
                        VStack{
                            ScrollView{
                                ForEach($section.items) { $item in
                                    //Pass the complete item to the CheckView not only the name
                                    CheckView(item: $item)
                                }
                            }
                        }
                    }
                }
            }
        }.navigationBarHidden(true)
            .navigationViewStyle(StackNavigationViewStyle())
            .listStyle(GroupedListStyle())
            .navigationViewStyle(StackNavigationViewStyle())
    }
}

示例 JSON:

[
    {
        "id": "9DC6D7CB-B8E6-4654-BAFE-E89ED7B0AF94",
        "name": "Africa",
        "items": [
            {
                "id": "59B88932-EBDD-4CFE-AE8B-D47358856B93",
                "name": "Algeria",
                "isChecked": false
            },
            {
                "id": "E124AA01-B66F-42D0-B09C-B248624AD228",
                "name": "Angola",
                "isChecked": false
                
            }
        ]
    }
]

評論:

使用 JSON 並將其存儲在包中的方法將阻止您在應用程序啟動之間保留isChecked屬性。 因為您不能從您的應用程序中寫入 Bundle。 只要應用程序處於活動狀態,該選擇就會一直存在,但一旦您重新安裝或強制退出它就會恢復默認設置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM