Changer la couleur de UISwitch à l'état «off»

97

J'ai appris que nous pouvons changer l'apparence du bouton UISwitch dans son état "on", mais est-il également possible de changer la couleur de l'UISwitch dans l'état "off"?

user198725878
la source
avez-vous utilisé la propriété tintColor pour changer de couleur?
rishi

Réponses:

134

Ma solution avec # 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

Résultat:

entrez la description de l'image ici

Long Pham
la source
6
Hé, @longpham, je voudrais juste faire un petit changement sur le code du rayon. Au cas où la hauteur changerait, j'utiliserais: mSwitch.layer.cornerRadius = mSwitch.frame.height / 2 (je suis juste paranoïaque).
Felipe
@Felipe Gringo Pas de problème. Dépendez de votre interface utilisateur. La norme UISwitchest de 31 pt.
Long Pham
132

Essayez d'utiliser ceci

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

Tout cela grâce à @Barry Wyckoff.

Sourabh Bhardwaj
la source
2
CECI est la bonne réponse :) setTint modifie également la couleur du "contour" qui "cache" visuellement l'arrière-plan sur fond blanc.
Lukasz 'Severiaan' Grela
2
Sachez que l'arrière-plan est de forme rectangulaire.
Lukasz 'Severiaan' Grela
Mon commutateur est redimensionné avec CGAffineTransformMakeScale(0.80, 0.80). Et cela ne fonctionne pas avec une vue à l'échelle. Parce que le calque de la vue n'est pas redimensionné. Comment puis-je faire fonctionner cela?
aykutt
1
@aykutt pour une vue à l'échelle, vous pouvez utiliser yourSwitch.layer.cornerRadius = yourSwitch.frame.height / 2 / scaleFactor
Hans Terje Bakke
37

Vous pouvez utiliser la tintColorpropriété sur le commutateur.

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

Notez que cela nécessite iOS 5+

bengoesboom
la source
3
La définition de tintColor dans iOS7 supprime le «contour» pour moi (teinte blanche sur fond blanc).
Lukasz 'Severiaan' Grela
28

Swift IBDesignable

import UIKit
@IBDesignable

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

définir la classe dans l'inspecteur d'identité

entrez la description de l'image ici

changer la couleur de l'inspecteur d'attributs

entrez la description de l'image ici

Production

entrez la description de l'image ici

Afzaal Ahmad
la source
Il ne donne pas de sortie correcte dans Swift 3
Ketan P
@KetanP pouvez-vous expliquer le problème plus en détail?
Afzaal Ahmad
exécutant Xcode 11.2.1, fonctionne lors de l'exécution de l'application, mais il n'affiche pas la couleur dans IB .... de toute façon, cela fonctionne lorsqu'il est déployé sur l'appareil.
zumzum
10

Voici une très bonne astuce: vous pouvez simplement accéder directement à la sous-vue de UISwitch qui dessine son arrière-plan "off" et changer sa couleur d'arrière-plan. Cela fonctionne beaucoup mieux dans iOS 13 que dans 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
}
mat
la source
6

La meilleure façon de gérer la couleur et la taille d'arrière-plan d'UISwitch

Pour l'instant c'est le code 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
        }
    }
}
Kalpesh jetani
la source
5

Dans Swift 4+:

off Etat:

switch.tintColor = UIColor.blue

on Etat:

switch.onTintColor = UIColor.red
Chryb
la source
2
Cela ne fonctionne pas sur iOS 13+, le réglage tintColorn'a aucun effet.
Eric le
5

Swift 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
    }
}
Ivan
la source
4

Swift 4 est le moyen le plus simple et le plus rapide de l'obtenir en 3 étapes:

// 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 vous modifiez manuellement la taille du commutateur (par exemple, en utilisant la mise en page automatique), vous devrez également le mettre à jour switch.layer.cornerRadius, par exemple en remplaçant layoutSubviewset après avoir appelé la super mise à jour du rayon de coin:

