Hiding button on a particular tableview cell - ios

Background:
My code displays a list of food items in a tableview, and in a UITableViewCell there is a button to add the food items to cart as needed.
When a particular item is added to cart, the add button corresponding to that cell should be hidden and rest all which are not added should not be hidden.
Problem:
The problem is, when I add an item say which is at index: 0 to cart, it gets added successfully and button is hidden, but when I click on second button which is at index : 1, second item also gets added to cart but the first item button becomes unhidden, at a time only one button which is at the current index is getting hidden instead of all the item buttons which are added to cart.
func addToCart(_ button : UIButton) {
self.selectedIndex = button.tag
let foodItem : HCFoodItem = arrFoodItems?.object(at: self.selectedIndex) as! HCFoodItem
foodItem.isFoodAddedToCart = true
arrItemsAddedToCart?.add(foodItem)
let arrayCount = arrItemsAddedToCart?.count
let badgeCount : String = String(format: "%d", arrayCount!)
tabBarController?.tabBar.items?.last?.badgeValue = badgeCount
HCDataCache.sharedDataCacheInstance.cacheData(object: arrItemsAddedToCart, key: HC_CACHE_KEY_CART_ITEM)
self.foodItemsTableView?.reloadData()
}
// hide/unhide buttons here...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellId: NSString = "LazyLoadTableViewCell"
var foodItems : HCFoodItem?
foodItems = arrFoodItems?.object(at: indexPath.row) as! HCFoodItem?
let cell: HCFoodItemTableViewCell = tableView.dequeueReusableCell(withIdentifier: cellId as String)! as! HCFoodItemTableViewCell
cell.imgFood?.downloadImageFrom(link: foodItems?.strFoodImage as! String, contentMode: UIViewContentMode.scaleAspectFill)
cell.btnAddDelete?.tag = indexPath.row
cell.btnAddDelete?.addTarget(self, action: #selector(HCHomeViewController.addToCart(_:)), for: UIControlEvents.touchUpInside)
let itemsAddedToCart : NSMutableArray? = HCDataCache.sharedDataCacheInstance.fetchCachedDataForKey(key: HC_CACHE_KEY_CART_ITEM)
if (itemsAddedToCart?.count)! > 0 {
for cardFoodItem in itemsAddedToCart!{
let foodItem : HCFoodItem = cardFoodItem as! HCFoodItem
if foodItem.strFoodName == foodItems?.strFoodName {
cell.btnAddDelete?.isHidden = true
}else{
cell.btnAddDelete?.isHidden = false
}
}
} else {
cell.btnAddDelete?.isHidden = false
}
cell.selectionStyle = .none
return cell
}

Related

How to work with the Cosmo Raiting Library in table cells? (SWIFT)

I don't understand how to link the selection of a set of rating stars to each cell and save this value?
Cosmo lib: https://github.com/evgenyneu/Cosmos
My Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCellTableViewCell
let currentNameItem = gameNameArray[indexPath.row]
cell.gameNameLabel?.text = currentNameItem["Name"] as? String
// MARK: - переменная из словаря - если true ставим галочку - если нет убираем
if (currentNameItem["isCompleted"] as? Bool) == true {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
let currentSubNameItem = gameSubNameArray[indexPath.row]
cell.subGameNameLabel?.text = currentSubNameItem["Name"] as? String
let currentScoreItem = gameScoreArray[indexPath.row]
cell.gameScoreValue?.text = currentScoreItem["Name"] as? String
let currentImageItem = gameImageArray[indexPath.row]
guard let url = URL(string: currentImageItem["Name"] as! String) else { return cell }
cell.gameImage.sd_setImage(with: url, completed: nil)
//I can set the rating value
cell.fiveStarRaiting.rating = 5
let currentRaitingItem = raitingArray[indexPath.row] //array where I would like to save the ratings
cell.fiveStarRaiting.rating = currentRaitingItem //here, the values from the rating array should be pulled up by the ide
//allows you to save the rating value at the end of the finger movement gesture
cell.fiveStarRaiting.didFinishTouchingCosmos = { [self] raiting in raitingArray.append(raitingStarValue)}
print(raitingArray.count)
//cell.testButton0.addTarget(self, action: #selector(testFuncButton(sender:)), for: .touchUpInside)
return cell
}
As you know, table view cells are reusable, so when you scrolls and when cells are disappeared then its values are also cleared that's how table view and collection view works.
So if you want to save those ratings then you can go with a local temporary dictionary.
e.g.
var ratingsArray = [Int:Float]()
store your indexpath as key in "ratingsArray" dictionary and set its value as cosmos ratings.
and set cosmos ratings values as prefill in "cellForRowAt" table view method,
if ratingsArray.keys.contains(indexPath.row) {
cell.fiveStarRaiting.rating = 5
}
else {
cell.fiveStarRaiting.rating = 0 // set starts default value here
}
cell.fiveStarRaiting.didFinishTouchingCosmos = { [self] rating in
ratingsArray[indexPath.row] = rating
}

