SwiftUI 带有动态数据的层次选择器

3

我正在尝试在SwiftUI(XCode 11.3.1)中使用具有动态数据的多个选择器。该应用程序有时会在模拟器和运行iOS 13.3.1的实际设备上崩溃,有时会冻结或在选择器中显示错误的数据。我尝试了这个问题答案中的建议,但没有成功。我做错了什么?

import SwiftUI

struct DbItem : Identifiable {
    var id: Int
    var name: String
}

final class DbStore : ObservableObject {

    let countries: [DbItem] = [DbItem(id: 0, name: "USA"), DbItem(id: 1, name: "France")]
    let citymap:[Int:[DbItem]] = [0:[DbItem(id: 10, name: "New York"), DbItem(id: 11, name: "Los Angeles"), DbItem(id: 12, name: "Dallas"), DbItem(id: 13, name: "Chicago")], 1:[DbItem(id: 20, name: "Paris"), DbItem(id: 21, name: "Nice"), DbItem(id: 22, name: "Lille")]]

    @Published var cities = [DbItem]()
    @Published var country : Int = -1 {
        willSet {
            if newValue >= 0 {
                self.id = UUID()
                DispatchQueue.main.async { [newValue] in
                    self.cities = self.citymap[newValue]!
                }
            }
        }
    }
    @Published var city : Int = -1 {
        didSet {
        }
    }
}

struct ContentView: View {
    @EnvironmentObject private var store: DbStore

    var body: some View {
        NavigationView {
            Form {
                VStack {
                    Picker(selection: $store.country,
                           label: Text("Country: ")
                    ) {
                        ForEach(store.countries) { country in
                            Text(country.name)
                        }
                    }
                    Picker(selection: $store.city,
                           label: Text("City: ")
                    ) {
                        ForEach(store.cities) { city in
                            Text(city.name)
                        }
                    }
                    .disabled(store.country < 0)
                }
            }
        }
    }
}

为什么你不按照提供的链接中所写的答案去做呢?哪一部分不够清晰明了? - user3441734
嗨@user3441734,首先感谢您在链接问题中提供的解决方案。我的实现方式不同之处在于Pickers的VStack嵌入了NavigationView+Form中。然后PickerStyle从轮式变为列表。这是我的设计要求。您可以通过将Pickers的VStack包装在“NavigationView { Form {...}}”中来重现问题。 - mect
我已经做出了必要的更改,请查看我的答案。 - user3441734
1个回答

2

使用你提出问题时提供的链接中的相同代码,我对代码进行了一些小改动,以适应你的需求。

struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Header").font(.title)) {
                    Picker(selection: $model.selectedContry, label: Text("Country")){
                        ForEach(0 ..< model.countryNemes.count){ index in
                            Text(self.model.countryNemes[index])
                        }
                    }
                    Picker(selection: $model.selectedCity, label: Text("City")){
                        ForEach(0 ..< model.cityNamesCount){ index in
                            Text(self.model.cityNames[index])
                        }
                        }
                    .id(model.id)
                }
            }.navigationBarTitle("Navigation Title")
        }
    }
}

请注意,在表单中没有 VStack,而是使用了 Section!结果按预期工作(其余代码保持不变)。请在真实设备上尝试代码(由于模拟器中已知的“返回按钮”错误)。
如果您对其余代码有困惑,这里有它的代码。
import Foundation
import SwiftUI

struct Country: Identifiable {
    var id: Int = 0
    var name: String
    var cities: [City]
}

struct City: Identifiable {
    var id: Int = 0
    var name: String
}

class Model: ObservableObject {
    let countries: [Country] = [Country(id: 0, name: "USA", cities: [City(id: 0, name: "New York"),City(id: 1, name: "Los Angeles"),City(id: 2, name: "Dallas"),City(id: 3, name: "Chicago")]),Country(id: 1, name: "France", cities: [City(id: 0, name: "Paris")])]

    @Published var selectedContry: Int = 0 {
        willSet {
            print("country changed", newValue, citySelections[newValue] ?? 0)
            selectedCity = citySelections[newValue] ?? 0
            id = UUID()
        }
    }
    @Published var id: UUID = UUID()
    @Published var selectedCity: Int = 0 {
        willSet {
            DispatchQueue.main.async { [newValue] in
                print("city changed", newValue)
                self.citySelections[self.selectedContry] = newValue
            }
        }
    }
    var countryNemes: [String] {
        countries.map { (country) in
            country.name
        }
    }
    var cityNamesCount: Int {
        cityNames.count
    }
    var cityNames: [String] {
        countries[selectedContry].cities.map { (city) in
            city.name
        }
    }

    private var citySelections: [Int: Int] = [:]
}

非常感谢 @user3441734!我已经因此开始抓狂了。 - mect

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接