如何在 swift (iOS) 中创建单选按钮和复选框?

How to create radio buttons and checkbox in swift (iOS)?

我正在开发一个允许进行调查的应用程序。我的布局是根据基于 XML 的问题生成的。

我需要创建单选按钮(单选)和复选框(多选)。我没有发现任何对 swift 有用的东西。

有人知道吗?

对于单选按钮和复选框,没有内置任何内容。

您可以自己轻松实现复选框。您可以为 UIControlStateNormal 的按钮设置一个 uncheckedImage,为 UIControlStateSelected 设置一个 checkedImage。现在点击,按钮将更改其图像并在选中和未选中图像之间交替。

要使用单选按钮,您必须为所有要用作单选按钮的按钮保留一个 Array。每当按下一个按钮时,您需要取消选中数组中的所有其他按钮。

对于单选按钮,您可以使用 SSRadioButtonsController 您可以创建一个控制器对象并向其添加按钮数组,如

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

主要原理类似于this here

查看 DLRadioButton。您可以直接从 Interface Builder 添加和自定义单选按钮。也可以完美地与 Swift 一起使用。

更新:版本 1.3.2 添加了方形按钮,同时提高了性能。

更新:版本1.4.4添加了多选选项,也可以用作复选框。

更新:版本 1.4.7 添加了 RTL 语言支持。

复选框

您可以创建自己的 CheckBox 控件扩展 UIButton 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
        }
    }
}

然后使用 Interface Builder 将其添加到您的视图中:

单选按钮

单选按钮可以用类似的方法解决。

比如经典的性别选择Woman - Man:

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

您可以像这样初始化单选按钮:

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

希望对您有所帮助。

创建单选按钮的步骤

BasicStep:采用两个按钮。为选中和未选中设置图像。 而不是向两个按钮添加操作。 现在开始代码

1)创建变量:

var btnTag    : Int = 0

2)在ViewDidLoad中定义:

 btnTag = btnSelected.tag

3)现在处于选定的点击操作中:

 @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)为取消选中按钮编写代码

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

单选按钮已为您准备就绪

一个非常简单的复选框控件。

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

有一个非常棒的库可供您使用(实际上您可以使用它代替 UISwitch):https://github.com/Boris-Em/BEMCheckBox

设置简单:

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

提供圆形和方形复选框

它也做动画:

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

您可以简单地将 UIButton 子类化并编写您自己的绘图代码以满足您的需要。我使用以下代码实现了一个类似于 android 的单选按钮。它也可以用在故事板中。See example in Github repo

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

对于复选框,您不需要继承 UIButton。它已经有 isSelected 属性 来处理这个问题。

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)

然后在操作方法中切换它的 isSelected 状态。

@objc func toggleCheckboxSelection() {
    checkbox.isSelected = !checkbox.isSelected
}
  1. 创建 2 个按钮,一个为 "YES",另一个为 "NO"。
  2. 创建一个 BOOL 属性 例如:isNRICitizen = false
  3. 为两个按钮提供相同的按钮连接并设置标签 (例如:是按钮 - 标签 10 和否按钮 - 标签 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)
}

对于复选框,实际上有一个以 UITableViewCell 附件形式存在的内置解决方案。您可以将表单设置为一个 UITableView,其中每个单元格都是可选选项,并使用 accessoryType 为所选项目设置复选标记。

这是一个伪代码示例:

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

然而,单选按钮确实需要自定义实现,请参阅其他答案。

选中或取消选中复选框按钮的决定超出了视图的范围。 View 本身应该只负责绘制元素,而不是决定元素的内部状态。我建议的实现方式如下:

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

它可以与 Interface Builder 一起使用或以编程方式使用。视图的用法可以像下面的例子:

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

Swift 5.0 更新了 Swift 的简单 RadioButton(无库)

首先将图像设置为按钮一个选中,第二个未选中。

然后提供2个RadioButton的Outlet。

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

在一种方法中使用两个按钮操作创建 IBAction。

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

}

Swift 5、带动画的复选框

注意:- 如果您想在 isSelected 时删除 blue 背景,请将 UIButton 类型从 System 更改为 Custom

创建按钮的出口

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

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

使用方法

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

检查我的复选框和单选按钮示例 https://github.com/rashidlatif55/CheckBoxAndRadioButton

我没有足够的声誉来发表评论,所以我会在这里留下我的 版本。适用于 Swift 5,XCode 11.3。

首先将您的按钮放在 IB 上,select 键入 "Custom" 并使用助手布局(Ctrl + 拖动)创建一个出口和一个动作。包括以下代码,它应该像这样结束:

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

不要忘记将图像添加到资产并更改名称以匹配!

checkbox.isSelected是检查

的方法

我制作了一个非常简单的 class 来在我正在处理的 Mac 应用程序中处理这个问题。希望这对某人有帮助

RadioButtonController Class:

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

}

视图控制器中的实现:

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
}

}

Swift4.2中Radio Button不使用第三方库的解决方法

创建 RadioButtonController.swift 文件并在其中放置以下代码:

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

在您的视图控制器文件中如下使用它:

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

请务必在资源中添加 radio_off 和 radio_on 图片。

结果:

虽然有些回答说的对,我们可以使用Selected State为按钮的Selected状态设置图片,但是当按钮必须同时有图片和文字时,它就无法优雅地工作了。

像许多人一样,我以子类化 UIButton 结束;但是,添加了对从 Interface Builder 设置图像的支持。

下面是我的代码:

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

在此线程中找不到简单的 SwiftUI 版本,因此这是一个使用 SF 符号的快速组件。

struct CheckBox: View {

  private let checked = Image("checkmark.square.fill")
  private let unChecked = Image("checkmark.square")
  @State private var isChecked: Bool = false

  var body: some View {
      ZStack {
          isChecked == false ? unChecked : checked
       }.onTapGesture {
          isChecked.toggle()
       }   
   }
}