Jak stworzyć przyciski radiowe i pole wyboru w Swift (iOS)?

85

Tworzę aplikację, która umożliwia przeprowadzanie ankiet. Mój układ jest generowany z pytań opartych na XML.

Muszę utworzyć przyciski opcji (pojedynczy wybór) i pola wyboru (wiele odpowiedzi). Nie znalazłem nic przydatnego dla szybkiego.

Czy ktoś ma pomysł?

Vannian
źródło
5
Musisz utworzyć wiele przycisków z obrazem tła jako obrazem CheckBox lub obrazem przycisku radiowego ... po kliknięciu przycisku zmień obraz na wybrany obraz ... powinien mieć dwa zestawy obrazów, tj. jeden jest wybrany, inny dla niewybranych ..
RJV Kumar
2
Ponieważ Xcode / iOS nie zapewnia pola wyboru / przycisku radiowego, ja też używam tego, co sugeruje RJV.
Bista
tak, próbowałem, to działa.
Vannian
Czy próbowałeś użyć bibliotek Cocoapods dla pól wyboru i przycisków opcji?
Wimukthi Rajapaksha

Odpowiedzi:

40

W przypadku przycisków radiowych i pól wyboru nie ma nic wbudowanego.

Możesz łatwo wdrożyć Checkboxy samodzielnie. Możesz ustawić niezaznaczony obraz dla swojego przycisku dla UIControlStateNormal i checkImage dla swojego UIControlStateSelected. Teraz po dotknięciu przycisk zmieni swój obraz i będzie przełączał się między obrazem zaznaczonym i niezaznaczonym.

Aby użyć przycisków opcji, musisz zachować Arraydla wszystkich przycisków, które mają zachowywać się jak przyciski opcji. Za każdym razem, gdy przycisk jest naciśnięty, należy odznaczyć wszystkie inne przyciski w tablicy.

Dla przycisków radiowych możesz użyć SSRadioButtonsController Możesz stworzyć obiekt kontrolera i dodać do niego tablicę przycisków jak

var radioButtonController = SSRadioButtonsController()
radioButtonController.setButtonsArray([button1!,button2!,button3!])

Główna zasada jest podobna do tego .

Shamas S
źródło
153

Pole wyboru

Możesz stworzyć własną kontrolkę CheckBox rozszerzającą UIButton za pomocą Swift:

import UIKit

class CheckBox: UIButton {
    // Images
    let checkedImage = UIImage(named: "ic_check_box")! as UIImage
    let uncheckedImage = UIImage(named: "ic_check_box_outline_blank")! as UIImage
    
    // Bool property
    var isChecked: Bool = false {
        didSet {
            if isChecked == true {
                self.setImage(checkedImage, for: UIControl.State.normal)
            } else {
                self.setImage(uncheckedImage, for: UIControl.State.normal)
            }
        }
    }
        
    override func awakeFromNib() {
        self.addTarget(self, action:#selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
        self.isChecked = false
    }
        
    @objc func buttonClicked(sender: UIButton) {
        if sender == self {
            isChecked = !isChecked
        }
    }
}

Następnie dodaj go do swoich widoków za pomocą Interface Builder:

wprowadź opis obrazu tutaj

Przyciski radiowe

Przyciski radiowe można rozwiązać w podobny sposób.

Na przykład klasyczny wybór płci Kobieta - Mężczyzna :

wprowadź opis obrazu tutaj

import UIKit

class RadioButton: UIButton {
    var alternateButton:Array<RadioButton>?
    
    override func awakeFromNib() {
        self.layer.cornerRadius = 5
        self.layer.borderWidth = 2.0
        self.layer.masksToBounds = true
    }
    
    func unselectAlternateButtons() {
        if alternateButton != nil {
            self.isSelected = true
            
            for aButton:RadioButton in alternateButton! {
                aButton.isSelected = false
            }
        } else {
            toggleButton()
        }
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        unselectAlternateButtons()
        super.touchesBegan(touches, with: event)
    }
    
