Change UIButton Color in CollectionViewCell [duplicate] - swift

This question already has answers here:
Change the color of UIButton in IBOutletCollection
(2 answers)
Closed 4 years ago.
I have two classes: ViewController and PercentCollectionViewCell.
When I tap on a button1_Tap the color is changed to blue. When I tap on a button2_Tap, the color of the second button turns blue, but the color of the first button does not turn back to white color.
PercentCollectionViewCell class:
class PercentCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var firstPercent: UIButton!
#IBOutlet weak var secondPercent: UIButton!
}
ViewController class:
let cellIdentifiers:[String] = ["FirstPercentCell", "SecondPercentCell"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellIdentifiers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifiers[indexPath.item], for: indexPath) as? PercentCollectionViewCell
collectionCell?.firstPercent?.addTarget(self, action: #selector(button1_Tap(_:)), for: .touchUpInside)
collectionCell?.secondPercent?.addTarget(self, action: #selector(button2_Tap(_:)), for: .touchUpInside)
return collectionCell!
}
#objc func button1_Tap(_ sender: UIButton) {
let firstPercent = sender
firstPercent.isSelected = true;
firstPercent.backgroundColor = UIColor.blue
secondPercent.isSelected = false;
secondPercent.backgroundColor = UIColor.white
percentage = 0.05
}
#objc func button2_Tap(_ sender: UIButton) {
let firstPercent = sender
firstPercent.isSelected = false;
firstPercent.backgroundColor = UIColor.white
secondPercent.isSelected = true;
secondPercent.backgroundColor = UIColor.blue
percentage = 0.1
}

Put your button_Tap function in your UICollectionViewCell subclass.
You can get by with just one button_Tap action. Hook both buttons to this function:
class PercentCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var firstPercent: UIButton!
#IBOutlet weak var secondPercent: UIButton!
#objc func button_Tap(_ sender: UIButton) {
let buttonSelected = sender
let buttonUnselected = (sender === firstPercent) ? secondPercent : firstPercent
buttonSelected.isSelected = true
buttonSelected.backgroundColor = .blue
buttonUnselected.isSelected = false
buttonUnselected.backgroundColor = .white
percentage = (sender === firstPercent) ? 0.05 : 0.1
}
}
Then either wire the buttons up in the Storyboard or set them up like this in cellForItemAt:
collectionCell?.firstPercent?.addTarget(collectionCell!, action: #selector(PercentCollectionViewCell.button_Tap(_:)), for: .touchUpInside)
collectionCell?.secondPercent?.addTarget(collectionCell!, action: #selector(PercentCollectionViewCell.button_Tap(_:)), for: .touchUpInside)
I'm sorry, I forgot to clarify that I have the value of percentage for
each button tap functions. So I think I need a few functions. Also in
the future, there will be more buttons (up to 10).
In that case, assign your buttons to an #IBOutlet collection and assign unique tag values in the range 0...9. Connect each button to the outlet collection in the Storyboard.
#IBOutlet var buttons: [UIButton]!
Then
#objc func button_Tap(_ sender: UIButton) {
buttons.forEach { button in button.backgroundColor = .white }
sender.backgroundColor = .blue
percentage = [0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5][sender.tag]
}
Make sure to assign the button_Tap action to each of your buttons.

Related

tableviewcell changing the images while scrolling

I have four button inside a tableview cell and each of the button has a unique as 0,1,2 and 3 respectively.
These four buttons and linked to a single button action method.
The code inside tableviewcell is as follows:
#IBOutlet weak var questionLbl: UILabel!
#IBOutlet weak var q1: UIButton!
#IBOutlet weak var q2: UIButton!
#IBOutlet weak var q3: UIButton!
#IBOutlet weak var q4: UIButton!
typealias emptyButtonClosure = (String,Int) -> ()
var buttonTapped:emptyButtonClosure?
#IBAction func btnClicked(sender:UIButton){
let buttonArray = [q1,q2,q3,q4]
for button in buttonArray {
if sender.tag == button!.tag{
button!.isSelected = true;
if let buttonTapped = buttonTapped{
buttonTapped(sender.titleLabel!.text!,sender.tag)
}
if let image = UIImage(named: "checked-radio") {
button!.setImage(image, for: .normal)
}
else
{
let image1 = UIImage(named: "unchecked-radio")
button!.setImage(image1, for: .normal)
}
}else{
button!.isSelected = false;
if let image = UIImage(named: "unchecked-radio") {
button!.setImage(image, for: .normal)
}
else
{
let image1 = UIImage(named: "checked-radio")
button!.setImage(image1, for: .normal)
}
}
}
}
These buttons are grouped into single array called as buttonarray because these buttons are radio buttons and only single selection is allowed.
Now the issue is,the value is fetched correctly for each cell in the tableview.But when the tableview is scrolled,the image position keeps changing.The code inside tableview is as follows:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "questioncell", for: indexPath) as! ChildQuestionCell
cell.questionLbl.text = self.listData?.nanniBookingDetails.questionsData[indexPath.row].question
cell.q1.setTitle(self.listData?.nanniBookingDetails.questionsData[indexPath.row].option1, for: .normal)
cell.q2.setTitle(self.listData?.nanniBookingDetails.questionsData[indexPath.row].option2, for: .normal)
cell.q3.setTitle(self.listData?.nanniBookingDetails.questionsData[indexPath.row].option3, for: .normal)
cell.q4.setTitle(self.listData?.nanniBookingDetails.questionsData[indexPath.row].option4, for: .normal)
cell.buttonTapped = { value,index in
print("button selected is",value,index)
self.answers.append(value)
}
return cell
}
How to retain the image position even when it is scrolled.Like the image selected for 1st row should not be shown in 3rd or 4th row.It should remain only in 1st row.
Please let me know how to solve this?

