在 SwiftUI 中设置切换颜色
Set Toggle color in SwiftUI
我跟随 Apple 的 tutorial on user input 实现了一个切换。目前,它看起来像这样:
这是生成此 UI 的代码:
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
}
}
现在,我希望 Toggle
的 颜色为蓝色而不是绿色。
我试过了:
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
.accentColor(.blue)
.foregroundColor(.blue)
.background(Color.blue)
None 其中有效,我找不到任何其他修饰符,例如 tintColor
.
如何更改 Toggle
的颜色?
我还没有找到直接更改 Toggle
颜色的方法,但是使用蓝色开关或任何其他自定义视图的另一种方法是创建您自己的自定义视图。要以最简单的形式制作自定义蓝色开关:
struct BlueToggle : UIViewRepresentable {
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.onTintColor = UIColor.blue
}
}
struct ContentView : View {
var body: some View {
BlueToggle()
}
}
结果:
只需使用 UIAppearance
API:
UISwitch.appearance().onTintColor = UIColor.blue
根据 UIAppearance
文档,它当然会默认更改 UISwitch
所有实例的外观。
注意:从 Xcode 11 beta 5 开始测试。
这个 (现在是 Xcode 11 beta 6)是一个解决方案。要在选项之间切换,一种快速的方法是使用布尔值而不是 if/else:
showFavoritesOnly ? .red : .blue
前景:
Toggle(isOn: $showGreeting) {
Text("Show Favorites only").foregroundColor(showFavoritesOnly ? .blue : .gray)
}
色调:
uiView.onTintColor = showFavoritesOnly ? UIColor.blue : UIColor.gray
另外自定义颜色:
SwiftUI 3.0
使用色调
引入了一个新的修改器,它也可以改变切换颜色:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.tint(.red)
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.tint(.orange)
SwiftUI 2.0
使用 SwitchToggleStyle
您现在只能在 SwiftUI 2.0 中为打开位置设置色调颜色:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.red))
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.orange))
SwiftUI 1.0
使用 ToggleStyle
我创建了一个新的 ToggleStyle 来更改 Toggle 的三种颜色(打开颜色、关闭颜色和拇指)。
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = Color(UIColor.green)
var offColor = Color(UIColor.systemGray5)
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
Button(action: { configuration.isOn.toggle() } )
{
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
}
}
.font(.title)
.padding(.horizontal)
}
}
使用示例
Toggle("", isOn: $toggleState)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .green,
offColor: .red,
thumbColor: Color(UIColor.systemTeal)))
Toggle("", isOn: $toggleState2)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .purple))
来自 SwiftUI 书籍
您可以在 init() 中修改所有 UISwitch 对象的全局 onTintColor。
@State var enable_dhcp = true
init()
{
UISwitch.appearance().onTintColor = .red
}
var body: some View
{
Toggle("DHCP", isOn: $enable_dhcp)
}
建立在@mohammad-reza-farahani 的解决方案之上,这是一种完全不妥协的方法,可以通过 SwiftUI 的实现协议获得 UISwitch 的可配置性。
首先将 UISwitch
包裹在 UIViewRepresentable
中,然后根据需要设置颜色:
final class CustomToggleWrapper: UIViewRepresentable {
var isOn: Binding<Bool>
init(isOn: Binding<Bool>) {
self.isOn = isOn
}
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
// On color
uiView.onTintColor = UIColor.blue
// Off color
uiView.tintColor = UIColor.red
uiView.layer.cornerRadius = uiView.frame.height / 2
uiView.backgroundColor = UIColor.red
uiView.isOn = isOn.wrappedValue
// Update bound boolean
uiView.addTarget(self, action: #selector(switchIsChanged(_:)), for: .valueChanged)
}
@objc
func switchIsChanged(_ sender: UISwitch) {
isOn.wrappedValue = sender.isOn
}
}
其次,使用包装的 UISwitch
:
创建一个 custom toggle style
struct CustomToggleStyle: ToggleStyle {
func makeBody(configuration: Self.Configuration) -> some View {
let toggle = CustomToggleWrapper(isOn: configuration.$isOn)
return HStack {
configuration.label
Spacer()
toggle
}
}
}
像往常一样实施 Toggle
,并应用您的 CustomToggleStyle
:
struct TestView: View {
@State private var isOn: Bool = true
var body: some View {
Toggle(
isOn: $isOn
) {
Text("Test: \(String(isOn))")
}.toggleStyle(CustomToggleStyle()).padding()
}
}
因为最初的问题只是关于改变颜色的切换而不是完整的 Toggle
视觉定制,我认为这样的事情会做:
import SwiftUI
struct CustomToggle: UIViewRepresentable {
@Binding var isOn: Bool
func makeCoordinator() -> CustomToggle.Coordinator {
Coordinator(isOn: $isOn)
}
func makeUIView(context: Context) -> UISwitch {
let view = UISwitch()
view.onTintColor = UIColor.red
view.addTarget(context.coordinator, action: #selector(Coordinator.switchIsChanged(_:)), for: .valueChanged)
return view
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
class Coordinator: NSObject {
@Binding private var isOn: Bool
init(isOn: Binding<Bool>) {
_isOn = isOn
}
@objc func switchIsChanged(_ sender: UISwitch) {
_isOn.wrappedValue = sender.isOn
}
}
}
// MARK: - Previews
struct CustomToggle_Previews: PreviewProvider {
static var previews: some View {
ViewWrapper()
}
struct ViewWrapper: View {
@State(initialValue: false) var isOn: Bool
var body: some View {
CustomToggle(isOn: $isOn)
.previewLayout(.fixed(width: 100, height: 100))
}
}
}
Karol Kulesza 和 George Valkov 提供了一个非常易于实施的解决方案。我只是想补充一点,您也可以将下面的代码放在应用程序委托的 didFinishLaunching 方法中。
UISwitch.appearance().onTintColor = .blue
您还可以使用
创建更具体的外观配置
appearance(whenContainedInInstancesOf:)
见https://www.hackingwithswift.com/example-code/uikit/what-is-the-uiappearance-proxy
SwiftUI 2.0(Post WWDC-2020)
使用新的 SwiftUI 增强功能,您可以使用 .toggleStyle
修饰符。
// Switch tinting
Toggle(isOn: $order.notifyWhenReady) {
Text("Send notification when ready")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
请注意,这仅适用于 iOS14/iPadOS14/macOS11 及以上。
我会稍微更改@Mark Moeykens 的回答以避免出现按钮点击动画。更好的解决方案是:
@available(iOS 13.0, *)
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = UIColor.proacPrimaryBlue.suColor
var offColor = UIColor.systemGray5.suColor
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
.onTapGesture {
configuration.isOn.toggle()
}
}
.font(.title)
.padding(.horizontal)
}
}
- 最简单的方法是在使用 toggle 之前设置
UISwitch.appearance().onTintColor = UIColor.red
并像下面这样使用 SwiftUI Toggle。
UISwitch.appearance().onTintColor = UIColor.red
...
let toggle = Toggle(isOn: $vm.dataUsePermission, label: {
Text(I18N.permit_data_usage)
.font(SwiftUI.Font.system(size: 16, weight: .regular))
})
if #available(iOS 14.0, *) {
toggle.toggleStyle(
SwitchToggleStyle(tint: Color(UIColor.m.blue500))
)
} else {
toggle.toggleStyle(SwitchToggleStyle())
}
...
- 您也可以在 SwiftUI 中使用相同的 Toggle 界面但名称不同,并更改色调颜色。
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
如果只需要Toggle而不需要Label,那么
TintableUISwitch(isOn: .constant(true))
使用下面的代码。
import SwiftUI
public struct TintableSwitch<Label>: View where Label: View {
@Binding var isOn: Bool
var label: Label
public init(isOn: Binding<Bool>, @ViewBuilder label: () -> Label) {
self._isOn = isOn
self.label = label()
}
public var body: some View {
HStack {
label
Spacer()
TintableUISwitch(isOn: $isOn, onTintColor: .red) // CHANGE HERE
}
}
}
public struct TintableUISwitch: UIViewRepresentable {
@Binding var isOn: Bool
private var onTintColor: UIColor
public init(isOn: Binding<Bool>, onTintColor: UIColor = UIColor.m.blue500) {
self._isOn = isOn
self.onTintColor = onTintColor
}
public func makeUIView(context: Context) -> UISwitch {
let uiSwitch = UISwitch()
uiSwitch.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged(_:)),
for: .valueChanged
)
uiSwitch.onTintColor = onTintColor
uiSwitch.isOn = isOn
return uiSwitch
}
public func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public class Coordinator: NSObject {
var tintableSwitch: TintableUISwitch
init(_ tintableSwitch: TintableUISwitch) {
self.tintableSwitch = tintableSwitch
}
@objc
func valueChanged(_ sender: UISwitch) {
tintableSwitch.isOn = sender.isOn
}
}
}
struct TintableSwitch_Previews: PreviewProvider {
static var previews: some View {
VStack {
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
}
}
}
struct TintableUISwitch_Previews: PreviewProvider {
static var previews: some View {
TintableUISwitch(isOn: .constant(true))
}
}
您可以使用色调修改器在 IOS 15.0 中更改切换颜色。
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.tint(.red)
IOS15.0及以下,可以使用toggleStyle修饰符改变切换颜色,但以后会贬值
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.toggleStyle(SwitchToggleStyle(tint: .red))
我跟随 Apple 的 tutorial on user input 实现了一个切换。目前,它看起来像这样:
这是生成此 UI 的代码:
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
}
}
现在,我希望 Toggle
的 颜色为蓝色而不是绿色。
我试过了:
Toggle(isOn: $showFavoritesOnly) {
Text("Show Favorites only")
}
.accentColor(.blue)
.foregroundColor(.blue)
.background(Color.blue)
None 其中有效,我找不到任何其他修饰符,例如 tintColor
.
如何更改 Toggle
的颜色?
我还没有找到直接更改 Toggle
颜色的方法,但是使用蓝色开关或任何其他自定义视图的另一种方法是创建您自己的自定义视图。要以最简单的形式制作自定义蓝色开关:
struct BlueToggle : UIViewRepresentable {
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.onTintColor = UIColor.blue
}
}
struct ContentView : View {
var body: some View {
BlueToggle()
}
}
结果:
只需使用 UIAppearance
API:
UISwitch.appearance().onTintColor = UIColor.blue
根据 UIAppearance
文档,它当然会默认更改 UISwitch
所有实例的外观。
注意:从 Xcode 11 beta 5 开始测试。
这个
showFavoritesOnly ? .red : .blue
前景:
Toggle(isOn: $showGreeting) {
Text("Show Favorites only").foregroundColor(showFavoritesOnly ? .blue : .gray)
}
色调:
uiView.onTintColor = showFavoritesOnly ? UIColor.blue : UIColor.gray
另外自定义颜色:
SwiftUI 3.0
使用色调
引入了一个新的修改器,它也可以改变切换颜色:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.tint(.red)
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.tint(.orange)
SwiftUI 2.0
使用 SwitchToggleStyle
您现在只能在 SwiftUI 2.0 中为打开位置设置色调颜色:
Toggle(isOn: $isToggleOn) {
Text("Red")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.red))
Toggle(isOn: $isToggleOn) {
Text("Orange")
Image(systemName: "paintpalette")
}
.toggleStyle(SwitchToggleStyle(tint: Color.orange))
SwiftUI 1.0
使用 ToggleStyle
我创建了一个新的 ToggleStyle 来更改 Toggle 的三种颜色(打开颜色、关闭颜色和拇指)。
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = Color(UIColor.green)
var offColor = Color(UIColor.systemGray5)
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
Button(action: { configuration.isOn.toggle() } )
{
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
}
}
.font(.title)
.padding(.horizontal)
}
}
使用示例
Toggle("", isOn: $toggleState)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .green,
offColor: .red,
thumbColor: Color(UIColor.systemTeal)))
Toggle("", isOn: $toggleState2)
.toggleStyle(
ColoredToggleStyle(label: "My Colored Toggle",
onColor: .purple))
来自 SwiftUI 书籍
您可以在 init() 中修改所有 UISwitch 对象的全局 onTintColor。
@State var enable_dhcp = true
init()
{
UISwitch.appearance().onTintColor = .red
}
var body: some View
{
Toggle("DHCP", isOn: $enable_dhcp)
}
建立在@mohammad-reza-farahani 的解决方案之上,这是一种完全不妥协的方法,可以通过 SwiftUI 的实现协议获得 UISwitch 的可配置性。
首先将 UISwitch
包裹在 UIViewRepresentable
中,然后根据需要设置颜色:
final class CustomToggleWrapper: UIViewRepresentable {
var isOn: Binding<Bool>
init(isOn: Binding<Bool>) {
self.isOn = isOn
}
func makeUIView(context: Context) -> UISwitch {
UISwitch()
}
func updateUIView(_ uiView: UISwitch, context: Context) {
// On color
uiView.onTintColor = UIColor.blue
// Off color
uiView.tintColor = UIColor.red
uiView.layer.cornerRadius = uiView.frame.height / 2
uiView.backgroundColor = UIColor.red
uiView.isOn = isOn.wrappedValue
// Update bound boolean
uiView.addTarget(self, action: #selector(switchIsChanged(_:)), for: .valueChanged)
}
@objc
func switchIsChanged(_ sender: UISwitch) {
isOn.wrappedValue = sender.isOn
}
}
其次,使用包装的 UISwitch
:
struct CustomToggleStyle: ToggleStyle {
func makeBody(configuration: Self.Configuration) -> some View {
let toggle = CustomToggleWrapper(isOn: configuration.$isOn)
return HStack {
configuration.label
Spacer()
toggle
}
}
}
像往常一样实施 Toggle
,并应用您的 CustomToggleStyle
:
struct TestView: View {
@State private var isOn: Bool = true
var body: some View {
Toggle(
isOn: $isOn
) {
Text("Test: \(String(isOn))")
}.toggleStyle(CustomToggleStyle()).padding()
}
}
因为最初的问题只是关于改变颜色的切换而不是完整的 Toggle
视觉定制,我认为这样的事情会做:
import SwiftUI
struct CustomToggle: UIViewRepresentable {
@Binding var isOn: Bool
func makeCoordinator() -> CustomToggle.Coordinator {
Coordinator(isOn: $isOn)
}
func makeUIView(context: Context) -> UISwitch {
let view = UISwitch()
view.onTintColor = UIColor.red
view.addTarget(context.coordinator, action: #selector(Coordinator.switchIsChanged(_:)), for: .valueChanged)
return view
}
func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
class Coordinator: NSObject {
@Binding private var isOn: Bool
init(isOn: Binding<Bool>) {
_isOn = isOn
}
@objc func switchIsChanged(_ sender: UISwitch) {
_isOn.wrappedValue = sender.isOn
}
}
}
// MARK: - Previews
struct CustomToggle_Previews: PreviewProvider {
static var previews: some View {
ViewWrapper()
}
struct ViewWrapper: View {
@State(initialValue: false) var isOn: Bool
var body: some View {
CustomToggle(isOn: $isOn)
.previewLayout(.fixed(width: 100, height: 100))
}
}
}
Karol Kulesza 和 George Valkov 提供了一个非常易于实施的解决方案。我只是想补充一点,您也可以将下面的代码放在应用程序委托的 didFinishLaunching 方法中。
UISwitch.appearance().onTintColor = .blue
您还可以使用
创建更具体的外观配置appearance(whenContainedInInstancesOf:)
见https://www.hackingwithswift.com/example-code/uikit/what-is-the-uiappearance-proxy
SwiftUI 2.0(Post WWDC-2020)
使用新的 SwiftUI 增强功能,您可以使用 .toggleStyle
修饰符。
// Switch tinting
Toggle(isOn: $order.notifyWhenReady) {
Text("Send notification when ready")
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
请注意,这仅适用于 iOS14/iPadOS14/macOS11 及以上。
我会稍微更改@Mark Moeykens 的回答以避免出现按钮点击动画。更好的解决方案是:
@available(iOS 13.0, *)
struct ColoredToggleStyle: ToggleStyle {
var label = ""
var onColor = UIColor.proacPrimaryBlue.suColor
var offColor = UIColor.systemGray5.suColor
var thumbColor = Color.white
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Text(label)
Spacer()
RoundedRectangle(cornerRadius: 16, style: .circular)
.fill(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 29)
.overlay(
Circle()
.fill(thumbColor)
.shadow(radius: 1, x: 0, y: 1)
.padding(1.5)
.offset(x: configuration.isOn ? 10 : -10))
.animation(Animation.easeInOut(duration: 0.1))
.onTapGesture {
configuration.isOn.toggle()
}
}
.font(.title)
.padding(.horizontal)
}
}
- 最简单的方法是在使用 toggle 之前设置
UISwitch.appearance().onTintColor = UIColor.red
并像下面这样使用 SwiftUI Toggle。
UISwitch.appearance().onTintColor = UIColor.red
...
let toggle = Toggle(isOn: $vm.dataUsePermission, label: {
Text(I18N.permit_data_usage)
.font(SwiftUI.Font.system(size: 16, weight: .regular))
})
if #available(iOS 14.0, *) {
toggle.toggleStyle(
SwitchToggleStyle(tint: Color(UIColor.m.blue500))
)
} else {
toggle.toggleStyle(SwitchToggleStyle())
}
...
- 您也可以在 SwiftUI 中使用相同的 Toggle 界面但名称不同,并更改色调颜色。
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
如果只需要Toggle而不需要Label,那么
TintableUISwitch(isOn: .constant(true))
使用下面的代码。
import SwiftUI
public struct TintableSwitch<Label>: View where Label: View {
@Binding var isOn: Bool
var label: Label
public init(isOn: Binding<Bool>, @ViewBuilder label: () -> Label) {
self._isOn = isOn
self.label = label()
}
public var body: some View {
HStack {
label
Spacer()
TintableUISwitch(isOn: $isOn, onTintColor: .red) // CHANGE HERE
}
}
}
public struct TintableUISwitch: UIViewRepresentable {
@Binding var isOn: Bool
private var onTintColor: UIColor
public init(isOn: Binding<Bool>, onTintColor: UIColor = UIColor.m.blue500) {
self._isOn = isOn
self.onTintColor = onTintColor
}
public func makeUIView(context: Context) -> UISwitch {
let uiSwitch = UISwitch()
uiSwitch.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged(_:)),
for: .valueChanged
)
uiSwitch.onTintColor = onTintColor
uiSwitch.isOn = isOn
return uiSwitch
}
public func updateUIView(_ uiView: UISwitch, context: Context) {
uiView.isOn = isOn
}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public class Coordinator: NSObject {
var tintableSwitch: TintableUISwitch
init(_ tintableSwitch: TintableUISwitch) {
self.tintableSwitch = tintableSwitch
}
@objc
func valueChanged(_ sender: UISwitch) {
tintableSwitch.isOn = sender.isOn
}
}
}
struct TintableSwitch_Previews: PreviewProvider {
static var previews: some View {
VStack {
TintableSwitch(isOn: .constant(true), label: {
Text("Switch")
})
Toggle(isOn: .constant(true), label: {
Text("Switch")
})
}
}
}
struct TintableUISwitch_Previews: PreviewProvider {
static var previews: some View {
TintableUISwitch(isOn: .constant(true))
}
}
您可以使用色调修改器在 IOS 15.0 中更改切换颜色。
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.tint(.red)
IOS15.0及以下,可以使用toggleStyle修饰符改变切换颜色,但以后会贬值
Toggle(isOn: $isToggleOn) {
Text("Toggle")
}.toggleStyle(SwitchToggleStyle(tint: .red))