在 SwiftUI 中设置切换颜色
Posted
技术标签:
【中文标题】在 SwiftUI 中设置切换颜色【英文标题】:Set Toggle color in SwiftUI 【发布时间】:2019-10-22 02:19:05 【问题描述】:在关注 Apple 的 tutorial on user input 之后,我实现了切换。目前,它看起来像这样:
这是生成此 UI 的代码:
NavigationView
List
Toggle(isOn: $showFavoritesOnly)
Text("Show Favorites only")
现在,我希望 Toggle
的 on 颜色为蓝色而不是绿色。
我试过了:
Toggle(isOn: $showFavoritesOnly)
Text("Show Favorites only")
.accentColor(.blue)
.foregroundColor(.blue)
.background(Color.blue)
这些都不起作用,我找不到任何其他修饰符,例如tintColor
。
如何更改Toggle
的颜色?
【问题讨论】:
根据文档,.accentColor
似乎应该更改Toggle
的颜色。也许向 Apple 提交错误报告,看看他们怎么说?
developer.apple.com/documentation/swiftui/toggle。它似乎是 SwiftUI 中最接近 UIKit 的 tintColor 的选项,这就是为什么我说它似乎应该改变 Toggle 的颜色。如果您提交有关它的错误报告,那么希望任何回复的人都会确认这是一个错误,或者有另一种 SwiftUI 方法可以做到这一点。
我认为这是一个错误。它应该随着.accentColor
而改变。我提交了 FB6158727,如果 Apple 对此有任何说明,我会通知您。 :)
@CliftonLabrum 你收到 Apple 的消息了吗?
我使用的是 11.3,但我无法让 .accentColor
工作。
【参考方案1】:
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 中为 on 位置设置色调颜色:
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 来更改 Toggle 的三种颜色(on color、off color 和 thumb)。
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 手册
【讨论】:
这是一个非常好的工作,似乎是目前唯一可行的解决方案。谢谢! 你在这里做了足够多的工作,创建一个独特的切换而不是用样式修饰符实例化他们破坏的切换是有意义的。使用会更干净 非常好!!!我想知道为什么他们摆脱了 ios 中的单选按钮。这非常有用。谢谢 @EverUribe,感谢您的提醒。更新了答案。 @MarkMoeykens 绝对出色的工作。不知道ToggleStyleConfiguration
有没有变化,但是在HStack中,不用Text(label),可以直接用configuration.label
。【参考方案2】:
您可以在 IOS 15.0 中使用色调修改器更改切换颜色。
Toggle(isOn: $isToggleOn)
Text("Toggle")
.tint(.red)
IOS 15.0以下,你可以使用toggleStyle修饰符来改变toggle的颜色,但以后会被贬值。
Toggle(isOn: $isToggleOn)
Text("Toggle")
.toggleStyle(SwitchToggleStyle(tint: .red))
【讨论】:
【参考方案3】:-
最简单的方法是在使用切换之前设置
UISwitch.appearance().onTintColor = UIColor.red
,然后使用如下所示的 SwiftUI 切换。
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")
)
如果只需要不带标签的切换,那么
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))
【讨论】:
【参考方案4】:我会稍微更改@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)
【讨论】:
【参考方案5】:SwiftUI 2.0(WWDC-2020 后)
使用新的 SwiftUI 增强功能,您可以使用 .toggleStyle
修饰符。
// Switch tinting
Toggle(isOn: $order.notifyWhenReady)
Text("Send notification when ready")
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
请注意,这只适用于 iOS14/iPadOS14/macOS11 及更高版本。
【讨论】:
【参考方案6】:Karol Kulesza 和 George Valkov 提供了一个非常易于实施的解决方案。我只是想补充一点,您也可以将下面的代码放在应用程序委托的 didFinishLaunching 方法中。
UISwitch.appearance().onTintColor = .blue
您还可以使用
创建更具体的外观配置appearance(whenContainedInInstancesOf:)
见https://www.hackingwithswift.com/example-code/uikit/what-is-the-uiappearance-proxy
【讨论】:
【参考方案7】:由于最初的问题只是关于更改颜色切换而不是完整的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))
【讨论】:
【参考方案8】:在 @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()
【讨论】:
这很好用。您可以在updateUIView
函数中使用 uiView.setOn(isOn.wrappedValue, animated: context.transaction.animation != nil && !context.transaction.disablesAnimations)
为更改设置动画。【参考方案9】:
您可以在 init() 中修改所有 UISwitch 对象的全局 onTintColor。
@State var enable_dhcp = true
init()
UISwitch.appearance().onTintColor = .red
var body: some View
Toggle("DHCP", isOn: $enable_dhcp)
【讨论】:
【参考方案10】:这个https://***.com/a/56480720/5941807(现在是 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
除了自定义颜色:https://***.com/a/57744208/5941807
【讨论】:
【参考方案11】:只需使用UIAppearance
API:
UISwitch.appearance().onTintColor = UIColor.blue
根据UIAppearance
文档,默认情况下它当然会更改UISwitch
的所有实例的外观。
注意:从 Xcode 11 beta 5 开始测试。
【讨论】:
这似乎在 iOS 14/Xcode 12 Beta 3 中被破坏了。 它一开始似乎可以工作,但如果你关闭并重新打开,颜色会重置为默认值【参考方案12】:我还没有找到直接更改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()
结果:
【讨论】:
谢谢。我会等待接受答案,看看是否还有其他答案。 我将如何使用具有相同 initToggle
的自定义切换:init(isOn: Binding<Bool>, label: () -> Label)
?
@LinusGeffarth 要实现这一点,我认为还需要使用UIViewControllerRepresentable
,但不确定。检查这个例子:developer.apple.com/tutorials/swiftui/interfacing-with-uikit这不是你问的,但它是一种处理自定义视图的更复杂的方式!以上是关于在 SwiftUI 中设置切换颜色的主要内容,如果未能解决你的问题,请参考以下文章