my Callback in CollectionViewCell doesn't work correctly

I have a simple CollectionViewCell with PhoneNumberTextField and a callback that I want to send to my server
class PhoneNumberCollectionViewCell: UICollectionViewCell, NiBLoadable, UITextFieldDelegate {
#IBOutlet weak var phoneLabel: UILabel!
#IBOutlet weak var PhoneNumberTextField: UITextField!
#IBOutlet weak var SecurityLabel: UILabel!
var returnValue: ((_ value: String) -> ())?
override func awakeFromNib() {
super.awakeFromNib()
Decorator.decorate(self)
}
func setPhoneLabelText(text: String) {
phoneLabel.text = text
}
func setSecurityLabel(text: String) {
SecurityLabel.text = text
}
func textFieldDidEndEditing(_ textField: UITextField) {
returnValue?(PhoneNumberTextField.text ?? "") // Use callback to return data
}
}
and I also have my ViewController with my CollectionView and I cant get my phoneNumber from cell to variable in ViewController. Here is come code
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let models = model[indexPath.row]
switch models {
case .phoneNumber:
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhoneNumberCollectionViewCell.name, for: indexPath) as? PhoneNumberCollectionViewCell {
cell.setSecurityLabel(text: "_ALLYOURDATAISINSECUREDAREA")
cell.setPhoneLabelText(text: "_YOURPHONENUMBER")
cell.PhoneNumberTextField.text = phoneNumber
cell.returnValue = { value in
self.phoneNumber = value
}
return cell
}
and when I print what I got from textField I got nil
#objc func sendPhoneNumber() {
print("PHONE NUMBER IS - \(String(describing: self.phoneNumber))")
}
what's wrong am I doing???
You need to set the delegate for the textField, or the textField delegates wont work.
override func awakeFromNib() {
super.awakeFromNib()
Decorator.decorate(self)
PhoneNumberTextField.delegate = self
}
Side note:
Don't capitalize variables and constants.
Edit to secondary question:
You either want to (through the Xib) make an IBAction outlet that is called on value changed, or:
textField.addTarget(self, action: #selector(textFieldValueChanged), for: .editingChanged)
both options will call the delegate function every time a letter is typed or removed.
Have you registered your collection view cell and declare the delegate and datasource?
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView(PhoneNumberCollectionViewCell.self, forCellWithReuseIdentifier: PhoneNumberCollectionViewCell.name)
collectionView.delegate = self
collectionView.dataSource = self
return collectionView

Change the color of UIButton in IBOutletCollection

I have an IBOutletCollection of UIButton:
#IBOutlet var buttons: [UIButton]!
and a function for a tap:
#IBAction func button_Tap(_ sender: UIButton) {
for button in buttons {
if button.tag == sender.tag { button.backgroundColor = .blue }
else {button.backgroundColor = .white}
}
}
The outlets are connected in a storyboard.
I need to change the color of the buttons on a button tap. When tap on the first button, it should turn to the blue color and other buttons should be white.
My code does not work correctly. When I tap on the button it turns blue color. But when I tap on another button, the first button does not change the color to white.
Can someone help me to solve this problem? Thanks.
Update:
I also have this code in class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource:
let cellIdentifiers:[String] = ["FirstPercentCell", "SecondPercentCell", "ThirdPercentCell", "FourthPercentCell", "FifthPercentCell"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cellIdentifiers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifiers[indexPath.item], for: indexPath)
return collectionCell
}
So maybe the problem in this part?
Try this code:
#IBOutlet var buttons: [UIButton]!
var lastTappedButton: UIButton?
#IBAction func button_Tap(_ sender: UIButton) {
for button in buttons {
if let lastTappedButton = lastTappedButton, lastTappedButton != sender {
button.backgroundColor = .white
}
if button.tag == sender.tag {
button.backgroundColor = .blue
lastTappedButton = button
}
}
}
For this to work
1- All buttons should be connected to
#IBOutlet var buttons: [UIButton]!
2- All buttons should be connected to
#IBAction func button_Tap(_ sender: UIButton)
3- Every button should have a different tag for example 0,1,2
4- You can use this
#IBAction func button_Tap(_ sender: UIButton) {
self.buttons.forEach { $0.backgroundColor = $0 === sender ? .blue : .white }
}
Result