Allow one button in collection view cell to be tapped and updated at a time

When a button gets tapped within a Collection View, the border color of the button gets updated to yellow and also reflects that it has been selected. If I tap on another button, I want to be able to update the border to yellow but also change the previous button's border to the original color black.
Attempted this solution: How to highlight selection only one button at a time from multiple buttons using Swift but was unable to change the previous button to the original color.
My current code
let buttonTitles: [String] = ["Red", "Blue", "Green", "Orange", "Gray"]
var isChosen: Bool = false
#objc func selectAction(_ sender: UIButton) {
let tag = sender.tag
print(tag)
isChosen = true
if tag == 1 {
sender.borderColor = .yellow
sender.borderWidth = 5
} else if tag == 2 {
sender.borderColor = .black
sender.borderWidth = 1
}
isChosen = !isChosen
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return buttonTitles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "colorsCollectionCell", for: indexPath) as! ColorsCollectionCell
cell.colorsButton.setTitle(timeOptions[indexPath.row], for: .normal)
cell.colorsButton.tag = indexPath.row
cell.colorsButton.addTarget(self, action: #selector(selectAction(_:)), for: .touchUpInside)
return cell
}
You can keep track of the selectedButton indexPath by declaring a variable:
var selectedButtonIndexPath: IndexPath?
Then inside cellForItemAt, you can define what the button looks like when selected:
if indexPath == selectedButtonIndexPath { // If button is selected
cell.colorsButton.layer.borderColor = UIColor.yellow.cgColor
cell.colorsButton.layer.borderWidth = 5
} else { // If it is not selected
cell.colorsButton.layer.borderColor = UIColor.black.cgColor
cell.colorsButton.layer.borderWidth = 1
}
Finally, inside your selectAction function:
#objc func selectAction(_ sender: UIButton) {
/* First let's get a reference to the cell that contains the newly selected button. Make sure you get the correct amount of superviews in there.. it depends on how you laid out your cells */
guard let newlySelectedButtonCell = sender.superview?.superview as? ColorsCollectionCell else { return }
// Let's find out the indexPath for the newly selected button
guard let newlySelectedButtonCellIndexPath = collectionView.indexPath(for: newlySelectedButtonCell) else { return }
// If previously selected button and newly selected button are the same, simply deselect button
if newlySelectedButtonCellIndexPath == selectedButtonIndexPath {
newlySelectedButtonCell.colorsButton.layer.borderColor = UIColor.black.cgColor
newlySelectedButtonCell.colorsButton.layer.borderWidth = 1
selectedButtonIndexPath = nil
return
}
// If newly selected button is different than previously selected button...
// First change the look for previously selected button
if let indexPath = selectedButtonIndexPath, let previouslySelectedButtonCell = collectionView.cellForItem(at: indexPath) as? ColorsCollectionCell {
newlySelectedButtonCell.colorsButton.layer.borderColor = UIColor.black.cgColor
newlySelectedButtonCell.colorsButton.layer.borderWidth = 1
}
// Now change the look of newly selected button
newlySelectedButtonCell.layer.borderColor = UIColor.yellow.cgColor
newlySelectedButtonCell.layer.borderWidth = 5
selectedButtonIndexPath = newlySelectedButtonCellIndexPath
}
There is an easier way to code the last function, and that would be to simply change the value of selectedButtonIndexPath and call reloadData() to update the collectionView. But it's not optimal:
#objc func selectAction(_ sender: UIButton) {
guard let newlySelectedButtonCell = sender.superview?.superview as? ColorsCollectionCell else { return }
guard let newlySelectedButtonCellIndexPath = collectionView.indexPath(for: newlySelectedButtonCell) else { return }
if newlySelectedButtonCellIndexPath != selectedButtonIndexPath {
selectedButtonIndexPath = newlySelectedButtonCellIndexPath
} else { // If same button selected, deselect? Unless you want a different behavior
selectedButtonIndexPath = nil
}
collectionView.reloadData()
}

