英文:
Change white and dark mode in SwiftUI App and persist it
问题
I want my app to support both light and dark themes, where users can change the theme to their preference or keep it as the default system theme. When users switch the theme to dark or light mode, the app should remember their choice and apply the same theme the next time they open the app. And I don't want to change it for every single view instead I want it for the whole app. Now it's changing the theme but when I reopen the app it sets to default.
I changing the theme like this:
struct Settings1: View {
@Environment(\.colorScheme) private var colorScheme
var body: some View {
VStack(alignment: .leading){
Text("Select Mode")
.font(.custom("Inter-Medium", size: 22))
Spacer()
Button {
changeTheme(to: .dark)
} label: {
HStack{
ZStack{
Circle()
.fill(colorScheme == .dark ? Color.green:Color.gray)
.frame(width:32 ,height: 32)
Circle()
.fill(Color.black)
.frame(width:30 ,height: 30)
}
Text("Dark Mode")
.font(.custom("Inter-Medium", size: 19))
.foregroundColor(Color("dayNightText"))
.padding(.leading)
Spacer()
}
}
Button {
changeTheme(to: .light)
} label: {
HStack{
ZStack{
Circle()
.fill(colorScheme == .light ? Color.green:Color.gray)
.frame(width:32 ,height: 32)
Circle()
.fill(Color.white)
.frame(width:30 ,height: 30)
}
Text("White Mode")
.font(.custom("Inter-Medium", size: 19))
.foregroundColor(Color("dayNightText"))
.padding(.leading)
Spacer()
}
}
Button {
changeTheme(to: .device)
} label: {
HStack{
ZStack{
Circle()
.fill(colorScheme == .light ? Color.green:Color.gray)
.frame(width:32 ,height: 32)
Circle()
.fill(Color.white)
.frame(width:30 ,height: 30)
}
Text("System Default")
.font(.custom("Inter-Medium", size: 19))
.foregroundColor(Color("dayNightText"))
.padding(.leading)
Spacer()
}
}
Spacer()
}
.frame(width: 300, height: 300)
.background(Color.gray.ignoresSafeArea())
.cornerRadius(30)
}
func changeTheme(to theme: Theme) {
UserDefaults.standard.theme = theme
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = theme.userInterfaceStyle
}
}
struct Settings1_Previews: PreviewProvider {
static var previews: some View {
Settings1()
}
}
enum Theme: Int {
case device
case light
case dark
}
extension Theme {
var userInterfaceStyle: UIUserInterfaceStyle {
switch self {
case .device:
return .unspecified
case .light:
return .light
case .dark:
return .dark
}
}
}
extension UserDefaults {
var theme: Theme {
get {
register(defaults: [#function: Theme.device.rawValue])
return Theme(rawValue: integer(forKey: #function)) ?? .device
}
set {
set(newValue.rawValue, forKey: #function)
}
}
}
and then on re-launch trying to recover the previous one but it's not working
import SwiftUI
@main
struct TestApp: App {
init(){
let savedTheme = UserDefaults.standard.theme
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = savedTheme.userInterfaceStyle
}
var body: some Scene {
WindowGroup {
NavigationView{
Settings1()
}
}
}
}
英文:
I want my app to support both light and dark themes, where users can change the theme to their preference or keep it as the default system theme. When users switch the theme to dark or light mode, the app should remember their choice and apply the same theme the next time they open the app. And I don't want to change it for every single view instead I want it for whole app. Now its changing the theme but when I reopened the app it sets to default.
I changing the theme like this :
struct Settings1: View {
@Environment(\.colorScheme) private var colorScheme
var body: some View {
VStack(alignment: .leading){
Text("Select Mode")
.font(.custom("Inter-Medium", size: 22))
Spacer()
Button {
changeTheme(to: .dark)
} label: {
HStack{
ZStack{
Circle()
.fill(colorScheme == .dark ? Color.green:Color.gray)
.frame(width:32 ,height: 32)
Circle()
.fill(Color.black)
.frame(width:30 ,height: 30)
}
Text("Dark Mode")
.font(.custom("Inter-Medium", size: 19))
.foregroundColor(Color("dayNightText"))
.padding(.leading)
Spacer()
}
}
Button {
changeTheme(to: .light)
} label: {
HStack{
ZStack{
Circle()
.fill(colorScheme == .light ? Color.green:Color.gray)
.frame(width:32 ,height: 32)
Circle()
.fill(Color.white)
.frame(width:30 ,height: 30)
}
Text("White Mode")
.font(.custom("Inter-Medium", size: 19))
.foregroundColor(Color("dayNightText"))
.padding(.leading)
Spacer()
}
}
Button {
changeTheme(to: .device)
} label: {
HStack{
ZStack{
Circle()
.fill(colorScheme == .light ? Color.green:Color.gray)
.frame(width:32 ,height: 32)
Circle()
.fill(Color.white)
.frame(width:30 ,height: 30)
}
Text("System Default")
.font(.custom("Inter-Medium", size: 19))
.foregroundColor(Color("dayNightText"))
.padding(.leading)
Spacer()
}
}
Spacer()
}
.frame(width: 300, height: 300)
.background(Color.gray.ignoresSafeArea())
.cornerRadius(30)
}
func changeTheme(to theme: Theme) {
UserDefaults.standard.theme = theme
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = theme.userInterfaceStyle
}
}
struct Settings1_Previews: PreviewProvider {
static var previews: some View {
Settings1()
}
}
enum Theme: Int {
case device
case light
case dark
}
extension Theme {
var userInterfaceStyle: UIUserInterfaceStyle {
switch self {
case .device:
return .unspecified
case .light:
return .light
case .dark:
return .dark
}
}
}
extension UserDefaults {
var theme: Theme {
get {
register(defaults: [#function: Theme.device.rawValue])
return Theme(rawValue: integer(forKey: #function)) ?? .device
}
set {
set(newValue.rawValue, forKey: #function)
}
}
}
and then on re launch trying to recover the previous one but its not working
import SwiftUI
@main
struct TestApp: App {
init(){
let savedTheme = UserDefaults.standard.theme
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = savedTheme.userInterfaceStyle
}
var body: some Scene {
WindowGroup {
NavigationView{
Settings1()
}
}
}
}
答案1
得分: 2
你可以在启动时使用 .preferredColorScheme
来设置 colorScheme
。
@main
struct StackOverflowDebuggingApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
Settings1()
} .preferredColorScheme(UserDefaults.standard.theme.colorScheme)
}
}
}
然后,像你之前在 userInterfaceStyle
中已经做的那样,为 Theme
添加一个 colorScheme
扩展:
extension Theme {
var userInterfaceStyle: UIUserInterfaceStyle {
switch self {
case .device:
return .unspecified
case .light:
return .light
case .dark:
return .dark
}
}
var colorScheme: ColorScheme? {
switch self {
case .device:
return nil
case .light:
return .light
case .dark:
return .dark
}
}
}
为了能够覆盖 preferredColorScheme
,请更新你的 changeTheme
函数以覆盖 rootViewController
,如下所示:
func changeTheme(to theme: Theme) {
UserDefaults.standard.theme = theme
UIApplication.shared.windows.first?.rootViewController?.overrideUserInterfaceStyle = theme.userInterfaceStyle
}
在 Xcode 15 Beta 上,在 iOS 17 模拟器上运行良好,不过不确定你使用的是哪个版本。
英文:
You can incorporate .preferredColorScheme to set the colorScheme on launch.
@main
struct StackOverflowDebuggingApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
Settings1()
} .preferredColorScheme(UserDefaults.standard.theme.colorScheme)
}
}
}
Then add a colorScheme extension to Theme exactly like how you already did with userInterfaceStyle
extension Theme {
var userInterfaceStyle: UIUserInterfaceStyle {
switch self {
case .device:
return .unspecified
case .light:
return .light
case .dark:
return .dark
}
}
var colorScheme: ColorScheme? {
switch self {
case .device:
return nil
case .light:
return .light
case .dark:
return .dark
}
}
}
To be able to override the preferredColorScheme though update your changeTheme function to override the rootViewController like so
func changeTheme(to theme: Theme) {
UserDefaults.standard.theme = theme
UIApplication.shared.windows.first?.rootViewController?.overrideUserInterfaceStyle = theme.userInterfaceStyle
}
Ran fine for me in Xcode 15 Beta on an iOS 17 simulator, not sure what version you're on though.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论