在 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")
        
    

现在,我希望 Toggleon 颜色为蓝色而不是绿色。 我试过了:

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 &amp;&amp; !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()
    

结果:

【讨论】:

谢谢。我会等待接受答案,看看是否还有其他答案。 我将如何使用具有相同 init Toggle 的自定义切换:init(isOn: Binding&lt;Bool&gt;, label: () -&gt; Label) @LinusGeffarth 要实现这一点,我认为还需要使用UIViewControllerRepresentable,但不确定。检查这个例子:developer.apple.com/tutorials/swiftui/interfacing-with-uikit这不是你问的,但它是一种处理自定义视图的更复杂的方式!

以上是关于在 SwiftUI 中设置切换颜色的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SwiftUI 列表中设置选定的行背景颜色?

在 NavigationView 的 SwiftUI 列表中设置雪佛龙的颜色

如何在swiftUI中的List中设置空列背景颜色?

SwiftUI:为特定视图设置状态栏颜色

如何在 SwiftUI 中设置 addObserver?

在 SwiftUI 的导航栏中设置图像和标题