Cambia il colore dell'interruttore UIS in stato "off"


97

Ho imparato che possiamo cambiare l'aspetto del pulsante UISwitch nel suo stato "on", ma è anche possibile cambiare il colore del pulsante UISwitch nello stato "off"?


hai usato la proprietà tintColor per cambiare colore?
rishi

Risposte:


134

La mia soluzione 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

Risultato:

inserisci qui la descrizione dell'immagine


6
Ehi, @longpham, vorrei solo apportare una piccola modifica al codice del raggio. Nel caso in cui l'altezza cambi, userei: mSwitch.layer.cornerRadius = mSwitch.frame.height / 2 (sono solo paranoico).
Felipe

@Felipe Gringo Nessun problema. Dipende dalla tua interfaccia utente. Lo standard UISwitchè 31pt.
Long Pham

132

Prova a usarlo

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

Tutto grazie a @Barry Wyckoff.


2
QUESTA è la risposta corretta :) setTint cambia anche il colore del "contorno" che "nasconde" visivamente lo sfondo su sfondo bianco.
Lukasz 'Severiaan' Grela

2
Tieni presente che lo sfondo è di forma rettangolare.
Lukasz 'Severiaan' Grela

Il mio interruttore viene ridimensionato con CGAffineTransformMakeScale(0.80, 0.80). E questo non funziona con la visualizzazione in scala. Perché il livello della vista non viene ridimensionato. Come posso farlo funzionare?
aykutt

1
@aykutt per la visualizzazione in scala puoi usare yourSwitch.layer.cornerRadius = yourSwitch.frame.height / 2 / scaleFactor
Hans Terje Bakke

37

È possibile utilizzare la tintColorproprietà sull'interruttore.

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

Nota che questo richiede iOS 5+


3
L'impostazione di tintColor in iOS7 rimuove il "contorno" per me (tinta bianca su sfondo bianco).
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
        }
    }
}

imposta la classe in Identity Inspector

inserisci qui la descrizione dell'immagine

cambia il colore dall'ispettore Attributi

inserisci qui la descrizione dell'immagine

Produzione

inserisci qui la descrizione dell'immagine


Non sta dando un'uscita adeguata in swift 3
Ketan P

@KetanP puoi spiegare il problema in modo più dettagliato?
Afzaal Ahmad

esegue Xcode 11.2.1, funziona quando si esegue l'app, ma non mostra il colore in IB .... comunque, funziona quando viene distribuito sul dispositivo.
zumzum

10

Ecco un bel trucco: puoi semplicemente raggiungere direttamente la sottoview dell'UISwitch che disegna il suo sfondo "spento" e cambiare il suo colore di sfondo. Funziona molto meglio in iOS 13 rispetto a 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

Il modo migliore per gestire il colore di sfondo e le dimensioni di UISwitch

Per ora è il codice 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

In Swift 4+:

off stato:

switch.tintColor = UIColor.blue

on stato:

switch.onTintColor = UIColor.red

2
Questo non funziona su iOS 13+, l'impostazione tintColornon ha effetto.
Eric,

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

4

Swift 4 è il modo più semplice e veloce per ottenerlo in 3 passaggi:

// 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

Se modifichi manualmente la dimensione dell'interruttore (ad esempio, utilizzando il layout automatico), dovrai aggiornare switch.layer.cornerRadiusanche il, ad esempio, sovrascrivendo layoutSubviewse dopo aver chiamato super aggiornando il raggio dell'angolo:

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

cos'è IntegrationSwitch? inoltre, non sembra funzionare in iOS 11. La modifica del colore di sfondo cambia una visualizzazione più ampia intorno allo switch
FlowUI. SimpleUITesting.com

@iOSCalendarViewOnMyProfile scusa, dovrebbe essereswitchControl
Milan Nosáľ

1
@iOSCalendarViewOnMyProfile dovrebbe cambiare il colore di sfondo, non il proiettile stesso .. nell'aspetto di iOS è sempre il colore predefinito ..
Milan Nosá

4

Se hai bisogno di altre opzioni per la tua app, potrebbe essere anche una buona idea implementare il codice di @ LongPham all'interno di una classe personalizzata. Come altri hanno sottolineato, per lo stato "off" dovrai cambiare anche il colore di sfondo, poiché l'impostazione predefinita è trasparente.

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

L'interruttore UIS offTintColorè trasparente, quindi tutto ciò che si trova dietro l'interruttore viene mostrato. Pertanto, invece di mascherare il colore di sfondo, è sufficiente disegnare un'immagine a forma di interruttore dietro lo switch (questa implementazione presuppone che lo switch sia posizionato da autolayout):

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

[Ma guarda ora l' altra mia risposta .]


2

2020 A partire da Xcode 11.3.1 e Swift 5

Ecco il modo più semplice che ho trovato per impostare il colore dello stato off UISwitch con una riga di codice . Scriverlo qui poiché questa pagina è ciò che è emerso per primo quando stavo cercando e le altre risposte non hanno aiutato.

Questo è se volessi impostare lo stato off in rosso e può essere aggiunto alla funzione viewDidLoad ():

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

Nota: ciò che sta effettivamente facendo è impostare il colore di sfondo dell'interruttore. Ciò può influenzare il colore dell'interruttore anche nello stato on (sebbene per me questo non fosse un problema poiché volevo che lo stato on e off fosse dello stesso colore).

Una soluzione per questo:

Collega semplicemente i colori con una dichiarazione "if else" all'interno della tua IBAction. Se l'interruttore è spento, colorare di rosso lo sfondo. Se l'interruttore è attivato, lasciare lo sfondo chiaro in modo che il colore "acceso" scelto venga visualizzato correttamente.

Questo va all'interno dello switch IBAction.

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

Ho riscontrato un comportamento in cui, al ripristino dell'app dallo sfondo, lo sfondo dell'interruttore tornava a essere chiaro. Per ovviare a questo problema ho semplicemente aggiunto nel codice seguente per impostare il colore ogni volta che l'app arriva in primo piano:

 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
}

Sembra più semplice delle altre risposte. Spero che aiuti!


Risposta bella e semplice, grazie mille, +1
mAc

1

Modo più sicuro in Swift 3 senza valori magici di 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)
  }

}

1

XCode 11, Swift 5

Non preferisco usare le subView, perché non sai mai quando Apple cambierà la gerarchia.

quindi uso invece la vista maschera.

funziona 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

inserisci qui la descrizione dell'immagine
inserisci qui la descrizione dell'immagine
Funzionando al 100% con IOS 13.0 e Swift 5.0, entrambi i colori dello stato impostano lo stesso # 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

A partire dalla soluzione di Matt, l' ho aggiunta a un controllo IBDesignable personalizzato. C'è un problema di temporizzazione che didMoveToSuperview()viene chiamato prima che offTintColorsia impostato che doveva essere gestito.

@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

categoria obiettivo c da utilizzare su qualsiasi interruttore UIS nel progetto utilizzando codice o storyboard:

#import <UIKit/UIKit.h>

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

implementazione

#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

alla fine ho usato anche transform e layer.cornerRadius. Ma ho aggiunto la traduzione per essere centrale.

    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
}

E ho usato backgroundColor e tintColor in designer. Spero che sia d'aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.