UIButton (checkbox) in custom UITableView subclass - Swift - ios

I asked a version of this question before but I failed to mention my UIButton checkboxes were in a UITableVlewCell. I was told to subclass this from a UITableViewCell instead of a UIButton. It's a to-do list app so I'm making this in a prototype cell. Currently, I'm able to run the app but not save the state of the checkbox using NSUserDefaults. I've followed a tutorial from YouTube and even downloaded the files along with it and converted them from Objective-C to Swift. When I tried to put all the code in the View Controller without a subclass, I got the error message "..Outlets cannot be connected to repeating content." In the subclass, I'm able to run the app but nothing saves. I'm also unsure of what else to call in my viewDidLoad() method.
My code is as follows:
override func viewDidLoad() {
super.viewDidLoad()
// NSUserDefaults
checked = defaults.boolForKey("boxIsChecked")
}
My subclass:
class TableViewCell: UITableViewCell {
var defaults = NSUserDefaults.standardUserDefaults()
let isSelected = UIImage(named: "Selected")
let unselected = UIImage(named: "Unselected")
#IBOutlet weak var checkBoxButton: UIButton!
#IBAction func checkBox(sender: UIButton) {
if (!checked) {
checked = true
checkBoxButton.setImage(isSelected, forState: UIControlState.Normal)
defaults.setBool(checked, forKey: "boxIsChecked")
}
else if (checked) {
checked = false
checkBoxButton.setImage(unselected, forState: UIControlState.Normal)
defaults.setBool(checked, forKey: "boxIsChecked")
}
}
func checkTheBox() {
if (!checked) {
checkBoxButton.setImage(unselected, forState: UIControlState.Normal)
}
else if (checked) {
checkBoxButton.setImage(isSelected, forState: UIControlState.Normal)
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
NOTE: var checked: Bool = true is a global variable in my first View Controller. I'm unsure if I need to initialize this as var checked: Bool. I have tried and got many error messages. Thank you for your help.
ALSO:: When I add TableViewCell().checkTheBox() to the viewDidLoad() method, I get a EXC_BAD_INSTRUCTION on the statement under else if in the checkTheBox method.

Related

Swift backgroundColor comparison not working

When programmatically adding background color to UIButton to system green sender.backgroundColor = UIColor.systemGreen it sets the backgroundColor to SystemGreen, yet when I check it with this if/else statement
if(sender.backgroundColor == UIColor.systemGreen)
{
doSomething()
}
else
{
otherThing()
}
doSomething() is not called, and code goes to the else statement. Why?
I tried this
Step 1: Set the background of UIButton in viewdidload
#IBOutlet weak var currentBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
currentBtn.backgroundColor = UIColor.systemBrown
}
step 2: compare the two colours with help of cgcolour in the button action, its works fine for me, please check your end too.
#IBAction func checkButton(_ sender: UIButton) {
if let getColour = sender.backgroundColor, getColour.cgColor == UIColor.systemBrown.cgColor{
print("comes here")
}else {
print("not here")
}
}

Getting properties of a UIButton in a UITableViewCell

I am entirely new at Swift, and am building a referral form. Some pages of the form have tickboxes where the user can select what type of support they need. I've created these tickboxes as cells for a UITableView. They have their own view and nib, and are made up of a button and an image for feedback. Here's the code:
class TickboxCell: UITableViewCell {
#IBOutlet weak var supportBox: UIView!
#IBOutlet weak var supportBtn: UIButton!
#IBOutlet weak var checkmark: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func buttonPressed(_ sender: UIButton) {
if supportBtn.isSelected == false {
supportBtn.isSelected = true
checkmark.image = UIImage(named: K.Images.checked)
} else {
supportBtn.isSelected = false
checkmark.image = UIImage(named: K.Images.notChecked)
}
}
}
In the ViewController for the page, I'm trying to create a function whereby, when the user presses the Next button to go to the next page of the form, all the labels of the buttons that have been selected are stored and then passed onto the final page of the referral form, ready to be emailed.
This is where I'm at with the function:
func getTicks() {
var ticks: [String:String?] = [:]
for cell in self.tableView.visibleCells {
if cell.supportBtn.isSelected == true {
if let title = cell.supportBtn.titleLabel?.text {
ticks.updateValue("Yes", forKey: title)
}
}
}
}
Of course, this doesn't work because the visible cells don't have what I'm looking for. They only have properties like baseClass and frame.
In my function for the tableView, there's the line
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! TickboxCell
I know that's where it makes the tableView cells into the Tickbox cells - I set the titleLabels for the cell buttons after that. So can I work this into the above function somehow? Or is there another way to tap into the selected status and titles of my buttons?
This is my first ever question on here, apologies if there's anything wrong with it!

