In response to @Vadian's request I have provided the code for a redacted View to add a Client with the relevant code for context as well as a sample JSON element. I have attempted various ways to create a reference back to the 'flag' in the JSON element related to the 'selectedCountry' over the last two days with no progress, as I understand that JSON does not have reference capability. The code inside the Picker works perfectly and displays a list of 'country.name's & 'country.flagImage's. I am trying to display the flag again after exiting the Picker. The secondary issue that 'selectedCountry' default setting of "Canada" does not appear initially in the Picker despite being an @State var. Any help would be appreciated!!!
CountryModel:
import SwiftUI
class Country: Codable, Identifiable, Comparable, ObservableObject {
static func == (lhs: Country, rhs: Country) -> Bool {
lhs.name == rhs.name
}
static func < (lhs: Country, rhs: Country) -> Bool {
lhs.name < rhs.name
}
let name: String
let isoAlpha3: String
let flag: Data?
var flagImage : UIImage? {
guard let flagData = flag else { return nil }
return UIImage(data: flagData)
}
struct HeadingData: Codable,Identifiable, Hashable {
let id: Int
let name: String
let isoAlpha3: String
let flag: Data
}
struct CurrencyData: Codable, Equatable, Identifiable, Hashable {
let id: UUID
let code: String
let name: String
let symbol: String
}
}
View (simplified):
import SwiftUI
struct SampleAddClient: View {
@Environment(\.managedObjectContext) var dbContext
@Environment(\.dismiss) var dismiss
let countries: [Country] = Bundle.main.decode("Countries.json")
@State private var inputName: String = ""
@State private var inputAddress: String = ""
@State private var selectedCountry: String = "Canada"
let client: ClientEntity?
var body: some View {
VStack(spacing: 12) {
HStack {
Text("Name:")
TextField("Client Name", text: $inputName)
.textFieldStyle(.roundedBorder)
}//: HSTACK1
HStack {
Text("Address")
TextField("Client Address", text: $inputAddress)
.textFieldStyle(.roundedBorder)
}//: HSTACK2
HStack {
Text("Client's Host Country:")
Spacer()
VStack {
Picker(selection: $selectedCountry,
label: Text("Host Country:")) {
ForEach(countries) { country in
HStack {
Text(country.name).tag(country.name)
// Find Flag
if let flagImage = country.flagImage {
Image(uiImage: flagImage).tag(country.flag)
} else {
let _ = print("Fail")
}
}
}
}
.padding(.horizontal, 0)
.padding()
.pickerStyle(MenuPickerStyle())
}//: INNER PICKER VSTACK
}//: HSTACK3
//Image(THIS IS WHERE THE FLAG IMAGE SHOULD GO.)
}//: OUTER VSTACK
.disableAutocorrection(true)
.padding()
.navigationBarTitle("Add Client")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
let newName = inputName.trimmingCharacters(in: .whitespaces)
let newAddress = inputAddress.trimmingCharacters(in: .whitespaces)
let newCountry = selectedCountry
if !newName.isEmpty {
Task(priority: .high) {
await storeClient(clientName: newName, clientAddress: newAddress, clientHostCountry: newCountry)
}
}
}
}
}//: TOOLBAR
}
func storeClient(clientName: String, clientAddress: String, clientHostCountry: String) async {
await dbContext.perform {
let newClient = ClientEntity(context: dbContext)
newClient.clientName = clientName
newClient.clientAddress = clientAddress
newClient.clientHostCountry = clientHostCountry
}
do {
try dbContext.save()
dismiss()
} catch {
print("Error saving record! \(error.localizedDescription)")
}
}
}
struct SampleAddClient_Previews: PreviewProvider {
static var previews: some View {
SampleAddClient(client: nil)
}
}
JSON Sample:
{ "id": 39, "name": "Canada", "isoAlpha2": "CA", "isoAlpha3": "CAN", "isoNumeric": 124, "currency": { "code": "CAD", "name": "Dollar", "symbol": "$" }, "flag": "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAUCAYAAACaq43EAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8 IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFREI0MDA4MjE3NzMxMUUyODY3Q0FBOTFCQzlGNjlDRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFREI0MDA4MzE3NzMxMUUyODY3Q0FBOTFCQzlGNjlDRiI IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkVEQjQwMDgwMTc3MzExRTI4NjdDQUE5MUJDOUY2OUNGIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkVEQjQwMDgxMTc3MzExRTI4NjdDQUE5MUJDOUY2OUNGIi8 IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8 IgrEzAAAAaZJREFUeNrEVktqwlAUPVr/TRXqB0QMHXbSqXsQdCRuQbsBR3bmJBtw7A4cuwyLBQNCKdgOVBCpIv7aUx8PiVZtNCo9cHm5yb3v3E/eTWwEPrEOlwtot4FIBGfBbAbE40C3u3HbsZQb/APslj3LZUDTrDMvS80NcbnIToc7MZkY18JO2K8wHu/2mU7JSIS/eRxHRTmfA6USMBjI3gnkckAoBBQKgNd7oYwFEomt6Hl3t99 T8Z20zdyhWZTrvn8tt3jo1wbjd2 R2fc65HpNFksksEgqWlkv0/a7Zs 7TZZqZCKQj49kckk fr6Z8bmpY5GN5 nUqTTaeg2mwxuPRi327TUfxMLp3x u6dmks1KX8vErRbp8x1PLKReP4H4 5us1cjb28MJPR6yWiW/vk7sscD7O3l/b06qqqSuH3ScDj/Hi4XceB pqMpodIHJdXUFPDwAfj gqsYWTifw9gaEw8D19QUm12xGDgaG/vJCPj8b nAobc4yudYhMgsEDF3XpaygKNLmQDgsf9Y PszH4kWIYzFgsTiJeGjJM5M56Q/kR4ABAHxxYPgLzAdZAAAAAElFTkSuQmCC" },
CodePudding user response:
Put tag on entire picker's row
HStack {
Text(country.name)
// Find Flag
if let flagImage = country.flagImage {
Image(uiImage: flagImage).tag(country.flag)
} else {
let _ = print("Fail")
}
}.tag(country.name) // << here !!
CodePudding user response:
you could try a small re-structure of your code using an ObservableObject
model to hold your countries: [Country]
. And a selectedCountry: Country?
for the Picker
selection,
such as in this example code (works well for me):
// -- here
struct Country: Codable, Identifiable, Comparable, Hashable {
let id = UUID() // <-- here
let name: String
let isoAlpha3: String
let flag: Data?
var flagImage: UIImage? {
guard let flagData = flag else { return nil }
return UIImage(data: flagData)
}
static func == (lhs: Country, rhs: Country) -> Bool {
lhs.name == rhs.name
}
static func < (lhs: Country, rhs: Country) -> Bool {
lhs.name < rhs.name
}
}
struct HeadingData: Codable, Identifiable, Hashable {
let id: Int
let name: String
let isoAlpha3: String
let flag: Data
}
struct CurrencyData: Codable, Equatable, Identifiable, Hashable {
let id: UUID
let code: String
let name: String
let symbol: String
}
// -- here
class CountryModel: ObservableObject {
@Published var countries: [Country] = []
init() {
self.countries = Bundle.main.decode("Countries.json")
}
}
struct SampleAddClient: View {
@Environment(\.managedObjectContext) var dbContext
@Environment(\.dismiss) var dismiss
@StateObject var viewModel = CountryModel() // <-- here
@State private var inputName: String = ""
@State private var inputAddress: String = ""
@State private var selectedCountry: Country? // <-- here
let client: ClientEntity?
var body: some View {
VStack(spacing: 12) {
HStack {
Text("Name:")
TextField("Client Name", text: $inputName)
.textFieldStyle(.roundedBorder)
}//: HSTACK1
HStack {
Text("Address")
TextField("Client Address", text: $inputAddress)
.textFieldStyle(.roundedBorder)
}//: HSTACK2
HStack {
Text("Client's Host Country:")
Spacer()
VStack {
Picker(selection: $selectedCountry, label: Text("Host Country:")) {
ForEach(viewModel.countries) { country in
HStack {
Text(country.name)
if let flagImage = country.flagImage {
Image(uiImage: flagImage)
} else {
Image(systemName: "questionmark")
}
}.tag(country as Country?) // <-- here
}
}
.padding(.horizontal, 20)
.padding()
.pickerStyle(MenuPickerStyle())
}
}//: HSTACK3
// THIS IS WHERE THE FLAG IMAGE SHOULD GO.
// -- here
VStack {
Text("selectedCountry: \(selectedCountry?.name ?? "no name")")
if let flagImage = selectedCountry?.flagImage {
Image(uiImage: flagImage)
}
}
}//: OUTER VSTACK
.onAppear {
selectedCountry = viewModel.countries.first // <-- here if required
}
.disableAutocorrection(true)
.padding()
.navigationBarTitle("Add Client")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
let newName = inputName.trimmingCharacters(in: .whitespaces)
let newAddress = inputAddress.trimmingCharacters(in: .whitespaces)
let newCountry = selectedCountry
if !newName.isEmpty {
Task(priority: .high) {
await storeClient(clientName: newName, clientAddress: newAddress, clientHostCountry: newCountry)
}
}
}
}
}//: TOOLBAR
}
func storeClient(clientName: String, clientAddress: String, clientHostCountry: String) async {
await dbContext.perform {
let newClient = ClientEntity(context: dbContext)
newClient.clientName = clientName
newClient.clientAddress = clientAddress
newClient.clientHostCountry = clientHostCountry
}
do {
try dbContext.save()
dismiss()
} catch {
print("Error saving record! \(error.localizedDescription)")
}
}
}