Updating button events in Swift?

I am creating a Check List app and trying to update button event which is set images itself after clicked.
Here is my code:
protocol CustomeCellDelegate {
func cell(cell: UITableViewCell, updateTheButton button: UIButton)
}
class Cells: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var checkBox: UIButton!
var buttonPressed = true
#IBAction func checkBoxAction(_ sender: UIButton) {
if buttonPressed {
sender.setImage(UIImage(named: "checkBoxB"), for: UIControlState.normal)
buttonPressed = false
} else {
sender.setImage(UIImage(named:""), for: UIControlState.normal)
buttonPressed = true
}
}
#objc func recordButtonImage(_ button: UIButton) {
cellDelegate?.cell(cell: self, updateTheButton: button) // cell notify the button that touched.
}
override func awakeFromNib() {
checkBox.addTarget(self, action: #selector(recordButtonImage(_:)), for: UIControlEvents.touchUpInside)
}
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, CustomeCellDelegate {
#IBOutlet weak var tableView: UITableView!
var myImages: [UIImage?]!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
myImages = Array(repeatElement(nil, count: 50))
let nib = UINib(nibName: "Cells", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "Cells")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
internal func tableView(_ tableView:UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "theCells") as? Cells
cell?.theTextField.delegate = self
cell?.cellDelegate = self
cell?.checkBox.setImage(myImages[indexPath.row], for: UIControlState.normal)
return cell!
}
// This function from protocol and it helps to update button images.
func cell(cell: UITableViewCell, updateTheButton button: UIButton) {
print("Delegate method Update Button Images.")
if let indexPath = tableView.indexPath(for: cell), let buttonImage = button.image(for: UIControlState.normal) {
myImages[indexPath.row] = buttonImage
}
}
I would like to update both events of the button that set image after checked and unchecked. Therefore, my table view can dequeue Reusable Cell and have those events updated.
But the result is just one event of button which is checked updated but not the unchecked one. The checked button still repeats whatever I do to uncheck it.
I think you don't need recordButtonImage method, you should call the delegate method from the button tapped action i.e. checkBoxAction, just like below
class Cells: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var checkBox: UIButton!
var buttonPressed = true
#IBAction func checkBoxAction(_ sender: UIButton) {
if buttonPressed {
sender.setImage(UIImage(named: "checkBoxB"), for: UIControlState.normal)
buttonPressed = false
} else {
sender.setImage(UIImage(named:""), for: UIControlState.normal)
buttonPressed = true
}
// cell notify the button that touched.
cellDelegate?.cell(cell: self, updateTheButton: button)
}
}
Also your data source method, doesn't tell the cell whether the button was pressed or not. It just sets the image but doesn't set the variable buttonPressed
internal func tableView(_ tableView:UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "theCells") as? Cells
cell?.theTextField.delegate = self
cell?.cellDelegate = self
cell?.checkBox.setImage(myImages[indexPath.row], for: UIControlState.normal)
return cell!
}
The best way to handle UIButton is using its own state.
You need to hold current state value in one array. So you can keep the state in cellForRow and other things. By default set all state to false.
In CellForRow, just put below code:
YOUR_BUTTON.isSelected = aryState[indexPath.row] as! Bool
Set image for your button
YOUR_BUTTON.setImage(UIImage(named: NORMAL_IMAGE), for: .normal)
YOUR_BUTTON.setImage(UIImage(named: SELECTED_IMAGE), for: .selected)
Just change button state when it clicked:
#IBAction func checkBoxAction(_ sender: UIButton) {
let button = sender
button.isSelected = !button.isSelected
}