Multiple sections in UITableView and UIButtons swift

I have a UITableView that has sections (Category0, Category1,..), and every row of a specific section is a UITableView that has one section which is the question (Question1,..) and rows which are the options to be answered (option1, option2,..).
The problem is when I click on a button in a specific category and a specific question (Category0, question1, option0) see screenshot1,
immediately another buttons in another categories are clicked (Category1, question2, option0) see screenshot2,
and (Category4, question1, option0) see screenshot3.
the code below:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? insideTableViewCell
cell?.answerlabel.text = "option \(indexPath.row)"
cell?.initCellItem(id: (myObject?.id)! , answer: (myObject?.answerArray![indexPath.row] as? String)!)
return cell!
}
In a custom UITableViewCell which is insideTableViewCell:
func initCellItem(id: Int , answer: String) {
radioButton.setImage( imageLiteral(resourceName: "unchecked"), for: .normal)
radioButton.setImage( imageLiteral(resourceName: "checked"), for: .selected)
radioButton.tag = id
radioButton.setTitle(answer, for: UIControlState.disabled)
radioButton.addTarget(self, action: #selector(self.radioButtonTapped), for: .touchUpInside)
}
#objc func radioButtonTapped(_ radioButton: UIButton) {
print(radioButton.tag)
print(radioButton.title(for: UIControlState.disabled) as Any)
let answer = radioButton.title(for: UIControlState.disabled) as Any
let StrId = String(radioButton.tag)
defaults.set(answer, forKey: StrId)
let isSelected = !self.radioButton.isSelected
self.radioButton.isSelected = isSelected
if isSelected {
deselectOtherButton()
}
}
func deselectOtherButton() {
let tableView = self.superview as! UITableView
let tappedCellIndexPath = tableView.indexPath(for: self)!
let section = tappedCellIndexPath.section
let rowCounts = tableView.numberOfRows(inSection: section)
for row in 0..<rowCounts {
if row != tappedCellIndexPath.row {
let cell = tableView.cellForRow(at: IndexPath(row: row, section: section)) as! insideTableViewCell
cell.radioButton.isSelected = false
}
}
}
You haven't posted code still guessing.
You can create model object like
class QuestionData {
var strQuestion:String? // This may contains Question
var strOptions:[String]? // It may contains options titles of your buttons
var selectedAnswerIndex:Int? // When any button tapped
}
And you should create category models like
class Categories {
var categoryTitle:String?
var questions:[QuestionData] = []
}
you can use this Categories class as main source of your dataSource array
var arrayDataSource = [Categories]()
And fill this with your original data.
now whenever any button tapped you can use selectedAnswerIndex:Int to store current selected option for question. and if it is null then user has not selected any option yet.
I have created class so it is reference type you can directly set the value without worry
Hope it is helpful to you
There has some and simple code I think it will help you :- if it is not sutable for you pls don't mind :-
if (!btnGreen3.isSelected)
{
btnGreen3.isSelected = !btnGreen3.isSelected
}
btnBlue3.isSelected = false
btnBlack3.isSelected = false
You need to save the states of every cell.
The reason is you are using dequereuseable cell with identifier when you scroll it switch to another cell.
So make Array or Dictionary where save the state of every selected and unselected Rows.