Radio Button Group Swift 3 Xcode 8

I have searched various sources and could not find a clear and simple solution for creating the equivalent of a radio button group in Swift 3 with Xcode 8.3 for an iOS application.
For example if I have 3 buttons in one group and only one should be selected at a time. Currently I am implementing this by changing the state of 2 buttons in the group to not selected when the other one is selected and vice versa.
#IBAction func buttonA(_ sender: Any) {
buttonB.isChecked = false
buttonC.isChecked = false
}
#IBAction func buttonB(_ sender: Any) {
buttonA.isChecked = false
buttonC.isChecked = false
}
#IBAction func buttonC(_ sender: Any) {
buttonA.isChecked = false
buttonB.isChecked = false
}
However I would expect a more efficient way to do this.
Any help on a more efficient solution will be appreciated.
You can connect all your button's IBAction to one single method.
#IBAction func buttonClick(_ sender: UISwitch) { // you're using UISwitch I believe?
}
You should add all the buttons into an array:
// at class level
var buttons: [UISwitch]!
// in viewDidLoad
buttons = [buttonA, buttonB, buttonC]
Then, write the buttonClick method like this:
buttons.forEach { $0.isChecked = false } // uncheck everything
sender.isChecked = true // check the button that is clicked on
Alternatives:
Try using a UITableView. Each row contains one option. When a row is selected, change that row's accessoryType to .checkMark and every other row's to .none.
If you are too lazy, try searching on cocoapods.org and see what other people have made.
Just make a single selector for all three button's touchUpInside event, and set radio_off image for normal state and radio_on image for selected state in your IB, then only you have to connect btnClicked method to all button's touchUpInside event
class ViewController: UIViewController {
#IBOutlet weak var btnFirst:UIButton!
#IBOutlet weak var btnSecond:UIButton!
#IBOutlet weak var btnThird:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnClicked(sender:UIButton){
let buttonArray = [btnFirst,btnSecond,btnThird]
buttonArray.forEach{
$0?.isSelected = false
}
sender.isSelected = true
}
Depending on your UI, you could take multiple approaches.
UITableView - Use a UITableView with a checkmark decorator. If your layout for these radio buttons is fairly traditional, this is the correct paradigm. If the layout is a grid instead of a list, you could use UICollectionView.
You can use the func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) in UITableViewDelegate to capture the selection. You can call indexPathForSelectedRow on the tableView when you want to commit the change to determine which cell was selected.
Apple's tutorial on UITableView can be found at:
https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/CreateATableView.html
Manage a group of UIButtons - You could store an array of references to UIButton objects that are part of your radio button group.
protocol RadioButtonDelegate: class {
func didTapButton(_ button: UIButton)
}
class RadioButtonGroup {
private var buttons: [UIButton] = []
weak var delegate: RadioButtonDelegate?
var selectedButton: UIButton? { return buttons.filter { $0.isSelected }.first }
func addButton(_ button: UIButton) {
buttons.append(button)
}
#objc private func didTapButton(_ button: UIButton) {
button.isSelected = true
deselectButtonsOtherThan(button)
delegate?.didTapButton(button)
}
private func deselectButtonsOtherThan(_ selectedButton: UIButton) {
for button in buttons where button != selectedButton {
button.isSelected = false
}
}
}
class MyView: UIView {
private var radioButtonGroup = RadioButtonGroup()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let button1 = UIButton(type: .custom)
button1.setTitle("Eeeny", for: .normal)
let button2 = UIButton(type: .custom)
button2.setTitle("Meeny", for: .normal)
let button3 = UIButton(type: .custom)
button3.setTitle("Miny", for: .normal)
self.radioButtonGroup.addButton(button1)
self.radioButtonGroup.addButton(button2)
self.radioButtonGroup.addButton(button3)
addSubview(button1)
addSubview(button2)
addSubview(button3)
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var maleLB: UIButton!
#IBOutlet weak var femaleLB: UIButton!
#IBOutlet weak var otherLB: UIButton!
var gender = "Male"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if gender == "Male"{
femaleLB.isSelected = true
}
}
#IBAction func maleBtn(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
femaleLB.isSelected = false
otherLB.isSelected = false
}
else{
sender.isSelected = true
femaleLB.isSelected = false
otherLB.isSelected = false
}
}
#IBAction func femaleBtn(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
maleLB.isSelected = false
otherLB.isSelected = false
}
else{
sender.isSelected = true
maleLB.isSelected = false
otherLB.isSelected = false
}
}
#IBAction func otherBtn(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
maleLB.isSelected = false
femaleLB.isSelected = false
}
else{
sender.isSelected = true
maleLB.isSelected = false
femaleLB.isSelected = false
}
}
}

