I have implemented a functionality that changes the color scheme of the app. Everything works fine except the color scheme for the .alert()
that has a TextField
in it.
If is daylight and color scheme is changed to .dark
, the .alert()
color scheme is still .light
includind the TextField
.
That means that when you type something in the TextField
, the foreground color is white, because the text color in dark mode changes to white, and you can't really see what you are typing.
Similar, if the system's color scheme is .dark
and the app's color scheme is white, the .alert
is in .dark
mode and the text is black, because the text in the app is black.
Applying .background()
or foregroundColor()
to TextField
or VStack
doesn't have any effect.
Is there a way to control the color scheme for .alert()
? Or do I have to look for an .alert()
alternative?
Here is some code to reproduce the bahaviour. Thanks!
import SwiftUI
struct ContentView: View {
@State private var showAlert = false
@State private var addSomething: String = ""
@State private var isSystemMode = false
@State private var isDarkMode = false
var body: some View {
VStack {
VStack {
Toggle("Automatic (iOS Settings)", isOn: $isSystemMode)
if !isSystemMode {
Toggle("Dark Mode", isOn: $isDarkMode)
}
}
Button{
self.showAlert = true
} label: {
Text("Add something")
.frame(maxWidth: .infinity)
}
.alert("Add something", isPresented: $showAlert, actions: {
TextField("Something", text: $addSomething)
Button("Done") {
// Some action
}
Button("Cancel", role: .cancel, action: {})
}, message: {
Text("Type in something")
})
}
.padding()
.preferredColorScheme(isSystemMode ? .none : isDarkMode ? .dark : .light)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I think the only way to truly make this work is by making your own custom alert. Here is an example of a custom alert, but you might have to modify it according to your needs:
struct CustomAlertView<Content: View>: View {
@Environment(\.colorScheme) var colorScheme
let title: String
let description: String
var cancelAction: (() -> Void)?
var cancelActionTitle: String?
var primaryAction: (() -> Void)?
var primaryActionTitle: String?
var customContent: Content?
init(title: String,
description: String,
cancelAction: (() -> Void)? = nil,
cancelActionTitle: String? = nil,
primaryAction: (() -> Void)? = nil,
primaryActionTitle: String? = nil,
customContent: Content? = EmptyView()) {
self.title = title
self.description = description
self.cancelAction = cancelAction
self.cancelActionTitle = cancelActionTitle
self.primaryAction = primaryAction
self.primaryActionTitle = primaryActionTitle
self.customContent = customContent
}
var body: some View {
HStack {
VStack(spacing: 0) {
Text(title)
.font(.system(size: 16, weight: .semibold, design: .default))
.padding(.top)
.padding(.bottom, 8)
Text(description)
.font(.system(size: 12, weight: .light, design: .default))
.multilineTextAlignment(.center)
.padding([.bottom, .trailing, .leading])
customContent
Divider()
HStack {
if let cancelAction, let cancelActionTitle {
Button { cancelAction() } label: {
Text(cancelActionTitle)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
}
}
if cancelActionTitle != nil && primaryActionTitle != nil {
Divider()
}
if let primaryAction, let primaryActionTitle {
Button { primaryAction() } label: {
Text("**\(primaryActionTitle)**")
.frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
}
}
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50, alignment: .center)
}
.frame(minWidth: 0, maxWidth: 400, alignment: .center)
.background(.ultraThickMaterial)
.cornerRadius(10)
.padding([.trailing, .leading], 50)
}
.zIndex(1)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(
colorScheme == .dark
? Color(red: 0, green: 0, blue: 0, opacity: 0.4)
: Color(red: 1, green: 1, blue: 1, opacity: 0.4)
)
}
}
Usage:
struct ContentView: View {
@State private var showAlert = false
var body: some View {
ZStack {
Button("Show alert") {
withAnimation {
showAlert.toggle()
}
}
if showAlert {
CustomAlertView(
title: "Alert title",
description: "Description here",
cancelAction: {
// Cancel action here
withAnimation {
showAlert.toggle()
}
},
cancelActionTitle: "Cancel",
primaryAction: {
// Primary action here
withAnimation {
showAlert.toggle()
}
},
primaryActionTitle: "Action",
customContent:
Text("Custom content here")
.padding([.trailing, .leading, .bottom])
)
}
}
}
}
If you want to include a TextField simply do:
customContent:
TextField("Text here", text: $text)
.textFieldStyle(.roundedBorder)
.padding([.trailing, .leading, .bottom])