UITableViewCell images disappear when scrolling

I have a UIButton inside a UITableViewCell where the image changes once the button is tapped. Though the selected buttons get selected as intended, once the UITableView scrolls, the selected images disappear since the cells are reused.
I'm having trouble writing the logic. Please help.
My code is below, in Swift 3.
CellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//Button_Action
addSongButtonIdentifier(cell: cell, indexPath.row)
}
This is where the cell is created:
func addSongButtonIdentifier(cell: UITableViewCell, _ index: Int) {
let addButton = cell.viewWithTag(TABLE_CELL_TAGS.addButton) as! UIButton
//accessibilityIdentifier is used to identify a particular element which takes an input parameter of a string
//assigning the indexpath button
addButton.accessibilityIdentifier = String (index)
// print("visible Index:",index)
print("Index when scrolling :",addButton.accessibilityIdentifier!)
addButton.setImage(UIImage(named: "correct"), for: UIControlState.selected)
addButton.setImage(UIImage(named: "add_btn"), for: UIControlState.normal)
addButton.isSelected = false
addButton.addTarget(self, action: #selector(AddToPlaylistViewController.tapFunction), for:.touchUpInside)
}
The tap function:
func tapFunction(sender: UIButton) {
print("IndexOfRow :",sender.accessibilityIdentifier!)
// if let seporated by a comma defines, if let inside a if let. So if the first fails it wont come to second if let
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]//toggle the state when tapped multiple times
}
sender.isSelected = !sender.isSelected //image toggle
print(" Array Data: ", self.sateOfNewSongArray)
selectedSongList.removeAll()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element{
print("true:", index)
selectedSongList.append(songDetailsArray[index])
print("selectedSongList :",selectedSongList)
}
}
}
There is logical error in func addSongButtonIdentifier(cell: UITableViewCell, _ index: Int) function at line addButton.isSelected = false
it should be addButton.isSelected = self.sateOfNewSongArray[index]
Since, cellForRowAtIndexpath method is called every time table is scrolled, it's resetting selected state of 'addButton'
You need to have array where you store which indexes are selected like selectedSongList array that you have. Then in your cellForRow method you need to use bool proparty from this array to give selected or deselected state to your button or in your addSongButtonIdentifier method selected state need to be
addButton.isSelected = selectedSongList.contains(indexPath.row)
Create a Model class for filling UITableView and take UIImage varaivals in that model, which will hold the current image for cell. On click on button action just change the UIImage variable with current image.
Best approach will be using a model class and keeping the track of each indiviual element in cell. But let me give you a quick fix.
Create a custom class of Button any where like this.
class classname: UIButton {
var imageName: String?
}
Go in your storyboard change the class from UIButton to classname
In your tableViewCellForIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let addButton = cell.viewWithTag(TABLE_CELL_TAGS.addButton) as! classname
if let imgName = addButton.imageName {
addButton.setImage(UIImage(named: imgName), for: UIControlState.normal)
} else {
addButton.setImage(UIImage(named: "add_btn"), for:UIControlState.normal)
}
addButton.addTarget(self, action: #selector(AddToPlaylistViewController.tapFunction), for:.touchUpInside)
return cell
}
Now let's move to your tapbutton implementation
func tapFunction(sender: classname) {
print("IndexOfRow :",sender.accessibilityIdentifier!)
// if let seporated by a comma defines, if let inside a if let. So if the first fails it wont come to second if let
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]//toggle the state when tapped multiple times
}
sender.imageName = sender.imageName == "correct" ? "add_btn" : "correct" //image toggle
sender.setImage(UIImage(named: sender.imageName), for:UIControlState.normal)
print(" Array Data: ", self.sateOfNewSongArray)
selectedSongList.removeAll()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element{
print("true:", index)
selectedSongList.append(songDetailsArray[index])
print("selectedSongList :",selectedSongList)
}
}
}

How to change previously pressed UIButton label in UITableViewCell?