    func toggleButton() {
        self.isSelected = !isSelected
    }
    
    override var isSelected: Bool {
        didSet {
            if isSelected {
                self.layer.borderColor = Color.turquoise.cgColor
            } else {
                self.layer.borderColor = Color.grey_99.cgColor
            }
        }
    }
}

Możesz zainicjować swoje przyciski radiowe w ten sposób:

    override func awakeFromNib() {
        self.view.layoutIfNeeded()
        
        womanRadioButton.selected = true
        manRadioButton.selected = false
    }
    
    override func viewDidLoad() {
        womanRadioButton?.alternateButton = [manRadioButton!]
        manRadioButton?.alternateButton = [womanRadioButton!]
    }

Mam nadzieję, że to pomoże.

crubio
źródło
1
dla Swift 3 trzeba zmienić przycisk Metoda kliknięto na przycisk funkcyjny Kliknięto (_ nadawca: UIButton)
hilzj
1
przy pierwszym uruchomieniu nie widzę żadnego obrazu z moim polem wyboru ... pojawia się dopiero po kliknięciu ... proszę mi pomóc
H Raval
2
Cześć @Nitesh, możesz uzyskać status Checked, używając publicznej właściwości bool isChecked, na przykład: if myCheckbox.isChecked {...}
crubio
1
@ Jerland2 Nie mam pojęcia, dlaczego nie widząc żadnego z twojego kodu. Wartość logiczna isChecked jest połączona z obrazem, więc musi być coś innego.
crubio
1
@amorenew, jeśli nadal szukasz sposobu, w jaki można wyświetlać interfejs w kreatorze interfejsów, zapoznaj się z moją odpowiedzią.
Mahendra Liya
22

Sprawdź DLRadioButton . Możesz dodawać i dostosowywać przyciski opcji bezpośrednio z programu Interface Builder. Działa również Swiftdoskonale z .

Swift Radio Button dla iOS

Aktualizacja: wersja 1.3.2dodała kwadratowe przyciski, również poprawiona wydajność.

Aktualizacja: wersja 1.4.4dodała opcję wielokrotnego wyboru, może być również używana jako pole wyboru.

Aktualizacja: wersja 1.4.7dodała obsługę języka RTL.

David Liu
źródło
Nie widzę, jak używać przycisku jako pola wyboru? ma tylko przycisk opcji?
dleviathan
1
Aby go użyć jako pole wyboru, można ustawić multipleSelectionEnabledi iconSquaredo YES.
David Liu
1
Hej @DavidLiu Trudno mi jest zaimplementować twoją bibliotekę do Swift 1.2. Jestem bardzo nowy w aplikacjach na iOS. Czy Twój plik ReadMe.md pomija kilka podstawowych kroków? Jestem bardzo zmieszany!
Milap
1
@milap Czy możesz zgłosić problem i wystawić przykładowy kod?
David Liu
@DavidLiu Mam to do pracy, po prostu potrzebowałem dodać twój framework do wbudowanych plików binarnych. Może chcesz dodać ten krok dla super początkujących!
Milap
11

Swift 5, pole wyboru z animacją

Utwórz wylot przycisku

@IBOutlet weak var checkBoxOutlet:UIButton!{
        didSet{
            checkBoxOutlet.setImage(UIImage(named:"unchecked"), for: .normal)
            checkBoxOutlet.setImage(UIImage(named:"checked"), for: .selected)
        }
    }

Utwórz rozszerzenie UIButton

extension UIButton {
    //MARK:- Animate check mark
    func checkboxAnimation(closure: @escaping () -> Void){
        guard let image = self.imageView else {return}
        
        UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveLinear, animations: {
            image.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
            
        }) { (success) in
            UIView.animate(withDuration: 0.1, delay: 0, options: .curveLinear, animations: {
                self.isSelected = !self.isSelected
                //to-do
                closure()
                image.transform = .identity
            }, completion: nil)
        }
        
    }
}

