According to https://developer.dji.com/api-reference/ios-uilib-api/Widgets/PreFlightStatusWidget.html:
"Tapping on status text will toggle between show and hide DUXPreflightChecklistController."
When I tap on the status text in the widget, the DUXPreflightChecklistController is not shown. Also, if I manually show the DUXPreflightChecklistController, there is a close button in the top right corner of the panel but tapping it does not hide the panel.
What is the proper way to configure this panel?
I'm using DJISDK 4.7.1 and DJIUXSDK 4.7.1 with Swift and iOS 12/xCode 10.0.
To provide a bit more detail, I do not want to use the Default Layout but I am using DUXStatusBarViewController. That is embedded in a UIView across the top of my app. I cannot find any properties for that controller that would allow me to hook it up to my instance of DUXPreflightChecklistController, which is also embedded in a UIView.
For: DUXPreflightChecklistController
I'd just solved that
var preflightChecklistController: DUXPreflightChecklistController!
weak var preFlightTableView: UITableView!
private var compassItemIndex: Int = -1
private var storageItemIndex: Int = -1
override func viewDidLoad() {
super.viewDidLoad()
preflightChecklistController = DUXPreflightChecklistController()
addChild(preflightChecklistController)
}
func renderChecklist() {
if let checklistVC = preflightChecklistController {
for subview in checklistVC.view.subviews {
if subview.isKind(of: UITableView.self) {
if let tableView = subview as? UITableView {
self.view.addSubview(tableView)
preFlightTableView = tableView
}
}
}
guard let checklistManager = checklistVC.checklistManager else { return }
let itemList = checklistManager.preFlightChecklistItems
for (index, item) in itemList.enumerated() {
if let _ = item as? DUXStorageCapacityChecklistItem {
storageItemIndex = index
}
if let _ = item as? DUXCompassChecklistItem {
compassItemIndex = index
}
}
preFlightTableView.reloadData()
checklistManager.startCheckingList()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
renderChecklist()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard preFlightTableView != nil else { return }
if let compassCell = preFlightTableView.cellForRow(at: IndexPath(item: compassItemIndex, section: 0)) {
for view in compassCell.subviews {
if let button = view as? UIButton, button.titleLabel?.text == "Calibrate" {
button.addTarget(self, action: #selector(doActionForSpecifiedBTN(sender:)), for: .touchUpInside)
break
}
}
}
if let storageCell = preFlightTableView.cellForRow(at: IndexPath(item: storageItemIndex, section: 0)) {
for view in storageCell.subviews {
if let button = view as? UIButton, button.titleLabel?.text == "Format" {
button.addTarget(self, action: #selector(doActionForSpecifiedBTN(sender:)), for: .touchUpInside)
break
}
}
}
}
#objc func doActionForSpecifiedBTN(sender: UIButton) {
guard let btnTitle = sender.titleLabel else { return }
switch btnTitle.text {
case "Calibrate":
// your func goes here
case "Format":
// your func goes here
default:
break
}
}
I am having hard time resolving one issue. I have a tableview which has many prototype cells. One of them i am using a tagview to show multiple tags selected. So i have used a XIB file which has a TAGVIEW as subview of it & I have used that XIB in tableview cell. Now when i first load the tableview and scroll down then height of cell is large but when i scroll down and up then it's fits in the size of tags. I have tried below solutions for the but not of them worked.
I tried solutions:
1.cell.layoutIfNeeded before return cell.
2.cell.layoutSubView
3.cell.tagView.layoutIfNeeded
4.cell.setNeedsLayout()
5.Relaod tableview in ViewDidAppear
6.In tagViewCellXIB, in AwakeForNib method added self.layoutIfNeeded.
7.In tagViewCellXIB,Override didMoveToParent() & added self.layoutIfNeeded.
I have given estimated row height already.
Here is tagViewCell Class
class TagViewCell: UITableViewCell {
#IBOutlet weak var tagView: TagListView!
#IBOutlet weak var textfield: UITextField!
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var heightTagViewConstraint : NSLayoutConstraint!
#IBOutlet weak var lblValidation: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.layoutIfNeeded()
// Initialization code
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
self.layoutIfNeeded()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
self.heightTagViewConstraint.constant = 35
}
func configureView(type: TagCellType){
switch type {
case .Language:
setupLanguageCell()
titleLbl.text = "Language"
textfield.isHidden = true
if UserData.userData.language.count == 0{
tagView.isHidden = true
textfield.isHidden = false
textfield.placeholder = "What languages do you speak?"
}
break
default:
textfield.isHidden = true
setupTagCell(tagType: type)
if type == .preferArea {
seUpPreffredAreaValidation()
titleLbl.text = "Preferred areas"
if UserData.userData.preferAreaArr.count == 0{
tagView.isHidden = true
textfield.isHidden = false
textfield.placeholder = "What areas do you prefer?"
}
}
else if type == .City{
titleLbl.text = "Preferred cities"
if UserData.userData.cities.count == 0{
tagView.isHidden = true
textfield.isHidden = false
textfield.placeholder = "What city do you prefer? (Optional)"
}
}else{
titleLbl.text = "Preferred countries"
if UserData.userData.country.count == 0{
tagView.isHidden = true
textfield.isHidden = false
textfield.placeholder = "What country do you prefer? (Optional)"
}
}
break
}
}
/// Set up tag view
func setupLanguageCell() {
tagView.removeAllTags()
tagView.tag = 1003 // For Language field
for (index, elememt) in UserData.userData.language.enumerated() {
print(index)
if let langDict = elememt as? NSDictionary{
tagView.isHidden = false
let languageTagView = tagView.addTag(langDict.value(forKey: "lang_name") as! String)
languageTagView.tagBackgroundColor = UIColor.clear
languageTagView.textColor = .black
languageTagView.textFont = UIFont.systemFont(ofSize: 17)
languageTagView.paddingX = -1
languageTagView.isUserInteractionEnabled = false
let levelTagView = tagView.addTag(Constants.languageLevel[langDict.value(forKey: "level") as! Int])
levelTagView.frame = CGRect(x: levelTagView.frame.origin.x, y: (languageTagView.frame.height / 2) - (levelTagView.frame.height / 2) + 2, width: levelTagView.frame.width, height: levelTagView.frame.height)
levelTagView.layoutIfNeeded()
levelTagView.layoutSubviews()
levelTagView.tagBackgroundColor = UIColor().textOrange()
levelTagView.textFont = UIFont.systemFont(ofSize: 10)
levelTagView.cornerRadius = 6
print("pading yyyyyy \(String(describing: tagView.rowViews.last?.frame.minY))")
if UserData.userData.language.count == index+1{
if (tagView.rowViews.last?.frame.minY)! == 48{
levelTagView.frame.origin.y = (languageTagView.frame.height / 2) - (levelTagView.frame.height / 2) + 8
}
}
}
}
if UserData.userData.language.count == 0 {
self.lblValidation.isHidden = false
} else {
self.lblValidation.isHidden = true
}
}
func seUpPreffredAreaValidation() {
if UserData.userData.preferAreaArr.count == 0 {
self.lblValidation.isHidden = false
} else {
self.lblValidation.isHidden = true
}
}
/// setup cell for country and cities
///
/// - Parameter tagType: type of cell country or cities
func setupTagCell(tagType:TagCellType) {
tagView.removeAllTags()
var tagArray:[String] = []
tagArray = UserData.userData.country
tagView.tag = 1001 // For countries field
if tagType == .City {
tagArray = []
tagArray = UserData.userData.cities
tagView.tag = 1002 // For city field
}
if tagType == .preferArea {
tagArray = []
tagArray = UserData.userData.preferAreaArr
tagView.tag = 1003
}
var tagValue = ""
for (_, elememt) in tagArray.enumerated() {
tagView.isHidden = false
print(elememt)
if elememt.characters.count > 17 {
let index = elememt.index(elememt.startIndex, offsetBy: 16)
tagValue = elememt.substring(to: index) + ".."
}else{
tagValue = elememt
}
let levelTagView = tagView.addTag(tagValue)
levelTagView.tagBackgroundColor = UIColor().textOrange()
tagView.textFont = UIFont.systemFont(ofSize: 17)
levelTagView.cornerRadius = 8
levelTagView.enableRemoveButton = false
levelTagView.paddingX = 6
levelTagView.paddingY = 3
}
}
}
For cellForRowAtIndexPath below code is used
let cell = self.tableView.dequeueReusableCell(withIdentifier: "tagcell", for: indexPath) as! TagViewCell
cell.configureView(type: .City)
cell.tagView.delegate = self
if UserData.userData.cities.count >= 0 && UserData.userData.cities.count <= 3 {
cell.heightTagViewConstraint.constant = 25.7
}
else if let height = cell.tagView.subviews.last?.frame.maxY {
cell.heightTagViewConstraint.constant = height + 10
}
return cell
Override layoutSubviews in your TagViewCell class like bellow
class TagViewCell: UITableViewCell {
override func layoutSubviews() {
//Set the frame of tagView here
//self.tagView.frame =
}
}
and call cell.layoutIfNeeded while returning cell. If you still get any problem please comment. we are here to help you.
I made a custom checkbox with button by creatin a class of UIButton
here is the class
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, forState: .Normal)
} else {
self.setImage(uncheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
func buttonClicked(sender: UIButton) {
if sender == self {
isChecked = !isChecked
}
}
}
I have two checkboxes in the viewcontroller but I dont know how to set checkbox in the ViewController.class to do when checkBox "Si" is checked, the checkbox "No" set unchecked like in the image
Your CheckBox class should adopt a delegation pattern so that it can advise its delegate (which in this case would be your view controller) that its value has changed. Then in your view controller you can update the other checkbox as required:
protocol CheckBoxDelegate {
func checkBoxDidChange(checkbox: CheckBox) -> Void
}
class CheckBox: UIButton {
// Images
let checkedImage = UIImage(named: "ic_check_box")! as UIImage
let uncheckedImage = UIImage(named: "ic_check_box_outline_blank")! as UIImage
weak var delegate: CheckBoxDelegate?
// Bool property
var isChecked: Bool = false {
didSet{
if isChecked == true {
self.setImage(checkedImage, forState: .Normal)
} else {
self.setImage(uncheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
func buttonClicked(sender: UIButton) {
isChecked = !isChecked
self.delegate?.checkBoxDidChange(self)
}
}
Then, in your View Controller:
class ViewController: UIViewController, CheckBoxDelegate {
#IBOutlet var siCheckBox: CheckBox! // Initialise some other way if you aren't using storyboard/nib
#IBOutlet var noCheckBox: CheckBox!
override func viewDidLoad() {
super.viewDidLoad()
self.siCheckBox.delegate = self
self.noCheckBox.delegate = self
}
//Mark: CheckBoxDelegate
func checkBoxDidChange(checkbox: CheckBox) {
if checkbox == self.siCheckBox {
self.noCheckBox.isChecked = !checkbox.isChecked
} else {
self.siCheckBox.isChecked = !checkbox.isChecked
}
}
From a user experience point of view, I would question why you need both checkboxes. A checkbox is an on/off control, so you would normally have text like "Select really great option?" with a single checkbox for yes/no; checked is yes, unchecked is no.
I would move func buttonClicked(sender: UIButton) to the parent ViewController.
let siCheckBox = CheckBox()
let noCheckBox = CheckBox()
func checkBoxToggled(sender: AnyObject) {
noCheckbox.isChecked = !noCheckbox.isChecked
siCheckbox.isChecked = !siCheckbox.isChecked
}
This is only safe if you're sure at least 1 is always checked. Otherwise I would add an if statement to make sure at l is checked.
I am working on a "to do" app in Swift and using Firebase as my backend.
On the first tap on my checkbox to signal that a task has been done, the UI updates and the variable should become true (a checkmark appears) but firebase and the local instance of the bool value for that item do not update to false. After a second tap, the UI continues to have normal functionality (the checkmark disappears); but firebase and the local instance both update to true. Following taps from then on are reversed (True for no checkmark and false for checkmark). When I stop the simulator in Xcode and re-run, the values and UI that load in are correct. It is not until I try and tap on the checkmark that I get the incorrect functionality again. Firebase only updates after the second tap and change in UI. I have include just the code that pertains to the checkbox. I think the problem happens in the doneHit function but I am not quite sure why its happening.
Please help. If there is an easier way to go about this, that would be helpful too.
protocol TaskCellDelegate {
func doneHit(cell : TaskCell)
}
class TaskCell : UITableViewCell {
var delegate : TaskCellDelegate?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var checkBox: CheckBox!
#IBOutlet weak var detailLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
checkBox.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton) {
delegate?.doneHit(self)
}
}
class CheckBox: UIButton {
//images
let checkedImage = UIImage(named: "checkedbox")
let unCheckedImage = UIImage(named: "uncheckedbox")
//bool propety
var isChecked:Bool = false{
didSet {
if isChecked == true{
self.setImage(checkedImage, forState: .Normal)
}
else {
self.setImage(unCheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton) {
if(sender == self){
if isChecked == false {
isChecked = true
} else {
isChecked = false
}
}
}
}
func doneHit(cell:TaskCell) {
if let ip = tableView.indexPathForCell(cell) {
var task = tasks[ip.row]
task.done = cell.checkBox.isChecked
if task.done == true {
task.completedBy = "Completed by: \(self.user)"
cell.label.textColor = UIColor.grayColor()
}
else {
task.completedBy = ""
cell.label.textColor = UIColor.blackColor()
}
let taskNameRef = self.ref.childByAppendingPath("tasks/\(task.title)")
let completedByData = ["completedBy": "\(self.user)"]
let doneData = ["done": task.done]
taskNameRef.updateChildValues(completedByData)
taskNameRef.updateChildValues(doneData)
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TaskCell", forIndexPath: indexPath) as! TaskCell
// Configure the cell...
cell.selectionStyle = .None
let idx = tasks[indexPath.row]
if let label = cell.viewWithTag(1) as? UILabel {
label.text = idx.title
if idx.done == true {
label.textColor = UIColor.grayColor()
} else {
label.textColor = UIColor.blackColor()
}
if let checkBox = cell.viewWithTag(2) as? CheckBox {
checkBox.isChecked = idx.done
}
if let userCompleted = cell.viewWithTag(3) as? UILabel {
if idx.done == true {
userCompleted.text = "Completed By: \(idx.completedBy)"
}
else {
userCompleted.text = ""
}
}
}
print("Task.done is: \(idx.done)")
print("isChecked is:\(cell.checkBox.isChecked)")
cell.delegate = self
return cell
}
Figured it out myself after 3 days. I scrapped the Checkbox class and turned the checkbox into a UIImageView and added a gesture recognizer. The rest was just moving the Checkbox logic into the TaskTVC class under the doneHit method.
I am developing an app that allows to do survey. My layout is generated from XML based questions.
I need to create radio buttons (single choice) and checkboxes (multiple answers). I did not find anything useful for swift.
Does anyone have an idea?
Checkbox
You can create your own CheckBox control extending UIButton with 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
}
}
}
And then add it to your views with Interface Builder:
Radio Buttons
Radio Buttons can be solved in a similar way.
For example, the classic gender selection 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
}
}
}
}
You can init your radio buttons like this:
override func awakeFromNib() {
self.view.layoutIfNeeded()
womanRadioButton.selected = true
manRadioButton.selected = false
}
override func viewDidLoad() {
womanRadioButton?.alternateButton = [manRadioButton!]
manRadioButton?.alternateButton = [womanRadioButton!]
}
For Radio Buttons and CheckBoxes there is nothing that comes built in.
You can implement Checkboxes easily yourself. You can set an uncheckedImage for your button for UIControlStateNormal and a checkedImage for your UIControlStateSelected. Now on tap, the button will change its image and alternate between checked and unchecked image.
To use radio buttons, you have to keep an Array for all the buttons that you want to behave as radio buttons. Whenever a button is pressed, you need to uncheck all other buttons in the array.
For radio buttons you can use SSRadioButtonsController
You can create a controller object and add buttons array to it like
var radioButtonController = SSRadioButtonsController()
radioButtonController.setButtonsArray([button1!,button2!,button3!])
The main principle is something like this here.
Swift 5, Checkbox with animation
NOTE:- if you want to remove the blue background while isSelected change the UIButton type from System to Custom
Check my Example for the Checkbox and Radio button
https://github.com/rashidlatif55/CheckBoxAndRadioButton
Create an outlet for the button
#IBOutlet weak var checkBoxOutlet:UIButton!{
didSet{
checkBoxOutlet.setImage(UIImage(named:"unchecked"), for: .normal)
checkBoxOutlet.setImage(UIImage(named:"checked"), for: .selected)
}
}
Create an extension of 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.85, y: 0.85)
}) { (success) in
UIView.animate(withDuration: 0.1, delay: 0, options: .curveLinear, animations: {
self.isSelected = !self.isSelected
//to-do
closure()
image.transform = .identity
}, completion: nil)
}
}
}
How to use
#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)
}
}
Check out DLRadioButton. You can add and customize radio buttons directly from the Interface Builder. Also works with Swift perfectly.
Update: version 1.3.2 added square buttons, also improved performance.
Update: version 1.4.4 added multiple selection option, can be used as checkbox as well.
Update: version 1.4.7 added RTL language support.
Solution for Radio Button in Swift 4.2 without using third-party libraries
Create RadioButtonController.swift file and place following code in it:
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
}
}
}
}
Use it as below in your view controller file:
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)
}
}
Be sure to add radio_off and radio_on images in Assets.
Result:
There's a really great library out there you can use for this (you can actually use this in place of UISwitch): https://github.com/Boris-Em/BEMCheckBox
Setup is easy:
BEMCheckBox *myCheckBox = [[BEMCheckBox alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[self.view addSubview:myCheckBox];
It provides for circle and square type checkboxes
And it also does animations:
shorter ios swift 4 version:
#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
}
A very simple checkbox control.
#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;
}
}
For a checkbox, you don't need to subclass the UIButton. It already has the isSelected property to handle this.
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)
Then in the action method toggle it's isSelected state.
#objc func toggleCheckboxSelection() {
checkbox.isSelected = !checkbox.isSelected
}
Steps to Create Radio Button
BasicStep : take Two Button. set image for both like selected and unselected.
than add action to both button.
now start code
1)Create variable :
var btnTag : Int = 0
2)In ViewDidLoad Define :
btnTag = btnSelected.tag
3)Now In Selected Tap Action :
#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)Do code for UnCheck Button
#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
}
}
Radio Button is Ready for you
You can simply subclass UIButton and write your own drawing code to suit your needs. I implemented a radio button like that of android using the following code. It can be used in storyboard as well.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()
}
}
}
I made a super simple class to handle this in a Mac application I'm working on. Hopefully, this is helpful to someone
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
}
}
}
}
Implementation in View Controller:
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
}
}
For checkboxes there is actually a built-in solution in the form of UITableViewCell accessories. You can set up your form as a UITableView in which each cell as a selectable option and use accessoryType to set a check mark for selected items.
Here is a pseudo-code example:
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()
}
Radio buttons however do require a custom implementation, see the other answers.
The decision of checking or unchecking the checkbox button is something out of the scope of the view. View itself should only take care of drawing the elements, not deciding about the internal state of that. My suggested implementation is as follows:
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
}
}
It can be used with Interface Builder or programmatically. The usage of the view could be as the following example:
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)
}
I don't have enough reputation to comment, so I'll leave my version of Salil Dwahan's version here. Works for Swift 5, XCode 11.3.
First place your button on IB, select type "Custom" and create an outlet and an action with the Assistant Layout (Ctrl + Drag). Include the following code and it should end like this:
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)
}
}
Don't forget to add the image to Assets and change the name to match!
checkbox.isSelected is the way to check
Though some of the answers mention it rightly that we can use the Selected State to set an image for Selected state of the button, it won't work elegantly when the button has to have both image and text.
Like many, I ended by subclassing UIButton; however, added support for setting images from Interface Builder.
Below is my code:
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
}
}
}
Create 2 buttons one as "YES" and another as "NO".
Create a BOOL property Ex: isNRICitizen = false
Give same button connection to both the buttons and set a tag
(Ex: Yes button - tag 10 and No button -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)
}
Swift 5.0 Updated Simple RadioButton For Swift (No Library)
First set images to button One Checked and Second Unchecked.
Then Provide 2 Outlet Of RadioButton.
#IBOutlet weak var radioMale: UIButton!
#IBOutlet weak var radioFemale: UIButton!
Create IBAction With Both Button Action in One Method.
#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)
}
}
Couldn't find an easy SwiftUI version in this thread so here is a quick component using SF symbols.
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()
}
}
}
If you use Image when change state. Try this:
var nightButtonState: Bool = false {
didSet {
nightButtonState ? autoNightButton.setBackgroundImage(UIImage(named: "unchecked_icon"), for: .normal) : autoNightButton.setBackgroundImage(UIImage(named: "checked_icon"), for: .normal)
}
}
Button action:
#IBAction func autoNightButtonAction(_ sender: UIButton) {
self.nightButtonState.toggle()
}