ForEach two arrays - simple solution possible?

Viewed 115

I'm struggling with a simple solution I can't find. I want to build a picker which shows the flags and selects the Language code from the other array. There must be a simple solution. so long I helped myself with this ugly onChange crutch.

Any suggestions?

struct LanguageView: View {
    
    @AppStorage ("language")var language: String = "de"
    @State private var flag: String = ""
    var languages = ["de", "en", "pl"]
    var languageFlags = ["", " ", "" ]
    
    var body: some View {
        
        VStack{
            
            HStack{
                
                Picker("", selection: $flag) {
                    ForEach(languageFlags, id: \.self) {
                        Text($0)
                            .font(.caption2)       
                    }
                    
                }
                .pickerStyle(SegmentedPickerStyle())
                
            }.onChange(of: flag, perform: { value in
                if flag == "" {language = "de"}
                if flag == " " {language = "en"}
                if flag == "" {language = "pl"}
            })
        }
        
        .environment(\.locale, .init(identifier: language))
    }
}
5 Answers

You can create an enum for Language:

enum Language: String, CaseIterable {
    case de, en, pl
    
    var flag: String {
        switch self {
        case .de:
            return ""
        case .en:
            return " "
        case .pl:
            return ""
        }
    }
}

Then, you don't need extra @State variables nor onChange:

struct LanguageView: View {
    @AppStorage("language") var language: Language = .de

    var body: some View {
        VStack {
            HStack {
                Picker("", selection: $language) {
                    ForEach(Language.allCases, id: \.self) {
                        Text($0.flag)
                            .font(.caption2)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
            }
        }
        .environment(\.locale, .init(identifier: language.rawValue))
    }
}

I think this would also a good answer for your need, you should define a custom type like I did! With my code, the selected Language saved directly and you should not worry about it! more secure I would say.


import SwiftUI

struct ContentView: View {
    var body: some View {
        LanguageView()
    }
}

struct Language: Hashable {
    let language: String
    let flag: String
}

let languageArray: [Language] = [Language(language: "de", flag: ""), Language(language: "en", flag: " "), Language(language: "pl", flag: "")]

struct LanguageView: View {
    @AppStorage ("language")var selectedFlag: Int = 0
    var body: some View {
        VStack{
            HStack{
                Picker("", selection: $selectedFlag) {
                    ForEach(languageArray.indices, id: \.self) { index in
                        Text(languageArray[index].flag)
                            .font(.caption2)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
            }
            .onAppear() { print("language is: " + languageArray[selectedFlag].language) }
            .onChange(of: selectedFlag) { newValue in
                print("language is: " + languageArray[newValue].language)
            }
        }   
    }
}

You could also have Array of type [(String,String)] a Tuple. If you don’t want parameter names, you can also access them using indices 0 and 1

struct LanguageView: View {
    
    @AppStorage ("language")var language: String = "de"
    @State private var flag: Int = 0
    
    var languageFlags = [(flag:"",language:"de"), (flag:" ",language:"en"), (flag:"",language:"pl")]
    
   
    var body: some View {
        
        VStack{
            HStack{
                Picker("", selection: $flag) {
                    ForEach(languageFlags.indices) { (index) in
                        Text(languageFlags[index].flag)
                            .font(.caption2)
                    }
                    
                }
                .pickerStyle(SegmentedPickerStyle())
                
            }.onChange(of: flag, perform: { index in
                language = languageFlags[index].language
            })
        }
        
        .environment(\.locale, .init(identifier: language))
    }
}

Use enum for languages and confirm to the CaseIterable protocol so that you can itereate over all of its cases (languages).

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. https://docs.swift.org

enum Language: String, CaseIterable {
    case de = ""
    case en = " "
    case pl = ""

    var name: String {
        get { return String(describing: self) }
    }
}



struct LanguageView: View {
    @AppStorage("language") var language: Language = .de

    var body: some View {
        VStack {
            HStack {
                Picker("", selection: $language) {
                    ForEach(Language.allCases, id: \.self) { language in
                        Text(language.rawValue)
                            .font(.caption2)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
            }
        }
        .environment(\.locale, .init(identifier: language.name))
    }
}

Everybody else posted an answer that preserved your combining the flags for the US and the UK. If you need to map from a user-selected flag to a language, a Dictionary seems like the obvious choice:

let flagsAndLanguages = ["": "de", "" : "en", "": "en", "" : "pl"]

let aFlag = ""

func languageForFlag(_ flag: String) -> String {
    return flagsAndLanguages[aFlag] ?? "unknown"
}

print("The lanagugage for the flag \(aFlag) is '\(languageForFlag(aFlag))'")

This outputs The lanagugage for the flag is 'en'.

Looking up entries in a Dictionary is extremely fast. KeyValuePairs, in contrast, are slower, but preserve ordering. I don't think you need to preserve a specific order of your flags/languages, do you?

Related