Jak używać

 @IBAction func checkbox(_ sender: UIButton){
        sender.checkboxAnimation {
            print("I'm done")
            //here you can also track the Checked, UnChecked state with sender.isSelected
            print(sender.isSelected)
            
        }
}

Sprawdź mój przykład dla pola wyboru i przycisku radiowego https://github.com/rashidlatif55/CheckBoxAndRadioButton

Rashid Latif
źródło
2
Myślę, że to zasługuje na więcej głosów. Dzięki za eleganckie rozwiązanie.
Carlo
po wybraniu przycisku pojawia się niebieski kwadrat. Jak mogę to zrobić okólnik
danu
1
@danu, aby było kółkiem, możesz dodać tę linię.Aby didSet checkBoxOutlet.layer.cornerRadius = checkBoxOutlet.frame.height / 2usunąć niebieski, tintgdy przycisk jest wybrany, możesz dodać tę linię jako rozszerzenie self.adjustsImageWhenHighlighted = false self.isHighlighted = false
Rashid Latif
1
@danu Stworzyłem dla Ciebie przykład i podaj link w mojej odpowiedzi. Możesz z tym grać
Rashid Latif
w ten sposób odpowiedź zdecydowanie zasługuje na więcej głosów dzięki @RashidLatif
danu
9

Istnieje naprawdę świetna biblioteka, której możesz do tego użyć (możesz jej użyć zamiast UISwitch): https://github.com/Boris-Em/BEMCheckBox

Konfiguracja jest łatwa:

