UIButton Radio button

May 20, 2020

Моя имплементация радио кнопки на основе UIButton, почему-то этого нет в UIKit, хотя может понадобиться. Мой текущий проект использует Storyboard, так что она работает для встраивания в XIB, но этот класс так же можно встраивать и при программной вёрстке.

RadioButton

import UIKit

protocol RadioButtonDelegate: class {

    func onClick(_ sender: UIButton)

}

/// UIButton subclass for a radio button

final class RadioButton: UIButton {

    

    weak var delegate: RadioButtonDelegate?

    

    var isChecked: Bool = false {

        didSet {

            changeImage()

        }

    }

    

    /// Init programmatically

    override init(frame: CGRect) {

        super.init(frame: frame)

        

        setup()

    }

    

    /// Init programmatically

    required init?(coder aDecoder: NSCoder) {

        super.init(coder: aDecoder)

        setup()

    }

    

    /// Init from NIB

    override func awakeFromNib() {

        super.awakeFromNib()

        setup()

    }

    

    private func setup() {

        self.addTarget(self, action: #selector(onClick), for: UIControl.Event.touchUpInside)

    }

    

    private func changeImage() {

        isChecked ? self.setImage(UIImage(named: "radioOn"), for: .normal) : self.setImage(UIImage(named: "radioOff"), for: .normal)

    }

    

    @objc

    func onClick(sender: UIButton) {

        if sender == self {

            delegate?.onClick(self)

        }

    }

}

Как это работает:

  • isChecked - булевская переменная, отображающая вкл/выкл кнопки
  • init и awakeFromNib нужны для инициализирования либо программным методом, либо с помощью XIB.
  • setup() добавляет Target-Action и вызывает метод onClick при нажатии на кнопку
  • changeImage() меняет свойство кнопки при нажатии, например, картинку кнопки
  • onClick(sender: UIButton)delegate и RadioButtonDelegate необходимы для делегирования, об этом напишу подробнее позже

Картинки с названиями radioOn и radioOff - картинки для вкл/выкл кнопки. При желании картинки можно заменить и рисовать точку внутри кнопки, а можно делать self.setTitle("✅", for: .normal).

Использование:

В контроллере создаём кнопку с нашим классом. Это можно сделать программно, можно в XIB, принципиальной разницы нет.

    @IBOutlet weak var firstRadioButton: RadioButton!

    @IBOutlet weak var secondRadioButton: RadioButton!

    

Контроллер, в котором создаётся кнопка, необходимо расширить до RadioButtonDelegate, а в расширении будет нужный нам функционал. В моём случае необходимо было создать две кнопки, и иметь возможность выбрать только одну из них. При включении одной выключается другая.

extension ViewController: RadioButtonDelegate {

    /// Radio button logic

    func onClick(_ sender: UIButton) {

        guard let currentRadioButton = sender as? RadioButton else { return }

        [firstRadioButton, secondRadioButton].forEach { $0.isChecked = false } // Set all to unchecked first

        currentRadioButton.isChecked = !currentRadioButton.isChecked

    }

}

Этот метод можно кастомизировать, добавив кнопок или изменив логику.

После этого необходимо настроить делегат для кнопки и при необходимости задать одной из кнопок значение ВКЛ

firstRadioButton.delegate = self

secondRadioButton.delegate = self

        

firstRadioButton.isChecked = true

        

Готово! Результат: