Michael
Michael

Reputation: 726

ForEach two arrays - simple solution possible?

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))
    }
}

Upvotes: 2

Views: 129

Answers (5)

swiftPunk
swiftPunk

Reputation: 1

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)
            }
        }   
    }
}

Upvotes: 1

mahan
mahan

Reputation: 15015

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))
    }
}

Upvotes: 0

Duncan C
Duncan C

Reputation: 131491

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?

Upvotes: 0

Tushar Sharma
Tushar Sharma

Reputation: 2882

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))
    }
}

Upvotes: 0

pawello2222
pawello2222

Reputation: 54621

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))
    }
}

Upvotes: 1

Related Questions