I wanted to have a UI button respond only if it is being held for more than a set number of seconds. So I used UILongPressGestureRecognizer as so:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var holdButton: UIButton!
#IBAction func holdButtonPressed(_ sender: Any) {
let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressHappened))
recognizer.minimumPressDuration = 2.0
view.addGestureRecognizer(recognizer)
}
and the handler
#objc func longPressHappened(gestureReconizer: UILongPressGestureRecognizer){
holdButton.backgroundColor = #colorLiteral(red: 0.7254902124, green: 0.4784313738, blue: 0.09803921729, alpha: 1)
DispatchQueue.main.async {
print ("Sucess")
}
}
As you can see, i have used DispatchQueue and tried to change the color of the button but neither are working. Can someone please tell me why?
Update :-
I am confused with implementing the methods given in the answer so i thought i will give my full code again
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var holdButton: UIButton! {
didSet {
let recognizer = UILongPressGestureRecognizer(target: self,action: #selector(longPressHappened))
recognizer.minimumPressDuration = 2.0
holdButton.addGestureRecognizer(recognizer)
}
}
override func viewDidLoad() {
super.viewDidLoad()
holdButton.layer.cornerRadius = 150
holdButton.layer.borderWidth = 1.0
holdButton.layer.borderColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
holdButton.clipsToBounds = true
}
#objc func longPressHappened(gestureReconizer: UILongPressGestureRecognizer){
holdButton.backgroundColor = #colorLiteral(red: 0.7254902124, green: 0.4784313738, blue: 0.09803921729, alpha: 1)
DispatchQueue.main.async {
print ("Sucess")
}
}
}
You just need to create custom button using UIView. Add a long press gesture to that view and upon required time triggered the delegate/Closures.
func addLongPressGesture() {
let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
recognizer.minimumPressDuration = 3.0 // Duration
customButton.addGestureRecognizer(recognizer)
}
#objc
func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) {
if gestureRecognizer.state == .began {
// Perform your functionality here
}
}
You need to add gesture to button instead of view
#IBOutlet weak var holdButton: UIButton! {
didSet {
let recognizer = UILongPressGestureRecognizer(target: self,action: #selector(longPressHappened))
recognizer.minimumPressDuration = 2.0
holdButton.addGestureRecognizer(recognizer)
}
}
Add the gesture in your viewDidLoad instead:
override func viewDidLoad() {
super.viewDidLoad()
//...
let recognizer = UILongPressGestureRecognizer(target: self,action: #selector(longPressHappened))
recognizer.minimumPressDuration = 2.0
holdButton.addGestureRecognizer(recognizer)
}
Whole isue is because, you are using UIBUtton for gesture recogniztion. you have to use Uiview for UILongPressGestureRecognizer .
If you want the animations like UIButton then you have to use manual animations or you can have ready to made code fro internet.
If you need further help, you can ask in comment
So I was looking at other answers and I found that all of them were in Objective-C which was why i posted this question... So because the answers were not working for me i used Swiftify to convert the code from This Question, and after some modification, it worked.
Here is the code snippet
override func viewDidLoad() {
super.viewDidLoad()
//...
let longPress_gr = UILongPressGestureRecognizer(target: self, action: #selector(doAction(_:)))
longPress_gr.minimumPressDuration = 2 // triggers the action after 2 seconds of press
holdButton.addGestureRecognizer(longPress_gr)
}
and then the objective-c function to make sure the code only gets triggered once after the long press happened
#objc func doAction(_ recognizer: UILongPressGestureRecognizer?) {
if recognizer?.state == .began {
print("sucess")
}
}
(However, I cant understand the difference between this answer and some of the answers given above... can someone comment on how it work)
i am using sambag time picker controller and i am set time on button on method of sambag picker but issue is that i have multiple buttons in one view controller so i did this for every but on set time method at the time of set button all buttons title are change what i want is that only change value of button which i pressed
here i will show you what i tried i will show you with my code
import UIKit
class ManageHoursViewController: UIViewController {
var theme: SambagTheme = .light
var result : String!
#IBOutlet weak var btnMonStartTime: UIButton!
#IBOutlet weak var btnMonEndTime: UIButton!
#IBOutlet weak var btnMonOff: UIButton!
#IBOutlet weak var btnMonOn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
round()
let hh2 = (Calendar.current.component(.hour, from: Date()))
let mm2 = (Calendar.current.component(.minute, from: Date()))
print(hh2)
print(mm2)
let dateTime = "\(hh2) : \(mm2)"
btnMonStartTime.setTitle("\(dateTime)", for: .normal)
btnMonEndTime.setTitle("\(dateTime)", for: .normal)
// Do any additional setup after loading the view.
}
func round(){
btnMonOff.isHidden = true
btnMonStartTime.layer.cornerRadius = btnMonStartTime.frame.height / 2
btnMonStartTime.layer.shadowColor = UIColor(red: 154/255, green: 154/255, blue: 154/255, alpha: 1).cgColor
btnMonStartTime.layer.shadowOffset = CGSize(width: 0, height: 1)
btnMonStartTime.layer.shadowOpacity = 1.0
btnMonStartTime.layer.shadowRadius = 2.0
btnMonStartTime.layer.masksToBounds = false
btnMonEndTime.layer.cornerRadius = btnMonEndTime.frame.height / 2
btnMonEndTime.layer.shadowColor = UIColor(red: 154/255, green: 154/255, blue: 154/255, alpha: 1).cgColor
btnMonEndTime.layer.shadowOffset = CGSize(width: 0, height: 1)
btnMonEndTime.layer.shadowOpacity = 1.0
btnMonEndTime.layer.shadowRadius = 2.0
btnMonEndTime.layer.masksToBounds = false
}
#IBAction func btnMonStartTimeTapped(_ sender: UIButton) {
let vc = SambagTimePickerViewController()
vc.theme = theme
vc.delegate = self
present(vc, animated: true, completion: nil)
}
#IBAction func btnMonEndTimeTapped(_ sender: UIButton) {
let vc = SambagTimePickerViewController()
vc.theme = theme
vc.delegate = self
present(vc, animated: true, completion: nil)
}
}
extension ManageHoursViewController : SambagTimePickerViewControllerDelegate {
func sambagTimePickerDidSet(_ viewController: SambagTimePickerViewController, result: SambagTimePickerResult) {
self.result = "\(result)"
print(self.result)
btnMonStartTime.setTitle(self.result, for: .normal)
btnMonEndTime.setTitle(self.result, for: .normal)
viewController.dismiss(animated: true, completion: nil)
}
func sambagTimePickerDidCancel(_ viewController: SambagTimePickerViewController) {
viewController.dismiss(animated: true, completion: nil)
}
}
i want like when monday start time button press then and i select time form picker then only monday start time button value change at now all button value changed
You can create a var to track last clicked one
var lastBu: UIButton!
#IBAction func btnMonStartTimeTapped(_ sender: UIButton) {
let vc = SambagTimePickerViewController()
vc.theme = theme
vc.delegate = self
lastBu = sender
present(vc, animated: true, completion: nil)
}
#IBAction func btnMonEndTimeTapped(_ sender: UIButton) {
let vc = SambagTimePickerViewController()
vc.theme = theme
vc.delegate = self
lastBu = sender
present(vc, animated: true, completion: nil)
}
I am trying to build an app which has object of Items and a controller for it which is called ItemsController. Also on my screen I suppose to have a table view and a button on it. The functionality of button is when it is clicked it needs to check favoriteItems array in ItemsController if it is found in that array remove element, if it isn't found there then append it to array. Operations for remove and append good but unfortunately all cells are not working together. They creates their own favoriteItems array.
To achieve this I tried to make ItemsController to be a singleton. I changed class to a struct and called ItemsController.sharedInstance everywhere. It actually solved a lot of my problem and I believe that the source I learnt this is not wrong about it. But why my tableview cells doesn't use ItemsController's favoriteItems array instead create theirs?
struct ItemsController {
var favoriteItems = [Items]()
static var sharedInstance = ItemsController()
init(){
itemsArray = [...items...]
}
}
class TableViewCell: UITableViewCell {
#IBOutlet weak var favoriteButton: UIButton!
let buttonFirstColor : UIColor = UIColor.clear
let buttonSecondColor : UIColor = UIColor(red: 224/255.0, green: 74/255.0, blue: 94/255.0, alpha: 1.0)
var itemsController = ItemsController.sharedInstance
var itemDedicated = Items(name: "", pic: "", aciklama: "")
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
favoriteButton.layer.cornerRadius = 0.5 * favoriteButton.bounds.size.width
favoriteButton.backgroundColor = UIColor.clear
favoriteButton.layer.borderColor = UIColor(red: 224/255.0, green: 74/255.0, blue: 94/255.0, alpha: 1.0).cgColor
favoriteButton.layer.borderWidth = 4.0
favoriteButton.clipsToBounds = true
setFavoriteButton()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func favoriteButtonClicked(_ sender: Any) {
var favoriteFound = false
for items in itemsController.favoriteItems{
if(items.name == itemDedicated.name){
favoriteFound = true }
}
if(favoriteFound){
itemsController.favoriteItems.remove(at: itemsController.favoriteItems.index(of: itemDedicated)!)
} else {
itemsController.favoriteItems.append(itemDedicated)
}
setFavoriteButton()
}
private func setFavoriteButton(){
var favoriteFound = false
for items in itemsController.favoriteItems{
if(items.name == itemDedicated.name){
favoriteFound = true }
}
if(favoriteFound){
favoriteButton.backgroundColor = buttonSecondColor
favoriteButton.setTitleColor(buttonFirstColor, for: .normal)
} else {
favoriteButton.backgroundColor = buttonFirstColor
favoriteButton.setTitleColor(buttonFirstColor, for: .normal)
}
}
}
I have Code Like this
func showSearchBar(searchBar: UISearchBar, navigationItem: UINavigationItem) {
searchBar.alpha = 0
navigationItem.titleView = searchBar
searchBar.showsCancelButton = true
navigationItem.setRightBarButtonItem(nil, animated: true)
for subView in searchBar.subviews {
for subsubView in subView.subviews {
if let textField = subsubView as? UITextField {
textField.attributedPlaceholder = NSAttributedString(string:NSLocalizedString("Search", comment:""),
attributes:[NSForegroundColorAttributeName: UIColor(red: 25/255, green: 128/255, blue: 214/255, alpha: 1)])
textField.font = UIFont(name:"AvenirNext-Medium", size: 15)
textField.textColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1)
}
}
}
UIView.animateWithDuration(0.5, animations: {
searchBar.alpha = 1
}, completion: { finished in
searchBar.becomeFirstResponder()
})
}
func hideSearchBar(searchBar: UISearchBar, navigationItem: UINavigationItem, navigationController: UINavigationController, leftButton: UIBarButtonItem) {
let button = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Search, target: self, action: "showSearchBar:")
searchBar.alpha = 0
navigationController.navigationBar.topItem?.rightBarButtonItem = button
navigationItem.setLeftBarButtonItem(leftButton, animated: true)
searchBar.clipsToBounds = true
UIView.animateWithDuration(0.3, animations: {
navigationController.navigationBar.alpha = 1
}, completion: { finished in
})
}
I've error after func hideSearchBar called
I don't know but i think error because I call showSearchBar in hideSearchBar but on showSearchBar have 2 parameters but i not send it... how can I send 2 parameters on action
I have try
showSearchBar:
showSearchBar::
but it still error, what should i do?
or i have another error on my code?
To get the name right, you need to translate from a Swift method declaration to the Objective-C name of that method. This translation is simple and follows rules that are completely mechanical, but you will be entering the name as a literal string and it is all too easy to make a typing mistake, so be careful:
The name starts with everything that precedes the left parenthesis in the method name.
If the method takes no parameters, stop. That’s the end of the name.
If the method takes any parameters, add a colon.
If the method takes more than one parameter, add the external names of all parameters except the first parameter, with a colon after each external parameter name.
Observe that this means that if the method takes any parameters, its Objective-C name will end with a colon. Capitalization counts, and the name should contain no spaces or other punctuation except for the colons.
To illustrate, here are three Swift method declarations, with their Objective-C names given as a string in a comment:
func sayHello() -> String // "sayHello"
func say(s:String) // "say:"
func say(s:String, times n:Int) // "say:times:"
It is possible to crash even though your selector name corresponds correctly to a declared method. For example, here’s a small test class that creates an NSTimer and tells it to call a certain method once per second:
class MyClass {
var timer : NSTimer? = nil
func startTimer() {
self.timer = NSTimer.scheduledTimerWithTimeInterval(1,
target: self, selector: "timerFired:",
userInfo: nil, repeats: true)
}
func timerFired(t:NSTimer) {
println("timer fired")
}
}
There’s nothing wrong with that class structurally; it compiles, and can be instantiated when the app runs. But when we call startTimer, we crash. The problem is not that timerFired doesn’t exist, or that "timerFired:" is not its name; the problem is that Cocoa can’t find timerFired. This, in turn, is because our class MyClass is a pure Swift class; therefore it lacks the Objective-C introspection and message-sending machinery that would permit Cocoa to see and call timerFired. Any one of the following solutions will solve the problem:
Declare MyClass as a subclass of NSObject.
Declare MyClass with the #objc attribute.
Declare timerFired with the #obc attribute.
Declare timerFired with the dynamic keyword. (But this would be overkill; you should reserve use of dynamic for situations where it is needed, namely where Objective-C needs the ability to alter the implementation of a class member.)
Here is Documentation.
In your case do this way:
"showSearchBar:navigationItem:"
UPDATE:
Remove General.swift from your project and replace your code in Home.swift with this code:
#IBAction func searchBarAction(sender: AnyObject) {
showSearchBar(self.searchBar, navItem: self.navigationItem)
}
func showSearchBar(searchBar: UISearchBar, navItem navigationItem: UINavigationItem) {
searchBar.alpha = 0
navigationItem.titleView = searchBar
searchBar.showsCancelButton = true
navigationItem.setRightBarButtonItem(nil, animated: true)
for subView in searchBar.subviews {
for subsubView in subView.subviews {
if let textField = subsubView as? UITextField {
textField.attributedPlaceholder = NSAttributedString(string:NSLocalizedString("Search", comment:""),
attributes:[NSForegroundColorAttributeName: UIColor(red: 25/255, green: 128/255, blue: 214/255, alpha: 1)])
textField.font = UIFont(name:"AvenirNext-Medium", size: 15)
textField.textColor = UIColor(red: 65/255, green: 65/255, blue: 65/255, alpha: 1)
}
}
}
UIView.animateWithDuration(0.5, animations: {
searchBar.alpha = 1
}, completion: { finished in
searchBar.becomeFirstResponder()
})
}
//MARK: UISearchBarDelegate
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
hideSearchBar(self.searchBar, navigationItem: self.navigationItem, navigationController: self.navigationController!, leftButton: btnMenu)
}
func hideSearchBar(searchBar: UISearchBar, navigationItem: UINavigationItem, navigationController: UINavigationController, leftButton: UIBarButtonItem) {
let button = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Search, target: self, action: "searchBarAction:") //add your button action here.
searchBar.alpha = 0
navigationItem.rightBarButtonItem = button
navigationItem.setLeftBarButtonItem(leftButton, animated: true)
searchBar.clipsToBounds = true
UIView.animateWithDuration(0.3, animations: {
navigationController.navigationBar.alpha = 1
}, completion: { finished in
})
}
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()
}