BEMCheckBox *myCheckBox = [[BEMCheckBox alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[self.view addSubview:myCheckBox];

Zawiera pola wyboru typu koło i kwadrat

wprowadź opis obrazu tutaj

A także animacje:

wprowadź opis obrazu tutaj

Calvin Alvin
źródło
4

Bardzo prosta kontrola pola wyboru.

 @IBAction func btn_box(sender: UIButton) {
    if (btn_box.selected == true)
    {
        btn_box.setBackgroundImage(UIImage(named: "box"), forState: UIControlState.Normal)

            btn_box.selected = false;
    }
    else
    {
        btn_box.setBackgroundImage(UIImage(named: "checkBox"), forState: UIControlState.Normal)

        btn_box.selected = true;
    }
}
Pragnesh Vitthani
źródło
4

krótsza wersja ios swift 4:

@IBAction func checkBoxBtnTapped(_ sender: UIButton) {
        if checkBoxBtn.isSelected {
            checkBoxBtn.setBackgroundImage(#imageLiteral(resourceName: "ic_signup_unchecked"), for: .normal)
        } else {
            checkBoxBtn.setBackgroundImage(#imageLiteral(resourceName: "ic_signup_checked"), for:.normal)
        }
        checkBoxBtn.isSelected = !checkBoxBtn.isSelected
    }
Gulz
źródło
1
ta odpowiedź jest prosta i daje taki sam wynik przy mniejszej ilości kodu! +1
Julian Silvestri
4

Rozwiązanie dla przycisku radiowego w Swift 4.2 bez korzystania z bibliotek innych firm

Utwórz plik RadioButtonController.swift i umieść w nim następujący kod:

import UIKit

class RadioButtonController: NSObject {
    var buttonsArray: [UIButton]! {
        didSet {
            for b in buttonsArray {
                b.setImage(UIImage(named: "radio_off"), for: .normal)
                b.setImage(UIImage(named: "radio_on"), for: .selected)
            }
        }
    }
    var selectedButton: UIButton?
    var defaultButton: UIButton = UIButton() {
        didSet {
            buttonArrayUpdated(buttonSelected: self.defaultButton)
        }
    }

    func buttonArrayUpdated(buttonSelected: UIButton) {
        for b in buttonsArray {
            if b == buttonSelected {
                selectedButton = b
                b.isSelected = true
            } else {
                b.isSelected = false
            }
        }
    }
}

Użyj go jak poniżej w pliku kontrolera widoku:

import UIKit

class CheckoutVC: UIViewController {

    @IBOutlet weak var btnPaytm: UIButton!
    @IBOutlet weak var btnOnline: UIButton!
    @IBOutlet weak var btnCOD: UIButton!

    let radioController: RadioButtonController = RadioButtonController()

    override func viewDidLoad() {
        super.viewDidLoad()

        radioController.buttonsArray = [btnPaytm,btnCOD,btnOnline]
        radioController.defaultButton = btnPaytm
    }

    @IBAction func btnPaytmAction(_ sender: UIButton) {
        radioController.buttonArrayUpdated(buttonSelected: sender)
    }

    @IBAction func btnOnlineAction(_ sender: UIButton) {
        radioController.buttonArrayUpdated(buttonSelected: sender)
    }

    @IBAction func btnCodAction(_ sender: UIButton) {
        radioController.buttonArrayUpdated(buttonSelected: sender)
    }
}

Pamiętaj, aby dodać obrazy radio_off i radio_on w Zasobach.

radio na obrazie radio wyłączone obraz

Wynik:

wynik przycisku opcji

Akhilendra Singh
źródło
1
Schludne rozwiązanie wielokrotnego użytku. Dzięki!
rak appdev
Precyzyjny!!! To była bardziej szczegółowa odpowiedź. Poza RadioButtonControllerinstancją sprawdziłem tag w selectedButtoncelu rozróżnienia, co zostało wybrane. Dzięki!
Randika Vishman
2

Kroki tworzenia przycisku radiowego

BasicStep: weź dwa przyciski. ustaw obraz zarówno dla zaznaczonych, jak i niezaznaczonych. niż dodać akcję do obu przycisków. teraz uruchom kod

1) Utwórz zmienną:

var btnTag    : Int = 0

2) W ViewDidLoad Define:

 btnTag = btnSelected.tag

3) Teraz w wybranej akcji dotknięcia:

 @IBAction func btnSelectedTapped(sender: AnyObject) {
    btnTag = 1
    if btnTag == 1 {
      btnSelected.setImage(UIImage(named: "icon_radioSelected"), forState: .Normal)
      btnUnSelected.setImage(UIImage(named: "icon_radioUnSelected"), forState: .Normal)
     btnTag = 0
    }
}

4) Zrób kod dla przycisku UnCheck

 @IBAction func btnUnSelectedTapped(sender: AnyObject) {
    btnTag = 1
    if btnTag == 1 {
        btnUnSelected.setImage(UIImage(named: "icon_radioSelected"), forState: .Normal)
        btnSelected.setImage(UIImage(named: "icon_radioUnSelected"), forState: .Normal)
        btnTag = 0
    }
}

Przycisk radiowy jest gotowy dla Ciebie

Krutarth Patel
źródło
2

W przypadku pola wyboru nie musisz tworzyć podklasy UIButton. Ma już isSelectedwłaściwość do obsługi tego.

