Updated Code Below
I am working on comment cells who are limited to 100 characters and if they contain more a "show more button" will show up.
If pressed, the exact cell should reload itself with the number of lines changed to 0 and fully display the cell, no matter how big.
What I have achieved is that cells reload, but not the selected one and kinda arbitrary.
Below is my code for the enlarging process
NOTE: Updatet Code for My Function
Problem: I have to press the button twice to get the result, to minimize and to maximize the cell
#IBAction func readMore(_ sender: UIButton) {
self.state = !self.state
print("state" , state)
self.tapMore.setTitle(self.state ? self.decreaseState: self.expandState, for: .normal)
self.commentLabel.numberOfLines = (self.state ? self.expandedLines: self.numberOfLines)
print(self.commentLabel.numberOfLines)
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.parentViewControllerCommentCell?.tableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
}
The index comes from
extension CommentTableViewCell {
var indexPath: IndexPath? {
return (superview as? UITableView)?.indexPath(for: self)
}
}
Note
The print statement prints out the chosen cell ( e.g. [0, 1] or [0,0] but it doesn't change then.
Whereas I hardcode my code and change
let myIndexPath = IndexPath(row: indexPath!.row, section: 0)
to
let myIndexPath = IndexPath(row: 0, section: 0)
The feature works, but arbitrarily reloads some cells and arbitrarily enlarges and decreases the cell.
In the variable version with row: indexPath!.row the lines state doesn't change as well, whereas with hardcoded the lines change between 3 and 0.
Thanks for your help :)
Addition
my commentCell
class CommentTableViewCell: UITableViewCell {
#IBOutlet weak var likeCountButton: UIButton!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var commentLabel: KILabel!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var tapMore: UIButton!
#IBOutlet weak var tapMoreButton: UIButton!
var delegate: CommentTableViewCellDelegate?
var postId : String!
Here is a better approach to get you the correct index path. First, in your cellForRow method, add the current index row as tag to your show more button, and then add click action to your button handler function.
Add an outlet of UIButton in you custom UITableViewCell class as
class CustomCell: UITableViewCell {
#IBOutlet var moreButton: UIButton! // Connect your button from storyboard
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.moreButton.tag = indexPath.row
/* Just add action normally from storyboard. No need to add target. cell.moreButton.addTarget(self, action:#selector(buttonUp(sender:)), for: .touchUpInside) */
return cell
}
Then in your handler function, you can get the correct index path by reading this tag
func tapForMore(sender: UIButton) {
let myIndexPath = IndexPath(row: sender.tag, section: 0)
print("myindex", myIndexPath)
//... other code here
}
You take class variable and track tap counts. Depending on these variables you can increase or decrease size of cell and reload it.
In YOURViewController declare variables as:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var CommentsTableView: UITableView!
var defaultSizeOfCell = 60.0
var newSize = 80.0
var selectedIndex = -1
var isExpanded = false
var expandCounter = 0
override func viewDidLoad() { ...
Connect button in cell to this action:
#IBAction func moreButtonAction(_ sender: UIButton) {
if !isExpanded {
if expandCounter == 0 {
expandCounter = expandCounter + 1
} else if expandCounter == 1 {
expandCounter = 0
isExpanded = true
selectedIndex = sender.tag
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.CommentsTableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
print("Increase")
}
} else if isExpanded {
if expandCounter == 0 {
expandCounter = expandCounter + 1
} else if expandCounter == 1 {
expandCounter = 0
isExpanded = false
selectedIndex = -1
let myIndexPath = IndexPath(row: sender.tag, section: 0)
UIView.animate(withDuration: 0.3, animations: {
self.CommentsTableView.reloadRows(at: [myIndexPath], with: UITableViewRowAnimation(rawValue: Int(UITableViewAutomaticDimension))!)
})
print("Decrease")
}
}
}
In tableview datasource function add tag to button:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath) as! TestTableViewCell
cell.moreButton.tag = indexPath.row
return cell
}
And finally add this delegate method for height of cells:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if selectedIndex == indexPath.row {
return CGFloat(newSize)
} else {
return CGFloat(defaultSizeOfCell)
}
}
Not to mention, button should be in cell and connected to YOURCustomTableViewCell class as:
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var moreButton: UIButton!
I have tested it against your requirements.
Related
I am trying to achieve functionality similar to Apple's reminders app where a tableview holds all the reminders and a + button at the end adds a new object.
My objects are held in an array called tempActions, which is the data source for the tableView.
Pressing 'Add Action' appends a new object to the array with the title "Empty Cell".
The title is a UITextView which users will be able to edit, but here's what I can't figure out how to do:
How do I take the text from the UITextView of that particular cell, append it to the array at the correct index (the index corresponds to indexPath.row) and then display it in the cell.label?
I thought of using the textViewDidEndEditing method but what I don't know how to do is reference the correct cell from the cellForRowAt method.
Would anyone be able to help clarify this, or am I approaching it in the wrong way?
Here's the code for the entire class:
class Step3: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate {
// Outlets
#IBOutlet weak var sectionText: UILabel!
#IBOutlet weak var sectionHeader: UILabel!
#IBOutlet weak var teableViewHeight: NSLayoutConstraint!
#IBOutlet weak var tableview: UITableView!
#IBAction func addAction(_ sender: Any) {
tempActions.append(Action(title: "Empty Cell", completed: false))
tableview.reloadData()
tableview.layoutIfNeeded()
teableViewHeight.constant = tableview.contentSize.height
print(tempActions)
}
#IBAction func nextAction(_ sender: Any) {
let newGoal = Goal(
title: tempTitle,
description: tempDescription,
duration: tempDuration,
actions: nil,
completed: false
)
newGoal.save()
performSegue(withIdentifier: "ToHome", sender: nil)
}
func textViewDidEndEditing(_ textView: UITextView) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tempActions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ActionCell", for: indexPath) as! ActionCell
cell.label.text = tempActions[indexPath.row].title
cell.label.textContainerInset = UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0);
cell.label.delegate = self
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableview.estimatedRowHeight = 40
tableview.rowHeight = UITableView.automaticDimension
}
}
Thanks in advance
If I understand it -- the textView is in a cell, and you want to find that cell in textViewDidEndEditing. If the superview of the textfield is the cell, you could do this:
func textViewDidEndEditing(_ textView: UITextView) {
if let cell = textView.superview as? ActionCell,
let indexPath = tableView.indexPath(for: cell) {
// Now you have the indexPath of the cell
// update tempActions
// YOUR CODE HERE
// Then reloadRows
tableView.reloadRows(at: [indexPath]), with: .automatic)
}
}
Another thing you could do is make tempAction's type have a unique ID and then store that in the ActionCell -- when you want to find the index, look up the ID in the tempActions array to find its index.
I am trying to increase label value by 1 on button click which is inside custom table cell. I am able to change values differently in different cells but the problem is if I tap + in first cell label changes from 0 to 1, when I tap + in second cell label directly changes from 0 to 2 and so on in other cells. How can I fix this?
var counts = 0
#IBAction func addPressed(_ sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: 0)
let cell = tblview.cellForRow(at: indexPath) as! HomeDetailsTableViewCell
counts = (counts + 1)
cell.lblNoOfItems.text = "\(counts)"
}
There can be 2 ways to achieve this:
1. Keep the count of each cell in an Array at the controller level and each time you press the button, get the count from array and use that, i.e.
var counts = [Int](repeating: 0, count: 10) //10 is the number of cells here
#IBAction func addPressed(_ sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: 0)
let cell = tblview.cellForRow(at: indexPath) as! HomeDetailsTableViewCell
counts[indexPath.row] += 1
cell.lblNoOfItems.text = "\(counts[indexPath.row])"
}
2. Keep the count of each cell in the custom cell itself, i.e. in HomeDetailsTableViewCell, i.e.
class HomeDetailsTableViewCell: UITableViewCell {
var count = 0
//Rest of the code...
}
#IBAction func addPressed(_ sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: 0)
let cell = tblview.cellForRow(at: indexPath) as! HomeDetailsTableViewCell
cell.count += 1
cell.lblNoOfItems.text = "\(cell.count)"
}
Also, the way you are implementing the code for adding + button is not correct. You must implement it within the HomeDetailsTableViewCell itself, i.e.
class HomeDetailsTableViewCell: UITableViewCell {
#IBOutlet weak var lblNoOfItems: UILabel!
var count = 0
#IBAction func addPressed(_ sender: UIButton) {
self.count += 1
self.lblNoOfItems.text = "\(self.count)"
}
//Rest of the code...
}
You can get this question's answer using below code in Swift 5
// 1st Step
Inside Your TableViewCell
import UIKit
#objc protocol CellDelagate : NSObjectProtocol {
func addPressed(_ cell: TableViewCell)
}
class TableViewCell: UITableViewCell {
var cellDelagate: CellDelagate?
var indexPath : IndexPath?
#IBOutlet weak var gPaybutton: UIButton!
#IBOutlet weak var proceesingLabel: UILabel!
var count = 0
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func proccessingButtonTapped(_ sender: UIButton) {
self.cellDelagate!.addPressed(self)
if gPaybutton.tag == indexPath?.row {
}
}
}
// 2nd Step
Inside Your ViewController
class ViewController: UIViewController {
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate, CellDelagate {
func addPressed(_ cell: TableViewCell) {
cell.count += 1
cell.proceesingLabel.text = "Processing : \(cell.count)"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell")! as? TableViewCell//1.
cell?.indexLabel?.text = yourArray[indexPath.row] //2.
cell?.cellDelagate = self //3.
cell?.tag = indexPath.row //4.
return cell! //5.
}
}
This is because you're using a global variable for all the cells in your class if you want to maintain the count differently for each cell you have to use different variables for each cell, I'm just giving you the directions so find your way .
Create a data source for your count and initialise it with the same datasource you are using for your table like, suppose you have a array of string for your tableview data source lets create list array that contains your title and count with it. I'm giving you all the steps in my solution you just have to put the code at the right place : -
//Suppose this is your tableivew data source
let list : [String] = ["Title1","Title2","Title3"]
//Make a dictionary that is going to be your datasource of tableview holding the count too
var listDict : [[String:Any]] = []
for item in list{
let dict : [String:Any] = ["title":item,"count":Int(0)]
listDict.append(dict)
}
//Where you are performing the actions like in didSelectRow or in any button
let cellIndexPath : IndexPath = IndexPath(item: 0, section: 0) // Get the indexPath of Cell
//Update the count
if let count = listDict[cellIndexPath.row]["count"] as? Int{
let newValue = count + 1
listDict[cellIndexPath.row]["count"] = newValue
}
//Reload the tableview cell of update manually your choice I'm reloading the cell for short
let tableview = UITableView()
tableview.reloadRows(at: [cellIndexPath], with: .fade)
so I am creating a table view with segmented control. at first - I didn't do segmented control but modified some code to at it. When I add numbers to my array, it is not actually populating the cells of the table view. In the table view every time I click a button that appends an amount to the array, the lines increase, but no data is shown. So I know it is working, but it is just not actually displaying the numbers in the table view cells. is there a specific reason why my code will not populate the views?
class SpendingHistoryViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var savedInformation = false
var newDefault : Double?
var selectedSegment = 1
let array2 = ["1","2","3"]
let userDefaults = UserDefaults.standard
#IBOutlet weak var tableViewSpending: UITableView!
#IBOutlet weak var youSpentLabel: UILabel!
#IBOutlet weak var youSavedLabel: UILabel!
#IBOutlet weak var segmentControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
self.tableViewSpending.delegate = self
self.tableViewSpending.dataSource = self
//GETS user defaults that was set in ADDSUBTRACTMONEY VC - and then sets the default to Array- then gets total
let moneySpentArray = UserDefaults.standard.array(forKey: "moneySpent") as? [Double] ?? [Double]()
AddSubtractMoneyController.moneySpentArray = moneySpentArray
let arraySum = moneySpentArray.reduce(0.0, +)
youSpentLabel.text = String(format: "%.2f", arraySum)
}
#IBAction func spentSavedAction(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
selectedSegment = 1
}
else {
selectedSegment = 2
}
self.tableViewSpending.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if selectedSegment == 1 {
return(AddSubtractMoneyController.moneySpentArray.count)
}
else {
return array2.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
//let cell2 = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell2")
let cell2 = tableViewSpending.dequeueReusableCell(withIdentifier: "cell2")! as UITableViewCell
let cell = tableViewSpending.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
cell.textLabel?.text = String(format: "%.2f", AddSubtractMoneyController.moneySpentArray[indexPath.row])
cell2.textLabel?.text = array2[indexPath.row]
self.tableViewSpending.reloadData()
if selectedSegment == 1 {
return cell
}
else {
return cell2
}
}
}
Can Anyone Suggest me an idea to create a UI like the below screenshot
https://drive.google.com/open?id=0B2JNfyRRcL0Gb2huQ3ZTV2N6Q1E
I used
ScrollView
imageView
Lable
TableView
Lable
Button
UiView
CollectionView
ImageView
Button
labels…
with this when I’m trying to scroll to down, only the UIScrollView is Scrolling not the UICollectionView.
I Also try to give the UiCollectionView height to its content height and made the scrollview height to fit that. Once i did this the collection view’s datasource and delegate methods are not calling when i use to scroll (means the first time when viewDidLoad - it loads the collection view cell) with the below below scene example the datasource and delegate methods are called only for the first two cells, since it is visible on the screen without scrolling whereas the others are not.
Im thinking to try it with tableview but i don’t know how…. because
the first content which is the banner image may not be in all categories (it may be in JSon response or may not be)
The Explore more label is static lable
The third thing “ Clothing, Bags belts and Wallets “ are the tableviewView Cells (cell count may be differ based on categories)
the forth is a Uiview which has the filter and sorting buttons and its a static one
the last is a collectionView cells (cell count may be differ based on categories)
So, Please Suggest me an Idea or an example or a sample code are highly welcomed….. thanks in Advance….!
Finally I found an idea,
I kept all within a TableView,
The first two (the banner image and the explore lable are the first cell of my tableview)
The second PrototypeCell is the category Titles(where the cloths, bags and belts)
The Third Prototype cell is a what with the filter and sort buttons and I used this cell as section Header View
And finally the last prototype cell is a one which have the collection View and its cell (I just design it and add it into a cell)
And sample code is as follows,
My CollectionViewCell
import UIKit
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgFav: UIImageView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var pinImage: UIImageView!
#IBOutlet weak var priceLbl: UILabel!
#IBOutlet var splPriceLbl: UILabel!
#IBOutlet weak var addToFav: UIButton!
override func awakeFromNib()
{
super.awakeFromNib()
self.contentView.autoresizingMask = [UIViewAutoresizing.flexibleRightMargin, UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleBottomMargin, UIViewAutoresizing.flexibleTopMargin]
self.contentView.translatesAutoresizingMaskIntoConstraints = true
}
}
My TableViewCell
import UIKit
class SubCategoryTableViewCell: UITableViewCell {
#IBOutlet weak var productListCollectVw: UICollectionView!
#IBOutlet weak var btnSort: UIButton!
#IBOutlet weak var btnFilter: UIButton!
#IBOutlet weak var subCategryTitle: UILabel!
#IBOutlet weak var lblExplore: UILabel!
#IBOutlet weak var imgBanner: UIImageView!
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
}
}
And Finally My ViewController
#IBOutlet weak var mainTableView: UITableView!
var subCategoryAry2 = NSMutableArray()
var imageUrl:URL!
var imageUrlStr:String = ""
var productListAry:NSMutableArray = []
func numberOfSections(in tableView: UITableView) -> Int
{
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if section == 0
{
return subCategoryAry2.count + 1
}
else
{
return 1
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
if section == 0
{
return nil
}
else
{
let CellIdentifier: String = "section2Cell"
let headerView: SubCategoryTableViewCell? = tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as! SubCategoryTableViewCell?
headerView?.btnFilter.addTarget(self, action: #selector(self.filterAction(_:)), for: .touchUpInside)
headerView?.btnSort.addTarget(self, action: #selector(self.sortAction(_:)), for: .touchUpInside)
if headerView == nil
{
print("No cells with matching CellIdentifier loaded from your storyboard")
}
return headerView!
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.section == 0
{
if indexPath.row == 0
{
let cell:SubCategoryTableViewCell = self.mainTableView.dequeueReusableCell(withIdentifier: "bannerCell") as! SubCategoryTableViewCell!
if self.imageUrlStr == "no_image.png" || self.imageUrlStr == ""
{
cell.imgBanner.isHidden = true
cell.imgBanner.frame.size.height = 0
cell.lblExplore.frame.origin.y = 0
}
else
{
cell.imgBanner.isHidden = false
let imageUrl1 = "\(self.imageUrl!)"
let trimmedUrl = imageUrl1.trimmingCharacters(in: CharacterSet(charactersIn: "")).replacingOccurrences(of: " ", with: "%20") as String
cell.imgBanner.sd_setImage(with: URL(string: trimmedUrl), completed: { (image, error, imageCacheType, imageUrl) in
if image != nil
{
}
else
{
cell.imgBanner.isHidden = true
cell.imgBanner.frame.size.height = 0
cell.lblExplore.frame.origin.y = 0
}
})
}
return cell
}
else //if indexPath.row == 1
{
let cell:SubCategoryTableViewCell = self.mainTableView.dequeueReusableCell(withIdentifier: "listCell") as! SubCategoryTableViewCell!
cell.subCategryTitle.text = (subCategoryAry2.object(at: (indexPath as NSIndexPath).row - 1) as AnyObject).value(forKey: "name") as? String
return cell
}
}
else
{
let cell:SubCategoryTableViewCell = (self.mainTableView.dequeueReusableCell(withIdentifier: "collectionCell") as? SubCategoryTableViewCell!)!
// Load Your CollectionView
cell.productListCollectVw.dataSource = self
cell.productListCollectVw.delegate = self
cell.productListCollectVw.reloadData()
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if indexPath.section == 0
{
if indexPath.row != 0
{
//***************** Do whatever you need to do if user did selected the row but remain the indexpath.row or indexpath.row - 1 *****************//
subCategoryID = ((subCategoryAry2.object(at: (indexPath as NSIndexPath).row - 1) as! NSObject).value(forKey: "category_id") as? String)! as NSString
print("tableView - didSelectRowAt \(indexPath.row)")
}
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.section == 0
{
var height = CGFloat()
if indexPath.row == 0
{
if self.imageUrlStr == "no_image.png" || self.imageUrlStr == ""
{
//***************** Reduce banner image height if it is nil *****************//
height = 38
}
else
{
height = 175 + 38
}
}
else
{
height = 44
}
return height
}
else
{
let height = (255 * self.productListAry.count/2) + (2 * (self.productListAry.count) + 4)
//***************** increase height as per your need *****************//
return CGFloat(height)
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
if section == 0
{
return 0
}
else
{
return 44
}
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int
{
return self.productListAry.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.reuseIdentifier, for: indexPath) as! CollectionViewCell
cell.title.text = (self.productListAry.object(at: (indexPath as NSIndexPath).row) as! NSDictionary).value(forKey: "name") as? String
//***************** Load your cell as per your need *****************//
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
print("didSelectItemAt \(indexPath.row)")
}
My Storyboard will look like the screenshot added
https://drive.google.com/file/d/0B2JNfyRRcL0GYjJsckpyaGpoMkE/view?usp=sharing
Another Swift beginner here. I simply want a Stepper in each of my TableView cells that increments a label in the same cell.
I have found a couple of questions on this topic, but they include other elements and I haven't been able to extract the basic concept.
Swift Stepper Action that changes UITextField and UILabel within same cell
Stepper on tableview cell (swift)
So far I have connected IBOutlets for my Label and Stepper, as well as an IBAction for my Stepper in my cell class.
class BuyStatsCell: UITableViewCell{
//these are working fine
#IBOutlet weak var category: UILabel!
#IBOutlet weak var average: UILabel!
#IBOutlet weak var price: UILabel!
//Outlet for Label and Stepper - How do I make these work?
#IBOutlet weak var purchaseAmount: UILabel!
#IBOutlet weak var addSubtract: UIStepper!
//Action for Stepper - And this?
#IBAction func stepperAction(_ sender: UIStepper) {
self.purchaseAmount.text = Int(sender.value).description
}
}
And I understand the concept of reusing the cell in the cellForRowAt indexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BuyStatsTabCell", for: indexPath) as! BuyStatsCell
cell.isUserInteractionEnabled = false
//these are working
cell.category.text = categories[indexPath.row]
cell.price.text = String(prices[indexPath.row])
cell.average.text = String(averages[indexPath.row])
//but is there something I need to add here to keep the correct Stepper and Label for each class?
return cell
}
One of the already asked questions includes a protocol and another function in the ViewController like this
protocol ReviewCellDelegate{
func stepperButton(sender: ReviewTableViewCell)
}
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
}
}
I don't know if this is the approach I should be trying to take. I am looking for the simplest solution, but I am having trouble putting the pieces together.
Any help is appreciated.
Thanks.
Easiest solution (simplyfied):
Create a model BuyStat with a property purchaseAmount (it's crucial to be a class).
You are strongly discouraged from using multiple arrays as data source
class BuyStat {
var purchaseAmount = 0.0
init(purchaseAmount : Double) {
self.purchaseAmount = purchaseAmount
}
}
In the view controller create a data source array
var stats = [BuyStat]()
In viewDidLoad create a few instances and reload the table view
stats = [BuyStat(purchaseAmount: 12.0), BuyStat(purchaseAmount: 20.0)]
tableView.reloadData()
In the custom cell create a property buyStat to hold the current data source item with an observer to update stepper and label when buyStat is set
class BuyStatsCell: UITableViewCell {
#IBOutlet weak var purchaseAmount: UILabel!
#IBOutlet weak var addSubtract: UIStepper!
var buyStat : BuyStat! {
didSet {
addSubtract.value = buyStat.purchaseAmount
purchaseAmount.text = String(buyStat.purchaseAmount)
}
}
#IBAction func stepperAction(_ sender: UIStepper) {
buyStat.purchaseAmount = sender.value
self.purchaseAmount.text = String(sender.value)
}
}
In cellForRowAtIndexPath get the data source item and pass it to the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BuyStatsTabCell", for: indexPath) as! BuyStatsCell
cell.buyStat = stats[indexPath.row]
return cell
}
The magic is: When you are tapping the stepper the label as well as the data source array will be updated. So even after scrolling the cell will get always the actual data.
With this way you don't need protocols or callback closures. It's only important that the model is a class to have reference type semantics.
NOTE: MY Cell class is just normal..All changes are in viewcontroller class
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()
}