override func layoutSubviews() {
    super.layoutSubviews()
    switchControl.layer.cornerRadius = switchControl.bounds.height / 2
}
Milan Nosáľ
la source
qu'est-ce que integrationSwitch? et aussi, ne semble pas fonctionner dans iOS 11. Changer la couleur d'arrière-plan change une vue plus large autour du commutateur
FlowUI. SimpleUITesting.com
@iOSCalendarViewOnMyProfile désolé, ça devrait êtreswitchControl
Milan Nosáľ
1
@iOSCalendarViewOnMyProfile, il est censé changer la couleur d'arrière-plan, pas la puce elle-même .. dans l'apparence iOS, c'est toujours la couleur par défaut ..
Milan Nosáľ
4

Si vous avez besoin d'autres commutateurs autour de votre application, il peut également être judicieux d'implémenter le code de @ LongPham dans une classe personnalisée. Comme d'autres l'ont souligné, pour l'état "off", vous devrez également changer la couleur d'arrière-plan, car la valeur par défaut est 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
  }
}
Luis Antonio Canettoli
la source
3

Le commutateur UISwitch offTintColorest transparent, donc tout ce qui se trouve derrière le commutateur transparaît. Par conséquent, au lieu de masquer la couleur d'arrière-plan, il suffit de dessiner une image en forme de commutateur derrière le commutateur (cette implémentation suppose que le commutateur est positionné par mise en page automatique):

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),
    ])
}

[Mais voyez maintenant mon autre réponse .]

mat
la source
2

2020 À partir de Xcode 11.3.1 et Swift 5

Voici le moyen le plus simple que j'ai trouvé de définir la couleur hors état de UISwitch avec une ligne de code . Écrire ceci ici puisque cette page est ce qui est apparu en premier lorsque je cherchais et les autres réponses n'ont pas aidé.

C'est si je voulais que l'état désactivé soit rouge et que je puisse être ajouté à la fonction viewDidLoad ():

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

Remarque - ce que cela fait réellement est de définir la couleur d'arrière-plan du commutateur. Cela peut également influencer la couleur de l'interrupteur à l'état activé (bien que pour moi, ce n'était pas un problème car je voulais que l'état marche et arrêt soit de la même couleur).

Une solution pour cela:

Attachez simplement les couleurs avec une déclaration «if else» dans votre IBAction. Si le commutateur est désactivé, coloriez l'arrière-plan en rouge. Si le commutateur est activé, laissez l'arrière-plan clair afin que la couleur «marche» choisie s'affiche correctement.

Cela va à l' intérieur du commutateur IBAction.

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

J'ai trouvé un comportement où, à la reprise de l'application à partir de l'arrière-plan, l'arrière-plan du commutateur redeviendrait clair. Pour remédier à ce problème, j'ai simplement ajouté le code suivant pour définir la couleur à chaque fois que l'application passe au premier plan:

 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
}

Cela semble plus simple que les autres réponses. J'espère que cela pourra aider!

Dave Y
la source
Belle et simple réponse, merci beaucoup, +1
mAc
1

Un moyen plus sûr dans Swift 3 sans valeurs magiques de 16 pt:

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)
  }

}
Timur Bernikovitch
la source
1

XCode 11, Swift 5

Je ne préfère pas utiliser les sous-vues, car on ne sait jamais quand Apple va changer la hiérarchie.

donc j'utilise la vue masque à la place.

cela fonctionne avec 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
        }
    }
andrew54068
la source
1

entrez la description de l'image ici
entrez la description de l'image ici
Fonctionnement 100% IOS 13.0 et Swift 5.0 commutent les deux couleurs d'état définies de la même manière # ios13 #swift # swift5

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

XCode 11, Swift 4.2

En commençant par la solution de Matt, je l'ai ajouté à un contrôle IBDesignable personnalisé. Il y a un problème de synchronisation qui didMoveToSuperview()est appelé avant que ne offTintColorsoit défini qui devait être traité.

@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
    }

}
échappé canadien
la source
0

catégorie objectif c à utiliser sur n'importe quel UISwitch du projet en utilisant du code ou un storyboard:

#import <UIKit/UIKit.h>

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

la mise en oeuvre

#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
Alexey Saechnikov
la source
0

tout ce que j'ai finalement utilisé transform et layer.cornerRadius aussi. Mais j'y ai ajouté une traduction pour être au centre.

    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
}

Et j'ai utilisé backgroundColor et tintColor dans Designer. J'espère que ça aide.

Chen Cohen
la source