Selecting a custom cell activates another cell

I have a custom cell class that has an image, a few text labels(1 truncated), and a button:
class CustomTVC: UITableViewCell {
/* Outlets */
#IBOutlet weak var imageHolder: UIImageView!
#IBOutlet weak var concatenatedTitleHolder: UILabel!
#IBOutlet weak var localDateHolder: UILabel!
#IBOutlet weak var descriptionHolder: UILabel!
#IBOutlet weak var seeMoreButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
}
When the user clicks on the button, it shows the full description of the truncated text label. However, the problem I have now is when the user clicks on the button for a specific cell, it shows the full description for the cell that the user clicked, and also the full description of another cell.
I know the reason that's happening is because the tableview reuses the cell via dequeueReusableCellWithIdentifier. How would I go about implementing a function that will make sure that when a user clicks on the button for a specific cell, only that cell's full description is shown?
Code for tableview:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as! CustomTVC
if listOfShows.count != 0 {
// Downloading and displaying picture
if let downloadPicture: UIImage = helperFunctions.downloadImage(listOfShows[indexPath.row].imageLink) {
cell.imageHolder.image = downloadPicture
}
// Enlarging / dismissing picture
cell.imageHolder.userInteractionEnabled = true
let newTapped = UITapGestureRecognizer(target: self, action: #selector(MainTVC.imagedTapped(_:)))
cell.imageHolder.addGestureRecognizer(newTapped)
// Concatenating channel + series + episode title
let concatenatedTitle = listOfShows[indexPath.row].channel + " " + listOfShows[indexPath.row].series + " " + listOfShows[indexPath.row].episodeTitle
// Converting into local date / time
let universalTime = helperFunctions.localDateAndTimeConverter(listOfShows[indexPath.row].originalAirDate)
/* Other labels */
cell.concatenatedTitleHolder.text = concatenatedTitle
cell.localDateHolder.text = universalTime
cell.descriptionHolder.text = listOfShows[indexPath.row].description
cell.seeMoreButton.tag = indexPath.row
cell.seeMoreButton.addTarget(self, action: #selector(MainTVC.buttonTapped(_:markedArray:)), forControlEvents: .TouchUpInside)
resetCellSettings(cell)
}
return cell
}
func buttonTapped(sender: UIButton, markedArray: [Bool]) {
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomTVC
cell.seeMoreButton.hidden = true
cell.descriptionHolder.numberOfLines = 0
cell.descriptionHolder.lineBreakMode = NSLineBreakMode.ByWordWrapping
cell.descriptionHolder.sizeToFit()
}
func resetCellSettings(cell: CustomTVC) {
cell.seeMoreButton.hidden = false
cell.descriptionHolder.numberOfLines = 1
cell.descriptionHolder.lineBreakMode = NSLineBreakMode.ByTruncatingTail
cell.descriptionHolder.sizeToFit()
}
You should put buttonTapped func in CustomTVC class. And set outlet IBAction of seeMoreButton for that func when cell created.