Cambiar el color de UISwitch en estado "apagado"


97

He aprendido que podemos cambiar la apariencia del botón UISwitch en su estado "encendido", pero ¿también es posible cambiar el color del UISwitch en el estado "apagado"?


¿Ha utilizado la propiedad tintColor para cambiar el color?
rishi

Respuestas:


134

Mi solución con # swift2:

let onColor  = _your_on_state_color
let offColor = _your_off_state_color

let mSwitch = UISwitch(frame: CGRectZero)
mSwitch.on = true

/*For on state*/
mSwitch.onTintColor = onColor

/*For off state*/
mSwitch.tintColor = offColor
mSwitch.layer.cornerRadius = mSwitch.frame.height / 2
mSwitch.backgroundColor = offColor

Resultado:

ingrese la descripción de la imagen aquí


6
Hey, @longpham, solo haría un pequeño cambio en el código del radio. En caso de que la altura alguna vez cambie, usaría: mSwitch.layer.cornerRadius = mSwitch.frame.height / 2 (estoy paranoico).
Felipe

@Felipe Gringo No hay problema. Depende de tu interfaz de usuario. El estándar UISwitches 31pt.
Long Pham

132

Intenta usar esto

yourSwitch.backgroundColor = [UIColor whiteColor];
youSwitch.layer.cornerRadius = 16.0;

Todo gracias a @Barry Wyckoff.


2
ESTA es la respuesta correcta :) setTint también cambia el color del "contorno", lo que "oculta" visualmente el fondo sobre el fondo blanco.
Lukasz 'Severiaan' Grela

2
Tenga en cuenta que el fondo tiene forma rectangular.
Lukasz 'Severiaan' Grela

Mi interruptor cambia de tamaño con CGAffineTransformMakeScale(0.80, 0.80). Y esto no funciona con la vista a escala. Porque la capa de la vista no cambia de tamaño. ¿Cómo puedo hacer que esto funcione?
aykutt

1
@aykutt para la vista escalada puede usar yourSwitch.layer.cornerRadius = yourSwitch.frame.height / 2 / scaleFactor
Hans Terje Bakke

37

Puede utilizar la tintColorpropiedad en el conmutador.

switch.tintColor = [UIColor redColor]; // the "off" color
switch.onTintColor = [UIColor greenColor]; // the "on" color

Tenga en cuenta que esto requiere iOS 5+


3
Configurar tintColor en iOS7 me quita el "contorno" (tinte blanco sobre fondo blanco).
Lukasz 'Severiaan' Grela

28

Rápido IBDesignable

import UIKit
@IBDesignable

class UISwitchCustom: UISwitch {
    @IBInspectable var OffTint: UIColor? {
        didSet {
            self.tintColor = OffTint
            self.layer.cornerRadius = 16
            self.backgroundColor = OffTint
        }
    }
}

establecer clase en el inspector de identidad

ingrese la descripción de la imagen aquí

cambiar el color del inspector de atributos

ingrese la descripción de la imagen aquí

Salida

ingrese la descripción de la imagen aquí


No está dando la salida adecuada en Swift 3
Ketan P

@KetanP ¿puede explicar el problema con más detalle?
Afzaal Ahmad

ejecutando Xcode 11.2.1, funciona cuando se ejecuta la aplicación, pero no muestra el color en IB ... de todos modos, funciona cuando se implementa en el dispositivo.
zumzum

10

Aquí hay un truco bastante bueno: puede acceder directamente a la subvista del UISwitch que dibuja su fondo "apagado" y cambiar su color de fondo. Esto funciona mucho mejor en iOS 13 que en iOS 12:

if #available(iOS 13.0, *) {
    self.sw.subviews[0].subviews[0].backgroundColor = .green
} else if #available(iOS 12.0, *) {
    self.sw.subviews[0].subviews[0].subviews[0].backgroundColor = .green
}

6

La mejor manera de administrar el color y el tamaño de fondo de UISwitch

Por ahora es el código Swift 2.3

import Foundation
import UIKit

@IBDesignable
class UICustomSwitch : UISwitch {