checkbox = UIButton.init(type: .custom)
checkbox.setImage(UIImage.init(named: "iconCheckboxOutlined"), for: .normal)
checkbox.setImage(UIImage.init(named: "iconCheckboxFilled"), for: .selected)
checkbox.addTarget(self, action: #selector(self.toggleCheckboxSelection), for: .touchUpInside)

Następnie w metodzie akcji przełącz jej isSelectedstan.

@objc func toggleCheckboxSelection() {
    checkbox.isSelected = !checkbox.isSelected
}
Salil Dhawan
źródło
1

Możesz po prostu utworzyć podklasę UIButton i napisać własny kod rysowania dostosowany do swoich potrzeb. Zaimplementowałem przycisk opcji, taki jak w Androidzie, używając następującego kodu. Może być również używany w scenorysie. Zobacz przykład w repozytorium Github

import UIKit

@IBDesignable
class SPRadioButton: UIButton {

@IBInspectable
var gap:CGFloat = 8 {
    didSet {
        self.setNeedsDisplay()
    }
}

@IBInspectable
var btnColor: UIColor = UIColor.green{
    didSet{
        self.setNeedsDisplay()
    }
}

@IBInspectable
var isOn: Bool = true{
    didSet{
        self.setNeedsDisplay()
    }
}

override func draw(_ rect: CGRect) {
    self.contentMode = .scaleAspectFill
    drawCircles(rect: rect)
}


//MARK:- Draw inner and outer circles
func drawCircles(rect: CGRect){
    var path = UIBezierPath()
    path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: rect.width, height: rect.height))

    let circleLayer = CAShapeLayer()
    circleLayer.path = path.cgPath
    circleLayer.lineWidth = 3
    circleLayer.strokeColor = btnColor.cgColor
    circleLayer.fillColor = UIColor.white.cgColor
    layer.addSublayer(circleLayer)

    if isOn {
        let innerCircleLayer = CAShapeLayer()
        let rectForInnerCircle = CGRect(x: gap, y: gap, width: rect.width - 2 * gap, height: rect.height - 2 * gap)
        innerCircleLayer.path = UIBezierPath(ovalIn: rectForInnerCircle).cgPath
        innerCircleLayer.fillColor = btnColor.cgColor
        layer.addSublayer(innerCircleLayer)
    }
    self.layer.shouldRasterize =  true
    self.layer.rasterizationScale = UIScreen.main.nativeScale
}

/*
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    isOn = !isOn
    self.setNeedsDisplay()
}
*/    

override func awakeFromNib() {
    super.awakeFromNib()
    addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
    isOn = false
}

@objc func buttonClicked(sender: UIButton) {
    if sender == self {
        isOn = !isOn
        setNeedsDisplay()
    }
}
}
Nikesh Jha
źródło
1

Stworzyłem super prostą klasę do obsługi tego w aplikacji na Maca, nad którą pracuję. Miejmy nadzieję, że to komuś pomoże

Klasa RadioButtonController:

class RadioButtonController: NSObject {

var buttonArray : [NSButton] = []
var currentleySelectedButton : NSButton?
var defaultButton : NSButton = NSButton() {
    didSet {
        buttonArrayUpdated(buttonSelected: self.defaultButton)
    }
}

func buttonArrayUpdated(buttonSelected : NSButton) {
    for button in buttonArray {
        if button == buttonSelected {
            currentleySelectedButton = button
            button.state = .on
        } else {
            button.state = .off
        }
    }
}

}

Implementacja w kontrolerze widoku:

class OnboardingDefaultLaunchConfiguration: NSViewController {

let radioButtonController : RadioButtonController = RadioButtonController()
@IBOutlet weak var firstRadioButton: NSButton!
@IBOutlet weak var secondRadioButton: NSButton!

@IBAction func folderRadioButtonSelected(_ sender: Any) {
    radioButtonController.buttonArrayUpdated(buttonSelected: folderGroupRadioButton)
}

@IBAction func fileListRadioButtonSelected(_ sender: Any) {
    radioButtonController.buttonArrayUpdated(buttonSelected: fileListRadioButton)
}

override func viewDidLoad() {
    super.viewDidLoad()
    radioButtonController.buttonArray = [firstRadioButton, secondRadioButton]
    radioButtonController.defaultButton = firstRadioButton
}

}
got2jam
źródło
0
  1. Utwórz 2 przyciski, jeden jako „TAK”, a drugi jako „NIE”.
  2. Utwórz właściwość BOOL Ex: isNRICitizen = false
  3. Daj to samo połączenie przycisku dla obu przycisków i ustaw tag (np .: przycisk Tak - tag 10 i przycisk Nie - tag 20)