I have UITableView with labels and a button to display list of audios.
Once the button labeled "Play" is pressed it changes to "Stop" and audio is played, and if another button is pressed in other cell the previous cell button SHOULD changes its label to "Play".
Now, the problem I have here is how to change the previous cell button back to "play" ?
The labels are outlets in UITableViewCell and the UIButton is programatically added in cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SongTitleCell", forIndexPath: indexPath) as SongsTableViewCell
let songDic : NSDictionary = arrSongs.objectAtIndex(indexPath.row) as NSDictionary
cell.lblSongTitle.text = songDic.objectForKey("SongTitle") as? String
btnPlayPause = UIButton(frame : CGRectMake(13,2,40,40))
btnPlayPause.tag = indexPath.row
btnPlayPause.setTitle("Play", forState: UIControlState.Normal)
btnPlayPause.addTarget(self, action: "cellSongClicked:", forControlEvents: UIControlEvents.TouchUpInside)
cell.contentView.addSubview(btnPlayPause)
return cell
}
The Action of the button here:
func cellSongClicked (sender : AnyObject ){
var remote = GetSongData()
remote.delegate = self
var btnCurrentPressed = sender as UIButton
//Play Selected Song
let songDic : NSDictionary = arrSongs.objectAtIndex(btnCurrentPressed.tag) as NSDictionary
if (btnCurrentPressed.currentTitle == "Play") {
remote.PlaySelectedSong(songDic.objectForKey("SongURL") as String!)
btnCurrentPressed.setTitle("Stop", forState: UIControlState.Normal)
} else {
playerContoller.stop()
btnCurrentPressed.setTitle("Play", forState: UIControlState.Normal)
}
}
Thanks
I would use this strategy:
1) mantain the index path of the cell containing the current playing song in a property of your class (you know the new indexPath from the UIButton tag in the "cellSongClicked" action)
2) In "cellForRowAtIndexPath" set the title of the button to "Play" or "Stop" depending on being this the cell of the current playing song
3) When a button is pressed refresh the current playing song cell and the new playing song cell calling on them "reloadRowsAtIndexPaths"
Hope this helps
EDIT CODE DETAILS:
1) don't reallocate every time the button. It must be another outlet of the SongsTableViewCell class (same as the label). And set the target/action from the Interface Builder (add #IBAction in front of "func cellSongClicked" and ctrl-drag from IB)
2) add the following property to your class:
private var currentSong : Int?
3) method cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SongTitleCell", forIndexPath: indexPath) as SongsTableViewCell
let songDic : NSDictionary = arrSongs.objectAtIndex(indexPath.row) as NSDictionary
cell.lblSongTitle.text = songDic.objectForKey("SongTitle") as? String
cell.btnPlayPause.tag = indexPath.row
var title : String
if let _currentSong = currentSong {
title = indexPath.row == _currentSong ? "Stop" : "Play"
} else {
title = "Play"
}
cell.btnPlayPause.setTitle(title, forState: UIControlState.Normal)
return cell
}
4) And the action:
#IBAction func cellSongClicked (sender : AnyObject ){
var remote = GetSongData()
remote.delegate = self
var btnCurrentPressed = sender as UIButton
//Play Selected Song
let songDic : NSDictionary = arrSongs.objectAtIndex(btnCurrentPressed.tag) as NSDictionary
var rowsToReload = [NSIndexPath]()
var stopCurrent = false
if let _currentSong = currentSong {
if btnCurrentPressed.tag == _currentSong {
stopCurrent = true
} else {
rowsToReload.append(NSIndexPath(forRow: _currentSong, inSection:0))
}
}
rowsToReload.append(NSIndexPath(forRow: btnCurrentPressed.tag, inSection:0))
currentSong = stopCurrent ? nil : btnCurrentPressed.tag
self.tableView.reloadRowsAtIndexPaths(rowsToReload, withRowAnimation: .None)
}
EDIT: Added stop for current song
Check if the user is tapping the current playing button (if btnCurrentPressed.tag == _currentSong). In that case do not reload that row (otherwise you will reload it twice) and set the currentSong property to nil (using temp boolean stopCurrent).

Resources