    @IBInspectable var OnColor : UIColor! = UIColor.blueColor()
    @IBInspectable var OffColor : UIColor! = UIColor.grayColor()
    @IBInspectable var Scale : CGFloat! = 1.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setUpCustomUserInterface()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setUpCustomUserInterface()
    }


    func setUpCustomUserInterface() {

        //clip the background color
        self.layer.cornerRadius = 16
        self.layer.masksToBounds = true

        //Scale down to make it smaller in look
        self.transform = CGAffineTransformMakeScale(self.Scale, self.Scale);

        //add target to get user interation to update user-interface accordingly
        self.addTarget(self, action: #selector(UICustomSwitch.updateUI), forControlEvents: UIControlEvents.ValueChanged)

        //set onTintColor : is necessary to make it colored
        self.onTintColor = self.OnColor

        //setup to initial state
        self.updateUI()
    }

    //to track programatic update
    override func setOn(on: Bool, animated: Bool) {
        super.setOn(on, animated: true)
        updateUI()
    }

    //Update user-interface according to on/off state
    func updateUI() {
        if self.on == true {
            self.backgroundColor = self.OnColor
        }
        else {
            self.backgroundColor = self.OffColor
        }
    }
}

5

En Swift 4+:

off estado:

switch.tintColor = UIColor.blue

on estado:

switch.onTintColor = UIColor.red

2
Esto no funciona en iOS 13+, la configuración tintColorno tiene ningún efecto.
Eric

5

Rápido 5:

import UIKit

extension UISwitch {    

    func set(offTint color: UIColor ) {
        let minSide = min(bounds.size.height, bounds.size.width)
        layer.cornerRadius = minSide / 2
        backgroundColor = color
        tintColor = color
    }
}

4

Swift 4 forma más fácil y rápida de conseguirlo en 3 pasos:

// background color is the color of the background of the switch
switchControl.backgroundColor = UIColor.white.withAlphaComponent(0.9)

// tint color is the color of the border when the switch is off, use
// clear if you want it the same as the background, or different otherwise
switchControl.tintColor = UIColor.clear

// and make sure that the background color will stay in border of the switch
switchControl.layer.cornerRadius = switchControl.bounds.height / 2

Si cambia manualmente el tamaño del interruptor (por ejemplo, usando el diseño automático), también tendrá que actualizarlo switch.layer.cornerRadius, por ejemplo, anulando layoutSubviewsy después de llamar a super actualizando el radio de la esquina:

override func layoutSubviews() {
    super.layoutSubviews()
    switchControl.layer.cornerRadius = switchControl.bounds.height / 2
}

¿Qué es integrationSwitch? y además, no parece funcionar en iOS 11. Al cambiar el color de fondo, cambia una vista más grande alrededor del interruptor
FlowUI. SimpleUITesting.com

@iOSCalendarViewOnMyProfile lo siento, debería serswitchControl
Milan Nosáľ

1
@iOSCalendarViewOnMyProfile se supone que debe cambiar el color de fondo, no la viñeta en sí ... en la apariencia de iOS, ese es siempre el color predeterminado ..
Milan Nosáľ

4

Si necesita otros conmutadores en su aplicación, también podría ser una buena idea implementar el código de @ LongPham dentro de una clase personalizada. Como han señalado otros, para el estado "desactivado", también deberá cambiar el color de fondo, ya que el valor predeterminado es transparente.

class MySwitch: UISwitch {

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    // Setting "on" state colour
    self.onTintColor        = UIColor.green

    // Setting "off" state colour
    self.tintColor          = UIColor.red
    self.layer.cornerRadius = self.frame.height / 2
    self.backgroundColor    = UIColor.red
  }
}

3

El UISwitch offTintColores transparente, por lo que lo que está detrás del interruptor se muestra. Por lo tanto, en lugar de enmascarar el color de fondo, basta con dibujar una imagen en forma de interruptor detrás del interruptor (esta implementación asume que el interruptor está posicionado por diseño automático):

