I have counting labels in a tableview cells according to plus and minus button the label value increases and decrease count by one and when i am scrolling down the label is repeating on other cells and data is changing in cells from moving up and down.Anyone please help me with this.Thanks
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:CustomTableViewCell = self.customTableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! CustomTableViewCell
cell.plusButton.tag = indexPath.row
cell.plusButton.addTarget(self, action: #selector(BtnPlusPressed(_:)), for: .touchUpInside)
cell.minusButton.tag = indexPath.row
cell.minusButton.addTarget(self, action: #selector(BtnMinusPressed(_:)), for: .touchUpInside)
cell.deviceLabel.text = self.laptop[indexPath.row]
return cell
}
#objc func BtnPlusPressed(_ sender: AnyObject) {
let button = sender as? UIButton
let cell = button?.superview?.superview as? CustomTableViewCell
let indexPath = customTableView.indexPath(for: cell!)
if let text = cell?.countLabel.text, let value = Int(text) {
var counter = value {
didSet {
cell?.countLabel.text = "\(counter)"
print()
}
}
counter += 1
callback?(counter)
}
}
#objc func BtnMinusPressed(_ sender: AnyObject) {
let button = sender as? UIButton
let cell = button?.superview?.superview as? CustomTableViewCell
let indexPath = customTableView.indexPath(for: cell!)
if let text = cell?.countLabel.text, let value = Int(text), value > 0 {
var counter = value {
didSet {
cell?.countLabel.text = "\(counter)"
}
}
counter -= 1
callback?(counter)
}
screenshot
I honestly don't know what's callback?(counter) doing. But I think your problem could be solved, as other people has already said, by storing your counter values along with the datasource. Right now your counterproperty only exists in the cellForRowblock.
Let's say you want all the values start from zero, and your model array is called list. You could do the following:
var counters = [Int]()
for i in 0..<list.count {
counters.append(0)
}
and then your method could be like this. I unwrapped the button to avoid dealing with optionals:
#objc func BtnMinusPressed(_ sender: AnyObject) {
guard let button = sender as? UIButton else { return }
let position = button.tag
guard counters[position] > 0 else { return }
counters[position]
counters[position] -= 1
cell.countLabel.text = "\(counters[position])"
}
Cells are reused. You have to save the counter value for each cell. But rather than using an extra array use a custom struct as data model
struct Model {
let name : String
var counter = 0
}
Declare the data source array
var laptop = [Model]()
And in Swift a better way as target/action is using a callback. The callback must be declared in the table view cell.
In the custom cell add IBactions and the callback to be able to update the model in the controller. The outlets for the buttons are not needed. Connect the actions to the buttons in Interface Builder.
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var deviceLabel: UILabel!
#IBOutlet weak var countLabel: UILabel!
var callback : ((Int) -> Void)?
var counter = 0 {
didSet { countLabel.text = "\(counter)" }
}
#IBAction func plusAction(_ sender: UIButton) {
counter += 1
callback?(counter)
}
#IBAction func minusAction(_ sender: UIButton) {
if counter > 0 { counter -= 1 }
callback?(counter)
}
}
In cellForRow assign the string value to the deviceLabel and assign also the callback. Whenever one of the buttons is pressed the label and the model are updated.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.customTableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! CustomTableViewCell
let item = self.laptop[indexPath.row]
cell.deviceLabel.text = item.name
cell.counter = item.counter
cell.callback = ( newValue in
self.laptop[indexPath.row].counter = newValue
}
return cell
}
Delete also both Btn...Pressed actions. the benefit is no view hierarchy math, no tags, no objective-c-ish target/action
Related
I am using table view cell to display the data . I have a segment control . In first section I am displaying the list of the move with button control . When the user click the check mark button I want to send those table view cell values to seconds control and added to on it .. Please give me some sample code based on the below code ..
Here is the table view cell .
import UIKit
protocol CellSubclassDelegate: AnyObject {
func buttonTapped(cell: MovieViewCell)
}
class MovieViewCell: UITableViewCell {
weak var delegate:CellSubclassDelegate?
static let identifier = "MovieViewCell"
#IBOutlet weak var movieImage: UIImageView!
#IBOutlet weak var movieTitle: UILabel!
#IBOutlet weak var movieOverview: UILabel!
#IBOutlet weak var someButton: UIButton!
#IBAction func someButtonTapped(_ sender: UIButton) {
self.delegate?.buttonTapped(cell: self)
}
func configureCell(title: String?, overview: String?, data: Data?) {
movieTitle.text = title
movieOverview.text = overview
movieImage.image = nil
if let imageData = data{
movieImage.image = UIImage(data: imageData)
// movieImage.image = nil
}
}
}
Table View Cell for row code ..
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
let row = indexPath.row
let title = presenter.getTitle(by: row)
let overview = presenter.getOverview(by: row)
let baseImageURL = presenter.getUrlImage(by: row)
let data = presenter.getImageData(by: baseImageURL)
cell.delegate = self
cell.configureCell(title: title, overview: overview, data: data)
return cell
}
Here is the code implementation of delegate .
extension MovieViewController : CellSubclassDelegate{
func buttonTapped(cell: MovieViewCell) {
guard (self.tableView.indexPath(for: cell) != nil) else {return}
let customViewController = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as? MovieDeatilsViewController
customViewController?.titlemovie = cell.movieTitle.text ?? ""
customViewController?.imagemovie = cell.movieImage.image
customViewController?.overview = cell.movieOverview.text ?? ""
// customViewController?.movieTitleHeader.text = cell.movieTitle.text ?? ""
self.navigationController?.pushViewController(customViewController!, animated: true)
}
}
Did select methods implementation ..
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dc = storyboard?.instantiateViewController(withIdentifier: "MovieDeatilsViewController") as! MovieDeatilsViewController
let row = indexPath.row
dc.titlemovie = presenter.getTitle(by: row) ?? ""
dc.overview = presenter.getOverview(by: row) ?? ""
let baseImageURL = presenter.getUrlImage(by: row)
dc.imagemovie = UIImage(data: presenter.getImageData(by: baseImageURL)!)
self.navigationController?.pushViewController(dc, animated: true)
}
}
Here is the screenshot of the applications .
When the user click check box I want to add those table view cell values into favourite segment control
Am example of what you need to achieve (The name of method and variables are not the exact ones, it just to help you understand the principle). In this case with only one table view:
struct MoviePresenter {
var nbRows: Int = 0
}
class MoviesVC {
var presenter = MoviePresenter()
var tableViewSelect : UISegmentedControl!
var movieTableView: UITableView!
var favoriteMovies = [Int]() // the list of row of favorite movies
// utility function to know if movie is favorite or not
func isInFavorites(_ row: Int) -> Bool {
return favoriteMovies.first(where: {$0 == row}) != nil
}
// Note : all table view datasource/delegate methods need to check
// which is the current selected display
func numberOfRowsInSection(_ section: Int) -> Int {
if tableViewSelect.selectedSegmentIndex == 0 {
return presenter.nbRows
} else {
return favoriteMovies.count
}
}
// in cellForRow
func cellForRow(indexPath: IndexPath) {
if tableViewSelect.selectedSegmentIndex == 0 {
// default display
let cell = UITableViewCell() // you dequeue your cell here
// init cell for presenter (by: row)
if isInFavorites(indexPath.row) {
// checkMark handling
}
// to handle checkmark
cell.tag = indexPath.row
} else {
// favorite list
let cell = UITableViewCell() // you dequeue your favorite cell here
let row = favoriteMovies[indexPath.row]
// if you want to handle checkmark
cell.tag = row
}
}
func checkMarkTappedInFavoriteCell(cell: UITableViewCell) {
let row = cell.tag
// set/unset favorite check mark when tapped
if isInFavorites(row) {
favoriteMovies.removeAll(where: {$0 == row})
} else {
favoriteMovies.append(row)
}
movieTableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .none)
}
// action to be executed when selected segment changes
#IBAction func tableViewSelectedIndexChanged(_ sender: UISegmentedControl) {
self.movieTableView.reloadData()
}
}
This only in the case you want only one table view. In case you want 2 table views the change in segmented control will also make one tableview visible and the other invisible.
I have a segment outlet in a tableview cell in a VC. There are two indexes: 1 and 2.
When I click on 2, I want to tell the collection view within another tableviewcell to reload another view.
And when I click back to 1, I want the same collection view to reload again and display the original content.
Here are my View Controller Functions:
class MyProfileTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,segment
{
//Variable selection to determine what is selected - 1 by default
var viewSelected = "1"
//Segment Function - viewSelected is used to tell VC what index it's on
func segmentSelected(tag: Int, type: String) {
if type == "1" {
print("1")
viewSelected = "1"
} else if type == "2" {
print("2")
viewSelected = "2"
}
}
//Cell For Row - tells tableviewcell to look at viewSelected
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = AboutTableView.dequeueReusableCell(withIdentifier: "ProfileSegmentTableViewCell", for: indexPath) as! ProfileSegmentTableViewCell
cell.segmentCell = self
return cell
} else {
let cell = AboutTableView.dequeueReusableCell(withIdentifier: "1_2Cell", for: indexPath) as! 1_2Cell
cell.viewSelected = viewSelected
return cell
}
Here is the Segment Control TableviewCell
//protocol used to delegate
protocol segment: UIViewController {
func segmentSelected(tag: Int, type: String)
}
class ProfileSegmentTableViewCell: UITableViewCell {
#IBOutlet weak var profileSegmentControl: UISegmentedControl!
var segmentCell: segment?
#IBAction func segmentPressed(_ sender: Any) {
profileSegmentControl.changeUnderlinePosition()
let Index = self.profileSegmentControl.selectedSegmentIndex
if Index == 0
{
segmentCell?.segmentSelected(tag: (sender as AnyObject).tag, type: "1")
)
} else {
segmentCell?.segmentSelected(tag: (sender as AnyObject).tag, type: "2")
}
}
CollectionView
//variable by default
var viewSelected = "1"
//viewDidLoad
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
cView.delegate = self
cView.dataSource = self
get {
self.cView.reloadData()
self.cView.layoutIfNeeded()
}
}
func get(_ completionHandler: #escaping () -> Void) {
getCount.removeAll()
if viewSelected = "1" {
print("1") } else {
print("2)
}
completionHandler()
}
Here's a very simple example of using a closure so your segmented-control cell can communicate with your table view controller.
Your cell class might look like this:
class ProfileSegmentTableViewCell: UITableViewCell {
#IBOutlet var profileSegmentControl: UISegmentedControl!
var callback: ((Int)->())?
#IBAction func segmentPressed(_ sender: Any) {
guard let segControl = sender as? UISegmentedControl else { return }
// tell the controller that the selected segment changed
callback?(segControl.selectedSegmentIndex)
}
}
When the user changes the selected segment, the cell uses the callback closure to inform the controller that a segment was selected.
Then, in your controller, you could have a var to track the currently selected segment index:
// track selected segment index
var currentIndex: Int = 0
and your cellForRowAt code would look like this:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
// first row - use cell with segemented control
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileSegmentTableViewCell", for: indexPath) as! ProfileSegmentTableViewCell
// set the segemented control's selected index
cell.profileSegmentControl.selectedSegmentIndex = self.currentIndex
// set the callback closure
cell.callback = { [weak self] idx in
guard let self = self else {
return
}
// update the segment index tracker
self.currentIndex = idx
// reload row containing collection view
self.tableView.reloadRows(at: [IndexPath(row: 1, section: 0)], with: .automatic)
}
return cell
} else if indexPath.row == 1 {
// second row - use cell with collection view
let cell = tableView.dequeueReusableCell(withIdentifier: "1_2Cell", for: indexPath) as! My_1_2Cell
// tell the cell which segment index is selected
cell.setData(currentIndex)
return cell
}
// all other rows - use simple Basic cell
let cell = tableView.dequeueReusableCell(withIdentifier: "PlainCell", for: indexPath) as! PlainCell
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
Here is a complete example you can run and examine: https://github.com/DonMag/ClosureExample
You can use NotificationCenter.default.addObserver... method and NotificationCenter.default.post..... Read about them. And don't forget to remove observers in deinit
I have a checkbox (UIButton) and a label in a UITableViewCell. I want to change the label's text (color + strikethrough) when I click on the checkbox.
This is for a Recipe Application. After a cooking step is done, the user can "check" it as done.
This is my current cellForRowAt Function for the tableView:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == groceryTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: groceryTableViewCell, for: indexPath) as! GroceryItemTableViewCell
cell.amoutLabel.text = indexPath.item % 2 == 0 ? "50 g" : "500 ml"
cell.itemLabel.text = indexPath.item % 2 == 0 ? "Cheese" : "Milk"
cell.selectionStyle = .none
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: cookingStepTableViewCell, for: indexPath) as! CookingStepTableViewCell
cell.cookingStepDescription.text = indexPath.item % 2 == 0 ? "Test 123..." : "Test 321..."
cell.selectionStyle = .none
cell.delegate = self
return cell
}
}
And this is my Button addTarget Function, which is delegated from the TableViewCell Class to the actual ViewController Class:
func cookingStepDone(description: String, isDone: Bool) {
// if isDone == true
// label textcolor is gray + strikethrough
// if isDone == false
// no change...
}
I want that cell.cookingStepDescription label is changed if "isDone" is true (= click on the checkbox)
Assuming that the button outlet is taken in cell class. so declare a action method in the cellForRowAtIndexpath i.e like this.
cell.yourDoneBtn?.addTarget(self, action: #selector(self.cookingStepDone), for: .touchUpInside)
Now in your action function:
#objc func cookingStepDone(sender: UIButton)
{
let location = self.yourTableViewName?.convert(sender.bounds.origin, from:sender)
let indexPath = self.yourTableViewName?.indexPathForRow(at: location!)
if let cell = self.yourTableViewName.cellForRow(at: indexPath!) as? yourTableViewCell // i.e groceryTableViewCell or CookingStepTableViewCell
{
if isDone == true
{
// Set your cell label textcolor to gray + strikethrough
}
else
{
// no change
}
}
DispatchQueue.main.async
{
self.yourTableView.reloadData() // reload your table view
}
}
Set your bool value where ever needed.
You can do this using below approach
define an Array, in you cookingStepDone method add indexPath to the array and if indexPath already in Array remove it and reload the tableView. and in cellForRowAtIndexpathmethod, check if the Array contains the indexPath. if contains make text strikeThrough else make normal.
What if you create a new class whose superclass would be UITableViewCell and inside that class you add in your #IBOutlets (UIButton and UILabel) and an #IBAction (buttonWasTapped)?
Something Like:
class RecipeTableViewCell: UITableViewCell {
#IBOutlet var myButton : UIButton!
#IBOutlet var myLabel : UILabel!
#IBAction func didTouchButton(sender : UIButton)
{
myLabel.textColor = UIColor.green;
}
}
Checkout this code : RecipeTableViewCell
class RecipeTableViewCell: UITableViewCell {
#IBOutlet var myButton : UIButton!
#IBOutlet var myLabel : UILabel!
var buttonClick : (() -> Void)? = nil
override func awakeFromNib() {
myButton.addTarget(self, action: #selector(didTouchButton(sender:)), for: .touchUpInside)
}
#IBAction func didTouchButton(sender : UIButton)
{
if let action = buttonClick {
action()
}
}
}
In cellForRowAt
let cell = tableView.dequeueReusableCell...
// Your code ...
cell.buttonClick = {
//access your label and data from here
cell.yourLbl.text = yourModel[indexPath.row].text
}
I have a ViewController with a TableView and a TableViewCell containing multiple sections and rows.
I have 2 button "plus" and "minus" and a label "totalLabel" in each row.
How can I get the value displayed in the label for each specific row when the user presses the + or - button?
for now when I run the app and press the + or - buttons only the totalLabel of the section 0/row 0 is working while random values just appear and disappear in the other sections/rows
my tableViewCell code :
import UIKit
protocol CommandeCellDelegate: class {
}
class CommandeCell: UITableViewCell {
weak var delegate : CommandeCellDelegate!
#IBOutlet weak var drinksLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var plusButton: UIButton!
#IBOutlet weak var minusButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
here is my code for cellForRowAt :
class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CommandeCellDelegate {
var count : Int = 0
var countValue : String!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommandeCell", for: indexPath) as! CommandeCell
cell.plusButton.tag = indexPath.section
cell.plusButton.tag = indexPath.row
cell.plusButton.addTarget(self, action: #selector(self.increaseValue), for: .touchUpInside)
cell.minusButton.tag = indexPath.section
cell.minusButton.tag = indexPath.row
cell.minusButton.addTarget(self, action: #selector(self.decreaseValue), for: .touchUpInside)
if indexPath.section == 0 {
let softInfo = softs[indexPath.row]
cell.drinksLabel?.text = softInfo.drinkName
cell.totalLabel?.text = // how to display countValue here?
let HappyHourStatus = partner!.barHHStatus
if case "0" = HappyHourStatus {
cell.priceLabel?.text = softInfo.drinkHHPrice
} else
if case "1" = HappyHourStatus {
cell.priceLabel?.text = softInfo.drinkPrice
}
}
else if indexPath.section == 1 {
let cocktailInfo = cocktails[indexPath.row]
cell.drinksLabel?.text = cocktailInfo.drinkName
cell.totalLabel?.text = // how to display countValue here?
let HappyHourStatus = partner!.barHHStatus
if case "0" = HappyHourStatus {
cell.priceLabel?.text = cocktailInfo.drinkHHPrice
} else
if case "1" = HappyHourStatus {
cell.priceLabel?.text = cocktailInfo.drinkPrice
}
}
return cell
}
and my funcs to increase or decrease the value :
func increaseValue(_ sender: UIButton) -> Int {
count = 1 + count
print(count)
countValue = "\(count)"
let rowToReload = IndexPath(row: sender.tag, section: sender.tag)
let rowsToReload: [Any] = [rowToReload]
tableView.reloadRows(at: rowsToReload as! [IndexPath], with: .automatic)
return count
}
func decreaseValue(_ sender: UIButton) -> Int {
if count == 0 {
print("Count zero")
} else {
count = count - 1
}
countValue = "\(count)"
let rowToReload = IndexPath(row: sender.tag, section: sender.tag)
let rowsToReload: [Any] = [rowToReload]
tableView.reloadRows(at: rowsToReload as! [IndexPath], with: .automatic)
return count
}
I have tried countless solutions but so far none is working - thank you for your help!
So your problem is this code
cell.plusButton.tag = indexPath.section
cell.plusButton.tag = indexPath.row
A tag can only store one value. So you are overriding the section with the row. So it is going to cause all sorts of weirdness. The better solution is to determine what cell you are targeting based on the button itself. Since you know what button was clicked you can convert the location of this button to a point on the table view. And then that point to a a particular index path.
So using your example code you can do something like below:
var softsCount: [Int] = []
var cocktailsCount: [Int] = []
override func viewDidLoad() {
super.viewDidLoad()
softsCount = Array(repeating: 0, count: softs.count) // Fill an array with 0
cocktailsCount = Array(repeating: 0, count: cocktails.count) // Fill an array with 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
if indexPath.section == 0 {
...
cell.totalLabel?.text = "\(softsCount[indexPath.row])"
...
} else if indexPath.section == 1 {
...
cell.totalLabel?.text = "\(cocktailsCount[indexPath.row])"
...
}
...
}
func increaseValue(_ sender: UIButton) {
let pointInTable = sender.convert(sender.bounds.origin, to: tableView)
if let indexPath = self.tableView.indexPathForRow(at: pointInTable), let cell = tableView.cellForRow(at: indexPath) {
if indexPath.section == 0 {
softsCount[indexPath.row] += 1
cell.totalLabel?.text = "\(softsCount[indexPath.row])"
} else if indexPath.section == 1 {
cocktailsCount[indexPath.row] += 1
cell.totalLabel?.text = "\(cocktailsCount[indexPath.row])"
}
}
}
No sure why you are returning count. I am sure this is just a partial implementation. But the button should take care of the entire action including updating the label with the new count. You don't normally return values from button presses.
So updated the example to update the label with the current count. Since I am unable to see what your drinks object I made an assumption that the drinks class has a count parameter that starts at 0. This way each individual drink has a count assigned to it.
I put stepper both outlets and action into tableview cell and using protocol delegate to connect it to tableview. When i tapped stepper in first row, stepper value appear normaly in first row but its also appear in some random row. how to fix this?
TableViewCell
protocol ReviewCellDelegate{
func stepperButton(sender: ReviewTableViewCell)
}
class ReviewTableViewCell: UITableViewCell {
#IBOutlet weak var countStepper: UIStepper!
#IBOutlet weak var stepperLabel: UILabel!
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 stepperButtonTapped(sender: UIStepper) {
if delegate != nil {
delegate?.stepperButton(self)
stepperLabel.text = "x \(Int(countStepper.value))"
}
}
ViewController
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "reviewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ReviewTableViewCell
var imageView: UIImageView?
let photoG = self.photos[indexPath.row]
imageView = cell.contentView.viewWithTag(1) as? UIImageView
//let layout = cell.goodiesImage
let tag = indexPath.row // +1
cell.tag = tag
photoG.fetchImageWithSize(CGSize(width: 1000, height: 1000), completeBlock: { image, info in
if cell.tag == tag {
imageView?.image = image
cell.goodiesImage.image = image
}
})
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
}
}
Reset the value of stepper while loading your cell. you can reset the cell property values in cell's prepareForReuse method. add the following method in your ReviewTableViewCell class.
override func prepareForReuse()
{
super.prepareForReuse()
countStepper.value = 0.0
}
In tableViewCell VC:
1 - add these field
var cellDelegate: cellProtocol?
var index: IndexPath?
2 - then add this in the delegate:
func onStepperClick(index: Int, sender: UIStepper)
3 - when you have dragged your stepper over as an action use this:
#IBAction func cellStepper(_ sender: UIStepper) {
cellDelegate?.onStepperClick(index: (index?.row)!, sender: sender)
sender.maximumValue = 1 //for incrementing
sender.minimumValue = -1 //for decrementing
//this will make sense later
}
In ViewController
1 - add these to the tableView function that has the cellAtRow variable.
cell.cellDelegate = self
cell.index = indexPath
2 - Use this instead of your stepperButton function
func onStepperClick(index: Int, sender: UIStepper) {
print(index)
if sender.value == 1.0{
//positive side of stepper was pressed
}else if sender.value == -1.0{
//negative side of stepper was pressed
}
sender.value = 0 //resetting to zero so sender.value produce different values on plus and minus
}
Hope this works for you
As mentioned by #A-Live, your component is being reused and so need to be updated.
So in your view controller:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "reviewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ReviewTableViewCell
var imageView: UIImageView?
let photoG = self.photos[indexPath.row]
imageView = cell.contentView.viewWithTag(1) as? UIImageView
//let layout = cell.goodiesImage
let tag = indexPath.row // +1
cell.tag = tag
photoG.fetchImageWithSize(CGSize(width: 1000, height: 1000), completeBlock: { image, info in
if cell.tag == tag {
imageView?.image = image
cell.goodiesImage.image = image
}
})
cell.countStepper.value = XXX[indexPath.row].value; //Here you update your view
cell.stepperLabel.text = "x \(Int(cell.countStepper.value))" //And here
And
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
XXX[sender.tag].value = sender.counterStepper.value //Here you save your updated value
}
NOTE:
1.MY Cell class is just normal..All changes are in viewcontroller class
2.I have taken stepper and over it added ibAddButton with same constraint as ibStepper
class cell: UITableViewCell {
#IBOutlet weak var ibAddButton: UIButton!
#IBOutlet weak var ibStepper: UIStepper!
#IBOutlet weak var ibCount: UILabel!
#IBOutlet weak var ibLbl: UILabel!
}
1.define empty int array [Int]()
var countArray = [Int]()
2.append countArray with all zeros with the number of data u want to populate in tableview
for arr in self.responseArray{
self.countArray.append(0)
}
3.in cell for row at
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! cell
let dict = responseArray[indexPath.row] as? NSDictionary ?? NSDictionary()
cell.ibLbl.text = dict["name"] as? String ?? String()
if countArray[indexPath.row] == 0{
cell.ibAddButton.tag = indexPath.row
cell.ibStepper.isHidden = true
cell.ibAddButton.isHidden = false
cell.ibCount.isHidden = true
cell.ibAddButton.addTarget(self, action: #selector(addPressed(sender:)), for: .touchUpInside)
}else{
cell.ibAddButton.isHidden = true
cell.ibStepper.isHidden = false
cell.ibStepper.tag = indexPath.row
cell.ibCount.isHidden = false
cell.ibCount.text = "\(countArray[indexPath.row])"
cell.ibStepper.addTarget(self, action: #selector(stepperValueChanged(sender:)), for: .valueChanged)}
return cell
}
4.objc functions
#objc func stepperValueChanged(sender : UIStepper){
if sender.stepValue != 0{
countArray[sender.tag] = Int(sender.value)
}
ibTableView.reloadData()
}
#objc func addPressed(sender : UIButton){
countArray[sender.tag] = 1//countArray[sender.tag] + 1
ibTableView.reloadData()
}