How to change previously pressed UIButton label in UITableViewCell? - ios

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).

Related

UITableView-upon scrolling it loses properties associated with selected cells. How to save these properties? - swift

I have an application with a custom popup that holds a tableview (see below). When I tap on a check box(UIButton), i swap out the background image and toggle between checked and unchecked. When I have a checked box, when I scroll up or down, the checked box defaults back to unchecked. Please could someone advise on how to keep the checked status of each cell upon scrolling?
class CustomHashTagPopup: UIViewController, UITableViewDelegate, UITableViewDataSource{
// CREATE TABLEVIEW CELLS
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableViewPicker.dequeueReusableCell(withIdentifier: "pickerCell") as! PreviewTableViewCustomPickerCell
if(indexPath == [0,0]){
let image = UIImage.init(named: "add")
cell.checkBoxOutlet.setBackgroundImage(image!, for: .normal)
cell.pickerLabel.text = "Create New HashTag"
cell.isCreateTagCell = true
}else{
let image = UIImage.init(named: "uncheck")
cell.checkBoxOutlet.setBackgroundImage(image!, for: .normal)
cell.pickerLabel.text = arrayHashTags[indexPath.row]
cell.isCreateTagCell = false
}
return cell
}
}
class PreviewTableViewCustomPickerCell: UITableViewCell {
var isSelect: Bool = false
var isCreateTagCell: Bool = false // TO DISTINGUISH 'CEATE NEW HASHTAG OPTION'
/*** OUTLETS ***/
#IBOutlet weak var checkBoxOutlet: UIButton!
#IBOutlet weak var pickerLabel: UILabel!
// TAP ON CHECK BOX
#IBAction func checkBoxBtn(_ sender: Any) {
if(isSelect == false && isCreateTagCell == false){
isSelect = true
// SHOW GREEN SELECTED CHECK BOX
let image = UIImage.init(named: "tick")
self.checkBoxOutlet.setBackgroundImage(image!, for: .normal)
}else if(isSelect == true && isCreateTagCell == false){
isSelect = false
// SHOW UNCHECKED BOX
let image = UIImage.init(named: "uncheck")
self.checkBoxOutlet.setBackgroundImage(image!, for: .normal)
}
}
}
The thing you miss is cell-reusing , you have to store every action inside the model array and check the current value and set the appropriate settings inside cellForRowAt , you may set the delegate of the actions to the vc for the buttons inside cellForRowAt for ease access of the model array to manipulate it as the user clicks that button at a specific indexPath , so conside declaring array like this
let arr = [false,true,false,false,false] first index is dummy as it will be skipped in Create New HashTag
//
#objc func btnClicked(_ sender:UIButton) {
arr[sender.tag] = !arr[sender.tag]
// reload indexPath of row = sender.tag , section = 0
}
//
Inside cellForRowAt
if (indexPath == [0,0]) {
let image = UIImage(named: "add")
cell.checkBoxOutlet.setBackgroundImage(image!, for: .normal)
cell.pickerLabel.text = "Create New HashTag"
cell.isCreateTagCell = true
}else{
let image = UIImage(named: arr[indexPath.row] ? "tick" : "uncheck") // edited here
cell.checkBoxOutlet.setBackgroundImage(image!, for: .normal)
cell.pickerLabel.text = arrayHashTags[indexPath.row]
cell.isCreateTagCell = false
}
cell.button.addTarget(self, action: #selector(btnClicked(_:)), for: .touchUpInside)
cell.button.tag = indexPath.row

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)
}
}
}

Hiding button on a particular tableview cell

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
}

