带有动态数据的 SwiftUI 分层选择器 [英] SwiftUI hierarchical Picker with dynamic data
问题描述
我正在尝试在 SwiftUI (XCode 11.3.1) 中使用多个带有动态数据的选择器.该应用程序有时会崩溃,有时会冻结或在模拟器和运行 iOS 13.3.1 的真实设备上的选择器中显示错误的数据.我尝试了
如果您对其余代码有一些问题,这里是
导入基础导入 SwiftUI结构国家:可识别{变量 ID:整数 = 0变量名:字符串var 城市:[城市]}结构城市:可识别{变量 ID:整数 = 0变量名:字符串}类模型:ObservableObject {让国家:[国家] = [国家(ID:0,名称:美国",城市:[城市(ID:0,名称:纽约"),城市(ID:1,名称:洛杉矶"),City(id: 2, name: "Dallas"),City(id: 3, name: "Chicago")]),Country(id: 1, name: "France", city: [City(id: 0,名称:巴黎")])]@Published var selectedContry: Int = 0 {将设置{打印(国家改变",新价值,城市选择[新价值]??0)selectedCity = citySelections[newValue] ??0id = UUID()}}@Published var id: UUID = UUID()@Published var selectedCity: Int = 0 {将设置{DispatchQueue.main.async { [newValue] in打印(城市改变",新值)self.citySelections[self.selectedContry] = newValue}}}var countryNemes: [字符串] {country.map {(国家)在国家的名字}}var cityNamesCount: Int {cityNames.count}var cityNames: [字符串] {country[selectedContry].cities.map { (city) in城市名称}}私人 var citySelections: [Int: Int] = [:]}
I am trying to use multiple pickers with dynamic data in SwiftUI (XCode 11.3.1). The app sometimes crashes and sometimes freezes or shows the wrong data in the picker both in the simulator and on a real device running iOS 13.3.1. I tried the suggestions in the answers to this question with no success. What am I doing wrong?
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)
}
}
}
}
}
Using the same code as provided in link mentioned in your question I made some small changes to adopt the code for your needs
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")
}
}
}
Please see, that there is no VStack but Section in the Form! The result works as expected. (the rest of code is without any changes). Try the code on real device (due the known "back button" bug in simulator)
In case you have some trouble with the rest of code, here it is
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] = [:]
}
这篇关于带有动态数据的 SwiftUI 分层选择器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!