I have the present code to allow selection of the days of the week for a repeating event. At present they are independent, and the duplication is high.
How can the buttons be refactored to reduce duplication?
// Array of days repeating:
var weekDayRepeat = [false,false,false,false,false,false,false]
var savedEventId : String = ""
#IBOutlet weak var datePickerStart: UIDatePicker!
#IBOutlet weak var datePickerEnd: UIDatePicker!
#IBOutlet weak var repeatSwitch: UISwitch!
#IBOutlet weak var monSelect: UIButton!
#IBOutlet weak var tuesSelect: UIButton!
#IBOutlet weak var wedsSelect: UIButton!
#IBOutlet weak var thursSelect: UIButton!
#IBOutlet weak var friSelect: UIButton!
#IBOutlet weak var satSelect: UIButton!
#IBOutlet weak var sunSelect: UIButton!
#IBOutlet weak var repeatingLabel: UILabel!
#IBAction func monSelect(sender: AnyObject) {
if(weekDayRepeat[0]) {
monSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[0] = false
} else {
monSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[0] = true
}
}
#IBAction func tuesSelect(sender: AnyObject) {
if(weekDayRepeat[1]) {
tuesSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[1] = false
} else {
tuesSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[1] = true
}
}
#IBAction func wedsSelect(sender: AnyObject) {
if(weekDayRepeat[2]) {
wedsSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[2] = false
} else {
wedsSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[2] = true
}
}
#IBAction func thursSelect(sender: AnyObject) {
if(weekDayRepeat[3]) {
thursSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[3] = false
} else {
thursSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[3] = true
}
}
#IBAction func friSelect(sender: AnyObject) {
if(weekDayRepeat[4]) {
friSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[4] = false
} else {
friSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[4] = true
}
}
#IBAction func satSelect(sender: AnyObject) {
if(weekDayRepeat[5]) {
satSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[5] = false
} else {
satSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[5] = true
}
}
#IBAction func sunSelect(sender: AnyObject) {
if(weekDayRepeat[6]) {
sunSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
weekDayRepeat[6] = false
} else {
sunSelect.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
weekDayRepeat[6] = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Set initial conditions of the page...:
repeatSwitch.on = true
datePickerStart.datePickerMode = UIDatePickerMode.Time
datePickerEnd.datePickerMode = UIDatePickerMode.Time
monSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
tuesSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
wedsSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
thursSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
friSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
satSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
sunSelect.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)
// Allow dynamically changing the mode given if repeating or not.
// If switch to repeating, then show selecting only the time and list of days to select/multi-select
repeatSwitch.addTarget(self, action: #selector(ViewController.switchChanged(_:)), forControlEvents: UIControlEvents.ValueChanged)
}
try the following code :
#IBOutlet var weekDayBtns: [UIButton]!
#IBAction func weekDayBtnAction(sender: UIButton) {
weekDayRepeat[sender.tag] = !weekDayRepeat[sender.tag]
sender.setTitleColor(weekDayRepeat[sender.tag] ? UIColor.blackColor():UIColor.lightGrayColor() , forState: UIControlState.Normal)
}
override func viewDidLoad() {
super.viewDidLoad()
weekDayBtns.forEach({$0.setTitleColor(UIColor.lightGrayColor(), forState: UIControlState.Normal)})
}
create an IBOutlet Collection: #IBOutlet var weekDayBtns: [UIButton]!
create an general IBAction :#IBAction func weekDayBtnAction(sender: UIButton){}
connect the btns all to IBOutlet Collection and IBAction
set the btn tag
here are screenshots:
#Sauron: One possible solution would be the following...
if each button is given a number as its title, you could replicate the technique used to make a simple calculator, where by...
the first button is used to create IBAction / func (kept as uibutton)
Subsequent buttons are then Control-dragged to the "ViewController" in the hierarchy in 'document outline' for the storyboard
When the popup shows. Go to the middle, under "sent events" which should have the name of the first button function you created
Do the same for all the other buttons
This helps to reduce duplication of the code writing. The buttons can then later be identified by their Title. E.g.
#IBAction func numberTapped(sender: UIButton) {
let nameOfDay = sender.currentTitle
}
Thus allowing you to utilise the weekday elsewhere within the code.
Found a youtube link that may help you quickly re: the duplication...?
https://www.youtube.com/watch?v=NJHsdjH2HdY
Related
I'm creating a test that has 5 buttons, each button corresponds to a specific color, the problem is that when I select a consecutive 2nd button, the previous button is still selected, how can I make my code select only one button at a time and deselect the previous one?
How can I fix this?
This is my code
class ViewController: UIViewController {
var buttonPressed: Bool = false
#IBOutlet weak var button1: UIButton!
#IBAction func buttonAction1(_ sender: UIButton) {
if buttonPressed {
buttonPressed = false
button1.setImage(UIImage(named: "Bolinha-5"), for: .normal)
}
else {
buttonPressed = true
button1.setImage(UIImage(named: "Bolinha-4"), for: .normal)
}
}
#IBOutlet weak var button2: UIButton!
#IBAction func buttonAction2(_ sender: UIButton) {
if buttonPressed {
buttonPressed = false
button2.setImage(UIImage(named: "Bolinha-5"), for: .normal)
}
else {
buttonPressed = true
button2.setImage(UIImage(named: "Bolinha-4"), for: .normal)
}
}
#IBOutlet weak var button3: UIButton!
#IBAction func buttonAction3(_ sender: UIButton) {
if buttonPressed {
buttonPressed = false
button3.setImage(UIImage(named: "Bolinha-2"), for: .normal)
}
else {
buttonPressed = true
button3.setImage(UIImage(named: "Bolinha-4"), for: .normal)
}
}
#IBOutlet weak var button4: UIButton!
#IBAction func buttonAction4(_ sender: UIButton) {
if buttonPressed {
buttonPressed = false
button4.setImage(UIImage(named: "Bolinha-3"), for: .normal)
}
else {
buttonPressed = true
button4.setImage(UIImage(named: "Bolinha-4"), for: .normal)
}
}
#IBOutlet weak var button5: UIButton!
#IBAction func buttonAction5(_ sender: UIButton) {
if buttonPressed {
buttonPressed = false
button5.setImage(UIImage(named: "Bolinha-3"), for: .normal)
}
else {
buttonPressed = true
button5.setImage(UIImage(named: "Bolinha-4"), for: .normal)
}
}
}
What you are going to accomplish is called Radio Buttons, unfortunately iOS (unlike macOS) doesn't provide this functionality.
My suggestion takes advantage of the option to assign different images to different states in Interface Builder – in this case the Default and Selected state – and to create an outlet collection, an array representing a sequence of UI elements of the same type.
The suggestion doesn't support an empty selection, by default the first button is selected.
In ViewController
create an outlet collection
#IBOutlet var buttons : [UIButton]!
and one IBAction
#IBAction func buttonAction(_ sender: UIButton) { }
In Interface Buider
set the images to the Default and Selected states of each button in the Attribute Inspector.
connect the buttons in the proper order to the outlet collection.
assign the tags 0 to 4 to the buttons in the same order.
connect all buttons also to the (same) IBAction.
In ViewController
create a property for the tag of the currently selected button
var selectedButton = 0
in viewDidLoad select the first button
override func viewDidLoad() {
super.viewDidLoad()
buttons[selectedButton].isSelected = true
}
Complete the IBAction, it deselects the previous button and selects the current.
#IBAction func buttonAction(_ sender: UIButton) {
let tag = sender.tag
buttons[selectedButton].isSelected = false
buttons[tag].isSelected = true
selectedButton = tag
}
let's consider you have named your buttons as follows: 1st button: "opt1Button", 2nd button: "opt2Button", 3rd button: "opt3Button" and so on...
create an IBAction with name "optionSelected" The function will look like:
#IBAction func optionSelected(_ sender: UIButton) {
opt1Button.isSelected = false
opt2Button.isSelected = false
opt3Button.isSelected = false
opt4Button.isSelected = false
opt5Button.isSelected = false
sender.isSelected = true
}
As soon any of the options is selected all the buttons will go to 'isSelected' false condition i.e all the buttons will be deselected at first and the selected button will be marked as selected. Same process will be followed again when any of the button is selected, everything will get deselected and the button user has pressed will be marked as selected.
Found this answer from (https://developer.apple.com/forums/thread/685124) and it worked for me.
Here I had placed two radio buttons on view controller class but unable to give logic to make a radio button active at a time can anyone help me ?
#IBAction func radioButtonAction(_ sender: KGRadioButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
workRadioButton.isSelected = false
} else{
}
}
#IBAction func WorkRadiobuttonAction(_ sender: KGRadioButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
homeRadioButton.isSelected = false
} else{
}
}
I am just using UIButton as radioButton.try like below
Create one array to store radio buttons
var radioButtonArray = [UIButton]()
Declare your radio buttons as UIButton or anyOther
#IBOutlet weak var radioButton1: UIButton!
#IBOutlet weak var radioButton2: UIButton!
Append all buttons into array
radioButtonArray.append(contentsOf: [radio1,radio2])
Set same target for all buttons
self.radioButton1.addTarget(self, action: #selector(self.folderRadioButtonClicked(_:)), for: UIControlEvents.touchUpInside)
self.radioButton2.addTarget(self, action: #selector(self.folderRadioButtonClicked(_:)), for: UIControlEvents.touchUpInside)
func folderRadioButtonClicked(_ sender:UIButton) {
for i in 0..<self.radioButtonArray.count {
if self.radioButtonArray[i] == sender{
// here you can perform what you want
self.radioButtonArray[i].setImage(UIImage(named: "checked.png"), for: UIControlState())
}else {
self.radioButtonArray[i].setImage(UIImage(named: "unchecked.png"), for: UIControlState())
}
}
}
i have 3 buttons that all have an image and action. However when the centerButton is pressed they should all change action and image. What is the easiest way to do this?
#IBOutlet weak var leftButton: UIButton!
#IBOutlet weak var rightButton: UIButton!
#IBOutlet weak var centerButton: UIButton!
override func draw(_ rect: CGRect) {
// Drawing code
centerButton.backgroundColor = UIColor(rgba: "#4834B4")
centerButton.layer.cornerRadius = self.centerButton.frame.width / 2
centerButton.addTarget(self, action: #selector(centerButtonClicked), for: UIControlEvents.touchUpInside)
leftButton.addTarget(self, action: #selector(rightButtonClicked), for: UIControlEvents.touchUpInside)
rightButton.addTarget(self, action: #selector(rightButtonClicked), for: UIControlEvents.touchUpInside)
}
func centerButtonClicked() {
if !pictureTaken {
//change action
} else {
//change action
}
}
If I have known well you want something like this:
func leftButtonClicked() {
if centerButton.backgroundColor == UIColor.yellowColor() {
centerButton.setBackgroundImage(image, forState: .Normal)
} else {
rightButton.setBackgroundImage(image, forState: .Normal)
}
}
func centerButtonClicked() {
if leftButton.backgroundColor == UIColor.yellowColor() {
leftButton.setBackgroundImage(image, forState: .Normal)
} else {
rightButton.setBackgroundImage(image, forState: .Normal)
}
I write two actions with an image changement.
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 ;)
I managed to change the title of a button itself and the text of a label by clicking on it.
On the top of that, I would like the title of the same button and the same text in the label to change AGAIN when clicked for the second time. Considering the code that I already build, how could I add that?
#IBOutlet weak var changeDegreeDisplay: UILabel!
#IBOutlet weak var radPressed: UIButton!
#IBAction func radianPressed(sender: AnyObject) {
radPressed.setTitle("Deg", forState: .Normal)
changeDegreeDisplay.text = "radians"
}
You probably would want to use an if statement to check for the current title of the button or its state, depends on your use case.
#IBAction func radianPressed(sender: UIButton) {
if sender.currentTitle == "Deg" {
sender.setTitle("Rad", forState: .Normal)
} else {
sender.setTitle("Deg", forState: .Normal)
}
}
Grab the title of the button, and check to see if it's "Deg" or "Rad", and then switch it to the correct one.
#IBOutlet weak var changeDegreeDisplay: UILabel!
#IBOutlet weak var radPressed: UIButton!
#IBAction func radianPressed(sender: AnyObject) {
let title = radPressed.titleForState(.Normal)
if title=="Deg" {
radPressed.setTitle("Rad", .Normal)
changeDegreeDisplay.text = "degrees"
} else {
radPressed.setTitle("Deg", .Normal)
changeDegreeDisplay.text = "radians"
}
}
I would suggest storing the titles as constants, so let degTitle = "Deg" and let radTitle = "Rad", and then compare using these constants in the conditional.