Need to fix my checkboxes: to change states in one click instead of two clicks. Swift 3, IOS

I have multiple checkboxes that work decently.
The way it works is that there's two images (an image of a checked box OR an image of an unchecked box) that show up or disappear into my button, based on clicking that button.
For some reason when it's the first time I click a checkbox it works perfectly (changes its state to: checked or unchecked - when clicked once), but when i go to try a second, third, or fourth (etc.) checkbox, it requires two clicks to change its state (checked/unchecked).
This is annoying and confusing to the user. Is there any way around this?
Here are my last 3 checkboxes:
/////Checkboxes
#IBOutlet weak var Box49: UIButton!
#IBOutlet weak var Box50: UIButton!
#IBOutlet weak var Box51: UIButton!
var BoxON = UIImage(named: "CheckBox")
var BoxOFF = UIImage(named:"UnCheckBox")
var isBoxClicked: Bool!
override func viewDidLoad() {
super.viewDidLoad()
isBoxClicked = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Box49(_ sender: Any) {
if isBoxClicked == true{
isBoxClicked = false
}else{
isBoxClicked = true
}
if isBoxClicked == true{
Box49.setImage(BoxON, for: UIControlState.normal)
}else{
Box49.setImage(BoxOFF, for: UIControlState.normal)
}
}
#IBAction func Box50(_ sender: Any) {
if isBoxClicked == true{
isBoxClicked = false
}else{
isBoxClicked = true
}
if isBoxClicked == true{
Box50.setImage(BoxON, for: UIControlState.normal)
}else{
Box50.setImage(BoxOFF, for: UIControlState.normal)
}
}
#IBAction func Box51(_ sender: Any) {
if isBoxClicked == true{
isBoxClicked = false
}else{
isBoxClicked = true
}
if isBoxClicked == true{
Box51.setImage(BoxON, for: UIControlState.normal)
}else{
Box51.setImage(BoxOFF, for: UIControlState.normal)
}
}
Thanks, Dan
The issue you are using same instance property isBoxClicked with all button instead of that you need to set the image to all button for both state normal and selected and then in your button action simply changed its selected state.
Also either change your button outlet name or action name because they both are same. So it should be like this.
#IBOutlet var box49: UIButton!
#IBOutlet var box50: UIButton!
#IBOutlet var box51: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
box49.setImage(BoxOFF, for: .normal)
box49.setImage(BoxON, for: .selected)
box50.setImage(BoxOFF, for: .normal)
box50.setImage(BoxON, for: .selected)
box51.setImage(BoxOFF, for: .normal)
box51.setImage(BoxON, for: .selected)
}
And now set your button action this way.
#IBAction func box49Button(_ sender: Button) {
sender.isSelected = !sender.isSelected
}
#IBAction func box50Button(_ sender: Button) {
sender.isSelected = !sender.isSelected
}
#IBAction func box51Button(_ sender: Button) {
sender.isSelected = !sender.isSelected
}
Or you can add single button action and set that action to all three button instead of having three different action for each button like this.
#IBAction func boxButton(_ sender: Button) {
sender.isSelected = !sender.isSelected
}
you are using one instance of isBoxClicked variable for all button. You should create an array of isBoxClicked. by using array you can check further that which checkbox(UIButon) is check an which is unchecked .
var isBoxClickedArray:[int] = {0,0}
First create enum for get index of buttons
enum ButtonIndex{
case Box49
case Box50
}
assign enum value as tag in button
Box49Button.tag = ButtonIndex.Box49.rawValue
Box50Button.tag = ButtonIndex.Box50.rawValue
create one method for get action
#IBAction func actionType(_ sender: Any) {
let button = sender as! UIBarButton
if ( button.tag == ButtonIndex.BOX49.rawValue ){
print("Box49 button pressed")
if ( isBoxClicked[ButtonIndex.BOX49.rawValue] == 0 ){
print("box49 is unchecked")
isBoxClicked[ButtonIndex.BOX49.rawValue] = 1// change it's value to checked
// change image also
}else{
// box is checked
isBoxClicked[ButtonIndex.BOX49.rawValue] = 0
// change value in array and image also
}
}else{
print("Box50 button pressed")
}
}
update
declare array in viewcontroller. and assign tag on button in viewdidload or cellforrowAtindexpath if you are using tableview. for enum declare them like this
import UIKit
enum indexs : Int{
case first
case second
}
class ViewController: UIViewController {

Swift Check , Unchecked Button

I have two modified buttons to be a checkbox with a subclass, the checkbox is siCheckbox and noCheckbox. if siCheckbox is checked the other will be unchecked.
The problem is , if i press siCheckBox again will set noCheckbox checked and siCheckBox unchecked
this is my code
import UIKit
class Paso1: UIViewController, CheckBoxDelegate {
#IBOutlet weak var siCheckBox: CheckBox!
#IBOutlet weak var noCheckBox: CheckBox!
override func viewDidLoad() {
super.viewDidLoad()
self.siCheckBox.delegate = self
self.noCheckBox.delegate = self
}
func checkBoxDidChange(checkbox: CheckBox) {
if checkbox == self.siCheckBox {
self.noCheckBox.isChecked = !checkbox.isChecked
} else {
self.siCheckBox.isChecked = !checkbox.isChecked
}
}
and this is the subclass for the buttons checkbox
protocol CheckBoxDelegate {
func checkBoxDidChange(checkbox: CheckBox) -> Void
}
import UIKit
class CheckBox: UIButton {
// Images
let checkedImage = UIImage(named: "check-greenb")! as UIImage
let uncheckedImage = UIImage(named: "check-baseb")! as UIImage
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: #selector(CheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
func buttonClicked(sender: UIButton) {
isChecked = !isChecked
self.delegate?.checkBoxDidChange(self)
}}
is there any examples or a better way to do this?
If you don't want your button to change its checked property if it is already checked, just add conditional logic in your buttonClicked function.
func buttonClicked(sender: UIButton) {
if !isChecked {
isChecked = !isChecked
self.delegate?.checkBoxDidChange(self)
}
}
I think this is a bit complicate to do this.
What I would do :
#IBOutlet weak var siCheckBox: CheckBox!
#IBOutlet weak var noCheckBox: CheckBox!
override func viewDidLoad() {
super.viewDidLoad()
self.siCheckBox.setImage(checkedImage, forState: .Selected)
self.siCheckBox.setImage(uncheckedImage, forState: .Normal)
self.noCheckBox.setImage(checkedImage, forState: .Selected)
self.noCheckBox.setImage(uncheckedImage, forState: .Normal)
}
#IBAction func checkBoxAction(sender : UIButton) {
siCheckBox.isSelected = false
noCheckBox.isSelected = false
sender.isSelected = true
}
That way you don't even need to subclass your button and no delegate to set. Don't forget to link your two buttons to the checkbox action function ;)

Resources