SwiftUI中如何用Toggle实现应用语言切换功能
解决SwiftUI中语言切换Toggle的选中状态异常问题
你的问题核心在于用两个独立的@State Bool管理单选逻辑,再加上错误的onChange设置,导致两个Toggle互相强制设为true,无法正常关闭。下面是正确的实现方案:
核心改进点
- 用单个状态变量管理当前选中的语言,替代两个独立的Bool,实现单选逻辑
- 给每个Toggle创建自定义Binding,关联选中状态与当前语言
- 选中语言时自动保存到UserDefaults并弹出重启提示
修正后的完整代码
struct Languages: View { @State private var selectedLanguage: String? @State private var showingAlert = false @Environment(\.presentationMode) var presentationMode init() { // 从UserDefaults初始化选中状态,默认取系统语言或"en" let savedLang = UserDefaults.standard.string(forKey: "language") _selectedLanguage = State(initialValue: savedLang ?? Locale.current.language.languageCode?.identifier ?? "en") } var body: some View { VStack(alignment: .leading) { DoubleTextView(topText: LocalizedStringKey("languages"), buttomText: "", topTextSize: 24, buttomTextSize: 0) // English Toggle ZStack(alignment: .top) { Toggle(isOn: Binding( get: { selectedLanguage == "en" }, set: { isSelected in if isSelected { selectedLanguage = "en" saveLanguageAndShowAlert() } } )) { 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)) // French Toggle ZStack(alignment: .top) { Toggle(isOn: Binding( get: { selectedLanguage == "fr" }, set: { isSelected in if isSelected { selectedLanguage = "fr" saveLanguageAndShowAlert() } } )) { 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) ) .alert("Restart your app", isPresented: $showingAlert) { Button("OK", role: .cancel) { } } .padding(.top, 40) .overlay { HStack { Spacer() Button { presentationMode.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) } // 抽离保存语言和弹出提示的逻辑 private func saveLanguageAndShowAlert() { guard let lang = selectedLanguage else { return } UserDefaults.standard.set(lang, forKey: "language") showingAlert = true } }
关键逻辑说明
- 状态初始化:在
init方法中从UserDefaults读取已保存的语言,没有的话用系统语言或默认英文,保证Toggle初始状态正确。 - 自定义Binding:每个Toggle的
isOn绑定通过get判断当前语言是否匹配,set只在选中时更新状态(因为是单选,不允许取消选中)。 - 逻辑抽离:把保存语言和弹出提示的代码放到单独的方法中,避免重复代码。
- 移除错误的onChange:之前的
onChange会强制将另一个Toggle设为true,导致状态混乱,现在用单选逻辑自然保证只有一个Toggle处于选中状态。
内容的提问来源于stack exchange,提问作者user19727360
相关产品推荐
相关产品推荐

