Picker 中的 SwiftUI FetchRequest 订单输出

SwiftUI FetchRequest Order Output in Picker

我有一个 json 我正在从包含国家列表(以及 ID、国家名称和国家缩写)的远程服务器中获取。我正在通过 ForEach 使用这些值填充选择器。

我无法解决的两个问题是:

  1. 我想在选择器中将美国显示为首选,然后按字母顺序列出所有其他国家/地区。

我考虑过使用 NSPredicate 或 NSCompoundPredicate 但这只是过滤掉值。我考虑过创建多个获取请求并过滤除美国以外的所有内容,然后另一个显示除美国以外的所有内容。这似乎是一种浪费,而且我不完全确定如何将它们结合起来。

  1. 根据用户的选择,我需要填充三个变量(id、name 和 abbr)。

我可以绑定到名称,而且效果很好。同样,我可以将选择器绑定到缩写并且它可以工作。但是,我不知道如何更新所有 3 个变量,因为选择器只有一个绑定。我尝试编写一个函数来在 ForEach 中设置所有这些变量 onTap 但这似乎不是一个好计划(或者也许是?)。

这是一些代码:

@FetchRequest(sortDescriptors: [SortDescriptor(\.name)] var countries: FetchedResults<Picker_Country>

// Load and decode the JSON from remote URL without caching

    func loadCountryData() async {
        
        let config = URLSessionConfiguration.default
        config.requestCachePolicy = .reloadIgnoringLocalCacheData
        config.urlCache = nil
        let URLSessionNoCache = URLSession(configuration: config)

        guard let url = URL(string: "https://api.foo.com/general/pickers/country_select") else {
            print("Invalid URL")
            return
        }

        do {
            let (data, _) = try await URLSessionNoCache.data(from: url)
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            let countries = try decoder.decode([CountryOptions].self, from: data)
            
            await MainActor.run {
                setCountryArray(with: countries)
            }

        } catch {
            print("Invalid Data")
        }
    }
    
    func setCountryArray(with downloadedCountryOptions: [CountryOptions]) {
        for countries in downloadedCountryOptions {
            let countryPicker = Picker_Country(context: moc)
            
            countryPicker.id =  Int16(countries.id)
            countryPicker.name = countries.name
            countryPicker.abbr = countries.abbr
        }
        try? moc.save()
    }

最后,这是我的选择器。

(注意:wrappedName 和 wrappedAbbr 只是允许我不必合并 nil 的变量。它们直接对应于名称和缩写。)

显然,通过使用 .tag,我显示了名称,但仅设置了缩写,这也是选择所绑定的内容。这就是用户选择时如何设置id、name、abbr的问题。

Picker("Country", selection: $createUser.countryAbbr) {
    ForEach(countries) { country in
        Text(country.wrappedName).tag(country.wrappedAbbr)
    } // End ForEach
} // End Picker
.pickerStyle(WheelPickerStyle())
.padding()
.labelsHidden()

实际上比我想象的要简单得多。当您在不使用 ForEach 的情况下创建选择器时,您可以执行以下操作:

Picker("Fooblah", selection: $fooblah) {
    Text("foo").tag(0)
    Text("blah").tag(1)
} // End Picker

现在,因为 JSON 存储在 CoreData 中,我们可以使用 NSPredicate 来过滤 FetchRequest 的结果,您执行两个 FetchRequests,如下所示:

@FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "abbr == 'US'")) var countryUS: FetchedResults<Picker_Country>
    @FetchRequest(sortDescriptors: [SortDescriptor(\.name)], predicate: NSPredicate(format: "abbr != 'US'")) var countries: FetchedResults<Picker_Country>

第一个 FetchRequest returns 仅限美国,而第二个 returns 美国除外。

现在,当您创建选择器时,您会执行两个 ForEach 语句,就像您会执行两个或更多 Text 语句一样,如下所示:

Picker("Country", selection: $createUser.countryAbbr) {
    ForEach(countryUS) { country in
        Text(country.wrappedName).tag(country.wrappedAbbr)
    } // End US ForEach
    ForEach(countries) { country in
        Text(country.wrappedName).tag(country.wrappedAbbr)
    } // End All except US ForEach
} // End Picker

使用 CoreData 并绑定到国家/地区的缩写,然后您可以创建一个关系,以便在 managedObjectContext 中您可以调用相应的国家/地区名称,就像您可以调用国家/地区的缩写一样。

另一个提示是,如果您只想使用国家/地区缩写,但仍以某种方式显示完整的国家/地区名称,您可以通过内置的语言环境功能来实现,如下所示:

Text(Locale.current.localizedString(forRegionCode: createUser.countryAbbr) ?? createUser.countryAbbr)

现在这对于您希望在 CoreData 的选择器中显示的任何类型的结果都是灵活的。只需根据您的情况修改 NSPredicate 过滤器即可。