简体   繁体   中英

How to change language of the app with toggles in SwiftUI

I have a view with list of toggles with languages that should change current app language after the restart. I understand how to create this feature with buttons, but it doesn't work the same way with toggles. I've tried to use on change, but it's not allowing to turn off the toggle after the second tap. How to do that properly?


struct Languages: View {
    
    @State private var currentLanguage = true
    @State private var currentLanguageEnglish = true
    @State private var currentLanguageRussian = false

    @State private var showingAlert = false

    @Environment(\.presentationMode) var mode: Binding<PresentationMode>
    
    @State var currentSysLanguage = UserDefaults.standard.string(forKey: "language")
    
    var body: some View {
        VStack(alignment: .leading) {
            
            DoubleTextView(topText: LocalizedStringKey("languages"), buttomText: "", topTextSize: 24, buttomTextSize: 0)
            
// Works just right, wrong design.
            Button("English", action: {
                currentSysLanguage = "en"
                UserDefaults.standard.set(currentSysLanguage, forKey: "language")
                showingAlert.toggle()
            })
            .alert("Restart your app", isPresented: $showingAlert) {
                        Button("OK", role: .cancel) { }
                    }
            
            Button("French", action: {
                currentSysLanguage = "fr"
                UserDefaults.standard.set(currentSysLanguage, forKey: "language")
                showingAlert.toggle()
            })
            .alert("Restart your app", isPresented: $showingAlert) {
                        Button("OK", role: .cancel) { }
                    }
            
// Does not work, this is how it should be.
            ZStack(alignment: .top) {
                Toggle(isOn: $currentLanguageEnglish) {
                    
                    Text("English")
                        .font(.custom("Manrope-Bold", size: 16))
                        .foregroundColor(.white)
                    
                }
                .padding(.horizontal)
                .tint(Color("active"))
            }
            .padding(.vertical, 30.0)
            .background(Rectangle()
                .fill(Color("navigation"))
                .frame(height: 50)
                .cornerRadius(8))
            
            ZStack(alignment: .top) {
                Toggle(isOn: $currentLanguageRussian) {
                    Text("French")
                        .font(.custom("Manrope-Bold", size: 16))
                        .foregroundColor(.white)
                    
                    
                }
                .padding(.horizontal)
                .tint(Color("active"))
            }
            .background(Rectangle()
                .fill(Color("navigation"))
                .frame(height: 50)
                .cornerRadius(8))
            


            Spacer()
        }
        .background(
            Image("background2")
                .resizable()
                .scaledToFill()
                .edgesIgnoringSafeArea(.all)
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        )
        .onChange(of: currentLanguageEnglish, perform: { newValue in
            currentLanguageRussian = true
        })
        .onChange(of: currentLanguageRussian, perform: { newValue in
            currentLanguageEnglish = true
        })

        .padding(.top, 40)
        .overlay {
            HStack {
                Spacer()
                Button {
                    
                    self.mode.wrappedValue.dismiss()
                    
                } label: {
                    HStack {
                    Image(systemName: "arrow.left")
                        .foregroundColor(.white)
                        .font(.system(size: 24))
                        
                        Spacer()
                }
                }
            }
            .padding(.horizontal, 3.0)
            .frame(maxHeight: .infinity, alignment: .top)
        }
        .navigationBarTitle("", displayMode: .inline)
        .navigationBarHidden(true)
        .padding(.horizontal, 10.0)
    }
}

There are many ways you can accomplish something similar, one is to make the toggle bind to a custom binding object:

Toggle(isOn: .init(get: { 
    currentSysLanguage == "en"
}, set: { isOn in
    currentSysLanguage = isOn ? "en" : defaultLanguage
})) {
    Text("English")
}

Here the getter makes the toggle listen to currentSysLanguage == "en" . It will be on/off based on if the statement evaluates true. Whereas the setter will be triggered when you manually toggle it, and set currentSysLanguage between "en" and defaultLanguage .

Note I have added the defaultLanguage to represent the state when all toggles are off.

You can then create as many toggles as needed with the same code by changing the language code it is comparing to.

Near the end, you would add a .onChange modifier to listen to currentSysLanguage to send the changes to UserDefaults :

.onChange(of: currentSysLanguage, perform: { newValue in
    UserDefaults.standard.set(newValue, forKey: "language")
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM