UIButton Radio button

Моя имплементация радио кнопки на основе 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

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

May 20, 2020