func putColor(_ color: UIColor, behindSwitch sw: UISwitch) {
    guard sw.superview != nil else {return}
    let onswitch = UISwitch()
    onswitch.isOn = true
    let r = UIGraphicsImageRenderer(bounds:sw.bounds)
    let im = r.image { ctx in
        onswitch.layer.render(in: ctx.cgContext)
        }.withRenderingMode(.alwaysTemplate)
    let iv = UIImageView(image:im)
    iv.tintColor = color
    sw.superview!.insertSubview(iv, belowSubview: sw)
    iv.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        iv.topAnchor.constraint(equalTo: sw.topAnchor),
        iv.bottomAnchor.constraint(equalTo: sw.bottomAnchor),
        iv.leadingAnchor.constraint(equalTo: sw.leadingAnchor),
        iv.trailingAnchor.constraint(equalTo: sw.trailingAnchor),
    ])
}

[Pero mira ahora mi otra respuesta .]


2

2020 a partir de Xcode 11.3.1 y Swift 5

Esta es la forma más sencilla que he encontrado de configurar el color de estado desactivado de UISwitch con una línea de código . Escribir esto aquí desde esta página es lo que apareció primero cuando estaba mirando y las otras respuestas no ayudaron.

Esto es si quisiera establecer el estado de apagado en rojo y se puede agregar a la función viewDidLoad ():

yourSwitchName.subviews[0].subviews[0].backgroundColor = UIColor.red

Nota: lo que realmente está haciendo es configurar el color de fondo del interruptor. Esto también puede influir en el color del interruptor en el estado de encendido (aunque para mí esto no fue un problema, ya que quería que el estado de encendido y apagado fuera del mismo color).

Una solución para esto:

Simplemente une los colores con una declaración "si más" dentro de tu IBAction. Si el interruptor está apagado, colorea el fondo de rojo. Si el interruptor está activado, deje el fondo despejado para que el color "activado" elegido se muestre correctamente.

Esto va dentro del interruptor IBAction.

  if yourSwitch.isOn == false {
           yourSwitch.subviews[0].subviews[0].backgroundColor = UIColor.red
    } else {
        yourSwitch.subviews[0].subviews[0].backgroundColor = UIColor.clear
    }

Encontré un comportamiento en el que, cuando la aplicación se reanudaba desde el fondo, el fondo del interruptor volvía a borrarse. Para solucionar este problema, simplemente agregué el siguiente código para establecer el color cada vez que la aplicación pasa a primer plano:

 override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    NotificationCenter.default.addObserver(
      self,
      selector: #selector(applicationWillEnterForeground(_:)),
      name: UIApplication.willEnterForegroundNotification,
      object: nil)
}

@objc func applicationWillEnterForeground(_ notification: NSNotification) {
   yourSwitch.subviews[0].subviews[0].backgroundColor = UIColor.red
   yourSwitch.subviews[0].subviews[0].backgroundColor = UIColor.red
}

Parece más simple que las otras respuestas. ¡Espero que ayude!


Hermosa y simple respuesta, muchas gracias, +1
mAc

1

Una forma más segura en Swift 3 sin valores mágicos de 16 puntos:

class ColoredBackgroundSwitch: UISwitch {

  var offTintColor: UIColor {
    get {
      return backgroundColor ?? UIColor.clear
    }
    set {
      backgroundColor = newValue
    }
  }

  override func layoutSubviews() {
    super.layoutSubviews()
    let minSide = min(frame.size.height, frame.size.width)
    layer.cornerRadius = ceil(minSide / 2)
  }

}

1

XCode 11, Swift 5

No prefiero usar subViews, porque nunca se sabe cuándo Apple cambiará la jerarquía.

así que uso la vista de máscara en su lugar.

funciona con iOS 12, iOS 13

    private lazy var settingSwitch: UISwitch = {
        let swt: UISwitch = UISwitch()
        // set border color when isOn is false
        swt.tintColor = .cloudyBlueTwo
        // set border color when isOn is true
        swt.onTintColor = .greenishTeal

        // set background color when isOn is false
        swt.backgroundColor = .cloudyBlueTwo

        // create a mask view to clip background over the size you expected.
        let maskView = UIView(frame: swt.frame)
        maskView.backgroundColor = .red
        maskView.layer.cornerRadius = swt.frame.height / 2
        maskView.clipsToBounds = true
        swt.mask = maskView

        // set the scale to your expectation, here is around height: 34, width: 21.
        let scale: CGFloat = 2 / 3
        swt.transform = CGAffineTransform(scaleX: scale, y: scale)
        swt.addTarget(self, action: #selector(switchOnChange(_:)), for: .valueChanged)
        return swt
    }()

    @objc
    func switchOnChange(_ sender: UISwitch) {
        if sender.isOn {
            // set background color when isOn is true
            sender.backgroundColor = .greenishTeal
        } else {
            // set background color when isOn is false
            sender.backgroundColor = .cloudyBlueTwo
        }
    }

1

ingrese la descripción de la imagen aquí
ingrese la descripción de la imagen aquí
Trabajando 100% IOS 13.0 y Swift 5.0 cambian ambos colores de estado establecidos igual # ios13 #swift # swift5

@IBOutlet weak var switchProfile: UISwitch!{
    didSet{
        switchProfile.onTintColor = .red
        switchProfile.tintColor = .red
        switchProfile.subviews[0].subviews[0].backgroundColor = .red
    }
}

0

XCode 11, Swift 4.2

Comenzando con la solución de Matt, la agregué a un control IBDesignable personalizado. Hay un problema de tiempo en el que didMoveToSuperview()se llama antes de que offTintColorse establezca el que debe manejarse.

@IBDesignable public class UISwitchCustom: UISwitch {

    var switchMask: UIImageView?
    private var observers = [NSKeyValueObservation]()

    @IBInspectable dynamic var offTintColor : UIColor! = UIColor.gray {
        didSet {
             switchMask?.tintColor = offTintColor
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initializeObservers()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initializeObservers()
    }

    private func initializeObservers() {
        observers.append(observe(\.isHidden, options: [.initial]) {(model, change) in
            self.switchMask?.isHidden = self.isHidden
        })
    }

    override public func didMoveToSuperview() {
        addOffColorMask(offTintColor)
        super.didMoveToSuperview()
    }

   private func addOffColorMask(_ color: UIColor) {
        guard self.superview != nil else {return}
        let onswitch = UISwitch()
        onswitch.isOn = true
        let r = UIGraphicsImageRenderer(bounds:self.bounds)
        let im = r.image { ctx in
            onswitch.layer.render(in: ctx.cgContext)
            }.withRenderingMode(.alwaysTemplate)
        let iv = UIImageView(image:im)
        iv.tintColor = color
        self.superview!.insertSubview(iv, belowSubview: self)
        iv.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            iv.topAnchor.constraint(equalTo: self.topAnchor),
            iv.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            iv.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            iv.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            ])
        switchMask = iv
        switchMask?.isHidden = self.isHidden
    }

}

0

categoría objetivo c para usar en cualquier UISwitch en el proyecto usando código o guión gráfico:

#import <UIKit/UIKit.h>

@interface UISwitch (SAHelper)
@property (nonatomic) IBInspectable UIColor *offTint;
@end

implementación

#import "UISwitch+SAHelper.h"

@implementation UISwitch (SAHelper)
@dynamic offTint;
- (void)setOffTint:(UIColor *)offTint {
    self.tintColor = offTint;   //comment this line to hide border in off state
    self.layer.cornerRadius = 16;
    self.backgroundColor = offTint;
}
@end

0

all finalmente usé transform y layer.cornerRadius también. Pero le agregué traducción para que sea el centro.

    private func setSwitchSize() {
    let iosSwitchSize = switchBlockAction.bounds.size
    let requiredSwitchSize = ...
    let transform = CGAffineTransform(a: requiredSwitchSize.width / iosSwitchSize.width, b: 0,
                                      c: 0, d:  requiredSwitchSize.height / iosSwitchSize.height,
                                      tx: (requiredSwitchSize.width - iosSwitchSize.width) / 2.0,
                                      ty: (requiredSwitchSize.height - iosSwitchSize.height) / 2.0)

    switchBlockAction.layer.cornerRadius = iosSwitchSize.height / 2.0
    switchBlockAction.transform = transform
}

Y usé backgroundColor y tintColor en el diseñador. Espero eso ayude.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.