How to get Text from a label in custom cell in Table View

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tbl_vw.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)as!cuscellTableViewCell
cell.txt_lbl.text = self.section[indexPath.row];
cell.sel_btn.addTarget(self, action: #selector(self.switchChanged), forControlEvents: .ValueChanged)
return cell
}
func switchChanged(sender: AnyObject) {
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
if switchControl.on {
print("The switch is on lets martch")
}
}
I have a switch and a label in a custom cell in tableview. When I ON the switch i need to get the text in the label, can any one help how to make it possible.
Use the tag property of UISwitch to store the position and use it in your handler to get the actual text form section array.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tbl_vw.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)as!cuscellTableViewCell
cell.txt_lbl.text = self.section[indexPath.row];
cell.sel_btn.tag = indexPath.row
cell.sel_btn.addTarget(self, action: #selector(self.switchChanged), forControlEvents: .ValueChanged)
return cell
}
func switchChanged(sender: AnyObject)
{
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
let text = self.section[switchControl.tag]
print(text)
if switchControl.on
{
print("The switch is on lets martch")
}
}
If you have multiple sections with multiple rows you can use a dictionary and store a tuple.
var items = [Int:(Int,Int)]()
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tbl_vw.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)as!cuscellTableViewCell
cell.txt_lbl.text = self.section[indexPath.row];
cell.sel_btn.tag = indexPath.row
let tuple = (section: indexPath.section, row: indexPath.row)
self.items[cell.sel_btn.hashValue] = tuple
cell.sel_btn.addTarget(self, action: #selector(self.switchChanged), forControlEvents: .ValueChanged)
return cell
}
func switchChanged(sender: AnyObject)
{
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
let tuple = self.items[switchControl.hashValue]
print(tuple.0) // this is the section
print(tuple.1) // this is the row
if switchControl.on
{
print("The switch is on lets martch")
}
}
In your case move func switchChanged(sender: AnyObject) to your custom class and assign selector in customCell for UISwitch. You will start receiving your switchChange event in your customCell now you are customCell you have access to your UISwitch & UILabel objects, also keep a boolean flag to maintain switch state if required.
Step 1: Move your switchChanged to customCell class.
Step 2: In customCell awakeFromNib or init method assign selector to UISwitch object to cell itself to receive switch change event in cell.
Step 3: Once your switch change event fires update your UILabel as you have access to it inside customCell.
Let me know if you have any doubt.
what you need to do is use block
from Get button click inside UI table view cell
first move your action method to table cell
in tableview cell add block property
#property (copy, nonatomic) void (^didTapButtonBlock)(id sender);
with your action method
call block
- (void)didTapButton:(id)sender {
if (self.didTapButtonBlock)
{
self.didTapButtonBlock(sender);
}
}
and receive that from cell , you have both section and row there
[cell setDidTapButtonBlock:^(id sender)
{
// Your code here
}];
Hope it helps
Set tag of sel_btn(UISwitch) in cellForRowAtIndexPath method.
sel_btn.tag = indexPath.row
On switchChanged method, get the tag of sel_btn and thus text of label.
let switchControl: UISwitch = sender as! UISwitch
let tag = switchControl.tag
let cell : cuscellTableViewCell = tbl_vw.cellForRowAtIndexPath(NSIndexPath(forRow: tag, inSection: 0)) as? cuscellTableViewCell
print(cell.txt_lbl.text)
If you want to get data from model not from view, then override the above two lines with below line:-
let textValue = self.section[tag];
In tableview delegate method -
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
add index path value to switch tag value -
cell.sel_btn.tag = indexPath.row
In Switch Value change method just get selected text when that switch is ON
func switchChanged(sender: AnyObject)
{
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
// If switch ON
if switchControl.on
{
print("The switch is ON”)
// Get Selected Label Text
let selectedText = self.section[switchControl.tag]
print(selectedText)
}
}

How to detect which table cell is clicked in swift 2

I am developing app which users will choose one of the two pictures in one cell. My prototype cell looks like :
How can I detect when the user presses the vote button which cell is selected ?
My tableView Code :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "NewTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NewTableViewCell
//For left image
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].poll_photo1 ){
cell.leftImage.sd_setImageWithURL(url)
}
//For right image
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].poll_photo2 ){
cell.rightImage.sd_setImageWithURL(url)
}
//For user picture
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].users[0].user_photo ){
cell.userPicture.sd_setImageWithURL(url)
}
// gets username and text
cell.userName.text=self.polls[indexPath.row].users[0].user_name
cell.description.text = self.polls[indexPath.row].poll_textfield
return cell
}
My web API:
I am assuming that your custom table view cell NewTableViewCell is having an outlet for your vote button.
Just tag your voteButton with indexPath.row and fetch the tag in its target function as shown below. You will get to know which cell's voteButton was tapped when you press your Vote Button
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("NewTableViewCell") as! NewTableViewCell
//Tagging with indexPath.row
cell.voteLeftButton.tag = indexPath.row
cell.voteRightButton.tag = indexPath.row
//This is the latest Swift 2.2 syntax for selector. If you are using the older version of Swift, Kindly check the selector syntax and make changes accordingly
cell.voteLeftButton.addTarget(self, action: #selector(voteLeftButtonPressed), forControlEvents: .TouchUpInside)
cell.voteRightButton.addTarget(self, action: #selector(voteRightButtonPressed), forControlEvents: .TouchUpInside)
return cell
}
func voteLeftButtonPressed(sender:UIButton){
print("Left Button Table cell clicked is \(sender.tag)")
}
func voteRightButtonPressed(sender:UIButton){
print("Right Button Table cell clicked is \(sender.tag)")
}
Add target/action to your button in cell configure method like this:
let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourController.tapAction(_:)))
Then implement tapAction method
func tapAction(sender : UITapGestureRecognizer)
{
if sender.state != UIGestureRecognizerState.Ended
{
return
}
let btn = sender.view as! UIButton
let pointTo : CGPoint = CGRectOffset(btn.bounds, btn.frame.size.width/2, btn.frame.size.height/2).origin;
let buttonPosition : CGPoint = btn.convertPoint(pointTo, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)
...
}

Resources