@IBAction func btnAction(_ sender:UIButton) {

isNRICitizen = sender.tag == 10 ? true : false
isNRICitizen ? self.nriCitizenBtnYes.setImage(#imageLiteral(resourceName: "radioChecked"), for: .normal) : self.nriCitizenBtnYes.setImage(#imageLiteral(resourceName: "radioUnchecked"), for: .normal)
        isNRICitizen ? self.nriCitizenBtnNo.setImage(#imageLiteral(resourceName: "radioUnchecked"), for: .normal) : self.nriCitizenBtnNo.setImage(#imageLiteral(resourceName: "radioChecked"), for: .normal)
}
Ravindra Kishan
źródło
0

W przypadku checkboxów istnieje faktycznie wbudowane rozwiązanie w postaci akcesoriów UITableViewCell. Możesz skonfigurować swój formularz jako UITableView, w którym każda komórka jest opcją do wyboru i użyj accessoryTypedo ustawienia znacznika wyboru dla wybranych elementów.

Oto przykład pseudokodu:

    let items = [SelectableItem]

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


        // Get the item for the current row
        let item = self.items[indexPath.row]

        // ...dequeue and set up the `cell` as you wish...

        // Use accessoryType property to mark the row as checked or not...
        cell.accessoryType = item.selected ? .checkmark : .none
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // Unselect row
        tableView.deselectRow(at: indexPath, animated: false)

        // Toggle selection
        let item = self.items[indexPath.row]
        item.selected = !item.selected
        tableView.reloadData()
    }

Jednak przyciski opcji wymagają niestandardowej implementacji, zobacz inne odpowiedzi.

Aron
źródło
0

Decyzja o zaznaczeniu lub odznaczeniu przycisku wyboru jest czymś poza zakresem widoku. Sam widok powinien zajmować się jedynie rysowaniem elementów, nie decydując o tym, jaki jest stan wewnętrzny. Moja sugerowana implementacja jest następująca:

import UIKit

class Checkbox: UIButton {

    let checkedImage = UIImage(named: "checked")
    let uncheckedImage = UIImage(named: "uncheked")
    var action: ((Bool) -> Void)? = nil

    private(set) var isChecked: Bool = false {
        didSet{
            self.setImage(
                self.isChecked ? self.checkedImage : self.uncheckedImage,
                for: .normal
            )
        }
    }

    override func awakeFromNib() {
        self.addTarget(
            self,
            action:#selector(buttonClicked(sender:)),
            for: .touchUpInside
        )
        self.isChecked = false
    }

    @objc func buttonClicked(sender: UIButton) {
        if sender == self {
            self.action?(!self.isChecked)
        }
    }

    func update(checked: Bool) {
        self.isChecked = checked
    }
}

Może być używany z Interface Builder lub programowo. Użycie widoku może wyglądać następująco:

let checkbox_field = Checkbox(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
checkbox_field.action = { [weak checkbox_field] checked in
    // any further checks and business logic could be done here
    checkbox_field?.update(checked: checked)
}
Soheil Novinfard
źródło
0

Swift 5.0 Zaktualizowany prosty RadioButton dla Swift (bez biblioteki)

Najpierw ustaw obrazy jako przycisk jeden zaznaczony, a drugi odznaczony.

Następnie zapewnij 2 gniazda z RadioButton.

@IBOutlet weak var radioMale: UIButton!
@IBOutlet weak var radioFemale: UIButton!

Utwórz IBAction z obiema akcjami przycisku w jednej metodzie.

 @IBAction func btnRadioTapped(_ sender: UIButton) {

    radioMale.setImage(UIImage(named: "Unchecked"), for: .normal)
    radioFemale.setImage(UIImage(named: "Unchecked"), for: .normal)

    if sender.currentImage == UIImage(named: "Unchecked"){

        sender.setImage(UIImage(named: "Checked"), for: .normal)

    }else{

        sender.setImage(UIImage(named: "Unchecked"), for: .normal)
    }

}
Threadripper
źródło
0

Nie mam wystarczającej reputacji, aby komentować, więc zostawię tutaj swoją wersję wersji Salila Dwahan . Działa dla Swift 5, XCode 11.3.

Najpierw umieść przycisk na IB, wybierz typ „Niestandardowy” i utwórz punkt sprzedaży oraz akcję z układem asystenta (Ctrl + Przeciągnij). Uwzględnij następujący kod i powinien kończyć się tak:

class YourViewController: UIViewController {
    @IBOutlet weak var checkbox: UIButton!
    @IBAction func checkboxTapped(_ sender: UIButton) {
        checkbox.isSelected = !checkbox.isSelected
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        checkbox.setImage(UIImage.init(named: "checkMark"), for: .selected)
    }
}

Nie zapomnij dodać obrazu do zasobów i zmienić nazwę, aby pasowała!

checkbox.isSelected to sposób na sprawdzenie

Bosco Domingo
źródło
0

Chociaż niektóre odpowiedzi słusznie wspominają o tym, że możemy użyć Selected State do ustawienia obrazu dla wybranego stanu przycisku, nie będzie to działać elegancko, gdy przycisk musi mieć zarówno obraz, jak i tekst.

Jak wielu, zakończyłem podklasą UIButton; jednak dodano obsługę ustawiania obrazów z Interface Builder.

Poniżej mój kod:

import UIKit

class CustomCheckbox: UIButton {

    @IBInspectable var defaultStateImage: UIImage? = nil {
        didSet{
            self.setNeedsDisplay()
        }
    }

    @IBInspectable var selectedStateImage: UIImage? = nil {
        didSet{
            self.setNeedsDisplay()
        }
    }

    @IBInspectable var gapPadding: CGFloat = 0 {
        didSet{
            self.setNeedsDisplay()
        }
    }

    @IBInspectable var isChecked: Bool = false {
        didSet{
            self.setNeedsDisplay()
        }
    }

    var defaultImageView: UIImageView? = nil
    var selectedImageView: UIImageView? = nil

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }

    func setup() {
        if(defaultStateImage != nil) {
            defaultImageView = UIImageView(image: defaultStateImage)
            defaultImageView?.translatesAutoresizingMaskIntoConstraints = false

            addSubview(defaultImageView!)

            let length = CGFloat(16)
            titleEdgeInsets.left += length

            NSLayoutConstraint.activate([
                defaultImageView!.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: -gapPadding),
                defaultImageView!.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
                defaultImageView!.widthAnchor.constraint(equalToConstant: length),
                defaultImageView!.heightAnchor.constraint(equalToConstant: length)
            ])
        }

        if(selectedStateImage != nil) {
            selectedImageView = UIImageView(image: selectedStateImage)
            selectedImageView!.translatesAutoresizingMaskIntoConstraints = false

            addSubview(selectedImageView!)

            let length = CGFloat(16)
            titleEdgeInsets.left += length

            NSLayoutConstraint.activate([
                selectedImageView!.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: -gapPadding),
                selectedImageView!.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
                selectedImageView!.widthAnchor.constraint(equalToConstant: length),
                selectedImageView!.heightAnchor.constraint(equalToConstant: length)
            ])
        }

        if defaultImageView != nil {
            defaultImageView!.isHidden = isChecked
        }

        if selectedImageView != nil  {
            selectedImageView!.isHidden = !isChecked
        }

        self.addTarget(self, action: #selector(checkChanged(_:)), for: .touchUpInside)
    }

    @objc func checkChanged(_ btn : UIButton){
        self.isChecked = !self.isChecked

        if defaultImageView != nil {
            defaultImageView!.isHidden = isChecked
        }

        if selectedImageView != nil  {
            selectedImageView!.isHidden = !isChecked
        }
    }
}

Mahendra Liya
źródło