SwiftUI中如何在应用设置里即时切换语言无需重启?
SwiftUI即时切换应用语言解决方案
问题原因
当前实现仅将选中语言保存到UserDefaults,但LocalizedStringKey会在视图初始化时一次性解析,不会监听语言变化;同时没有通知所有视图刷新,导致必须重启应用才能生效。
解决步骤
1. 创建语言管理类
实现一个ObservableObject统一管理语言状态,触发视图刷新并提供本地化字符串:
import SwiftUI class LanguageManager: ObservableObject { static let shared = LanguageManager() @Published var currentLanguage: String { didSet { UserDefaults.standard.set(currentLanguage, forKey: "language") } } init() { currentLanguage = UserDefaults.standard.string(forKey: "language") ?? Locale.preferredLanguages.first ?? "en" } // 获取对应语言的资源包 var localizedBundle: Bundle { let path = Bundle.main.path(forResource: currentLanguage, ofType: "lproj") ?? Bundle.main.path(forResource: "en", ofType: "lproj")! return Bundle(path: path)! } // 获取本地化字符串 func localizedString(for key: String) -> String { NSLocalizedString(key, bundle: localizedBundle, comment: "") } }
2. 在App入口注入环境对象
让所有视图都能访问语言管理实例:
@main struct YourAppName: App { var body: some Scene { WindowGroup { ContentView() .environmentObject(LanguageManager.shared) } } }
3. 重构语言设置页面
简化代码逻辑,使用语言管理类的状态而非本地@State:
struct Languages: View { @State private var showingAlert = false @Environment(\.presentationMode) var mode @EnvironmentObject var languageManager: LanguageManager // 语言选项列表,简化重复代码 private let languageOptions: [(code: String, name: String)] = [ ("en", "English"), ("ru", "Русский"), ("fr", "Français"), ("es", "Español"), ("ko", "한국어"), ("ja", "日本語"), ("zh-Hans", "简体中文"), ("id", "Indonesia"), ("pt-BR", "Português (Brasil)"), ("it", "Italiano"), ("de", "Deutsch") ] var body: some View { VStack(alignment: .leading) { // 使用本地化标题 DoubleTextView( topText: LocalizedStringKey(languageManager.localizedString(for: "languages")), buttomText: "", topTextSize: 24, buttomTextSize: 0 ) // 遍历生成语言切换按钮 ForEach(languageOptions, id: \.code) { option in ZStack { Toggle(isOn: Binding( get: { languageManager.currentLanguage == option.code }, set: { isOn in languageManager.currentLanguage = isOn ? option.code : (Locale.preferredLanguages.first ?? "en") } )) { Text(option.name) .foregroundColor(.white) .font(.custom("Manrope-Bold", size: 16)) } .padding(.horizontal) .toggleStyle(SwitchToggleStyle(tint: Color("active"))) } .padding(.vertical, 10) .background(Rectangle() .fill(Color("navigation")) .frame(height: 50) .cornerRadius(8)) } } .alert(isPresented: $showingAlert) { Alert( title: Text(languageManager.localizedString(for: "later")), primaryButton: .destructive(Text(languageManager.localizedString(for: "Ok"))) { // 可选:请求刷新场景替代强制退出 UIApplication.shared.requestSceneSessionRefresh(nil) }, secondaryButton: .cancel(Text(languageManager.localizedString(for: "laterButton"))) ) } .background( Image("background2") .resizable() .scaledToFill() .edgesIgnoringSafeArea(.all) .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) ) .onChange(of: languageManager.currentLanguage) { _ in showingAlert = true } .padding(.top, 40) .overlay ( HStack { Button { mode.wrappedValue.dismiss() } label: { 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) } }
4. 全局替换本地化文本使用方式
将所有直接使用LocalizedStringKey的地方,改为通过LanguageManager获取本地化字符串,比如自定义通用组件:
struct LocalizedText: View { let key: String @EnvironmentObject var languageManager: LanguageManager var body: some View { Text(languageManager.localizedString(for: key)) } } // 使用示例:LocalizedText(key: "welcome_message")
关键说明
@Published属性会在语言变化时自动通知所有依赖的视图重新渲染- 自定义
localizedBundle确保加载选中语言的本地化资源文件 - 避免使用
exit(0)强制退出,改用requestSceneSessionRefresh更符合iOS应用规范
内容的提问来源于stack exchange,提问作者RinaredStaring
相关产品推荐
相关产品推荐

