英文:
How to change SwiftUI alert color scheme?
问题
我已经实现了一个可以改变应用程序颜色方案的功能。一切都运行正常,除了带有TextField
的.alert()
的颜色方案。
如果是白天并且颜色方案更改为.dark
,.alert()
的颜色方案仍然是.light
,包括TextField
。
这意味着当您在TextField
中键入时,前景颜色是白色,因为在暗模式下,文本颜色会变为白色,您实际上看不到您在键入什么。
类似地,如果系统的颜色方案是.dark
,而应用程序的颜色方案是白色,那么.alert
会处于.dark
模式,文本是黑色,因为应用程序中的文本是黑色。
对TextField
或VStack
应用.background()
或foregroundColor()
没有任何效果。
是否有一种方法可以控制.alert()
的颜色方案?或者我是否需要寻找.alert()
的替代方案?
以下是一些代码以复现这种行为。谢谢!
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 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()
}
}
答案1
得分: 2
我认为要使这个工作起来的唯一方法是创建自定义提醒。以下是一个自定义提醒的示例,但你可能需要根据你的需求进行修改:
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 {
// ...
}
}
用法:
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: {
// 取消操作在这里
withAnimation {
showAlert.toggle()
}
},
cancelActionTitle: "Cancel",
primaryAction: {
// 主要操作在这里
withAnimation {
showAlert.toggle()
}
},
primaryActionTitle: "Action",
customContent:
Text("Custom content here")
.padding([.trailing, .leading, .bottom])
)
}
}
}
}
如果你想包含一个TextField,只需添加:
customContent:
TextField("Text here", text: $text)
.textFieldStyle(.roundedBorder)
.padding([.trailing, .leading, .bottom])
英文:
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])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论