Found nil when trying to read from table cell in Swift - iphone

I have a problem I do not understand since I am new to iPhone app programming and Swift.
I have a TableView in Swift which I use to display some results on. I have added a button to each cell such that the user can choose different cells and then press delete in order to delete the results shown in the cell.
For just a few number of results that worked just fine but now I have started to get nil-exception.
The program crashes in the function getIndexToDelete when I try to get the cell for a certain row.
Here is the code where I handle the table:
import UIKit
class DailyResultTable: UIViewController, UITableViewDataSource, UITableViewDelegate {
var results = Results()
var yearShownIndex = 0
var monthShownIndex = 0
var dayShownIndex = 0
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var DeleteButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// self.view.backgroundColor = UIColor(patternImage: UIImage(named: "StatisticBackground")!)
}
// DataSource
func numberOfSectionsInTableView(tableview: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return (self.results?.getDayList(yearShownIndex, posMonth: monthShownIndex).count)!
print("return number of rows")
if (results.getYearList().count > 0 ){
if (results.getMonthList(yearShownIndex).count > 0){
if (results.getDayList(yearShownIndex, posMonth: monthShownIndex).count > dayShownIndex){
return (results.getDayList(yearShownIndex, posMonth: monthShownIndex)[dayShownIndex].results.count)
}
}
}
print("No numbers to show return 0")
return 0;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DayResultCell", forIndexPath: indexPath) as! ResultTableCell
let row = indexPath.row
//cell.ResultField.text = String((results?.getDayList(yearShownIndex, posMonth: monthShownIndex)[row].day)!) + "/" + String((results?.getMonthList(yearShownIndex)[monthShownIndex].month)!)
let res = results.getDayList(yearShownIndex, posMonth: monthShownIndex)[dayShownIndex].results[row].result
let maxRes = results.getDayList(yearShownIndex, posMonth: monthShownIndex)[dayShownIndex].results[row].maxresult
let discipline = results.getDayList(yearShownIndex, posMonth: monthShownIndex)[dayShownIndex].results[row].discipline
let text1 = String(res) + "/"
let text2 = String(maxRes)
let text3 = " - " + discipline
let text = text1 + text2 + text3
print(text)
cell.ResultField.text = text
return cell
}
// Delegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
#IBAction func CheckBox(sender: UIButton) {
let image = UIImage(named: "Selected") as UIImage!
let selected = sender.selected
sender.selected = !selected
sender.setImage(image, forState: .Selected)
}
func getIndexToDelete()->[Int]{
var indices = [Int]()
for i in 0..<tableView.numberOfRowsInSection(0){
let indexPath = NSIndexPath(forRow: i, inSection: 0)
// Here does the program crash
let cell = tableView.cellForRowAtIndexPath(indexPath) as! ResultTableCell
if (cell.CheckBoxButton.selected){
indices += [i]
}
}
return indices
}
#IBAction func DeletePressed(sender: UIButton) {
let deleteIndices = getIndexToDelete()
var goback = false;
var count = 0;
for index in deleteIndices{
print("Count: " + String(count))
results.ListResults[yearShownIndex].months[monthShownIndex].day[dayShownIndex].results.removeAtIndex(index-count)
count += 1
print(String((results.getDayList(yearShownIndex, posMonth: monthShownIndex)[dayShownIndex].results.count)));
}
loadView()
results.recreatePlainResult()
results.saveResults()
if (results.ListResults[yearShownIndex].months[monthShownIndex].day[dayShownIndex].results.count == 0){
print(String(results.ListResults[yearShownIndex].months[monthShownIndex].day.count))
results.ListResults[yearShownIndex].months[monthShownIndex].day.removeAtIndex(dayShownIndex)
results.recreatePlainResult()
results.saveResults()
print(String(results.ListResults[yearShownIndex].months[monthShownIndex].day.count))
goback = true
}
if (results.ListResults[yearShownIndex].months[monthShownIndex].day.count == 0){
results.ListResults[yearShownIndex].months.removeAtIndex(monthShownIndex)
results.recreatePlainResult()
results.saveResults()
goback = true
}
if (results.ListResults[yearShownIndex].months.count == 0){
results.ListResults.removeAtIndex(monthShownIndex)
results.recreatePlainResult()
results.saveResults()
goback = true
}
if (goback){
// return;
navigationController?.popViewControllerAnimated(true)
}
}
}
Here is the ResultTableCell:
import UIKit
class ResultTableCell: UITableViewCell {
#IBOutlet weak var ResultField: UITextField!
#IBOutlet weak var CheckBoxButton: UIButton!
}
I can of course put let cell = tableView.cellForRowAtIndexPath(indexPath) as! ResultTableCellinside an if-clause, but then other strange things happens.

Don't use !.
cellForRowAtIndexPath returns UITableViewCell? and can be nil if the row isn't visible when you make the call.
Check whether you actually got a cell at all using if let before casting it to your expected object type.

There are a few things going on here, let's take them one at a time:
The crash is from forcing the nil value through the ! that part should be obvious.
The for loop goes from 0 through to the number of rows, but UITableViewCell instances get recycled, so that can't work.
The checkbox selection is not the standard iOS table cell selection but something more custom
If you were using the built-in selection behavior of UITableViewyou could use this method.
If you want to continue using your custom checkbox selection, than probably the easiest way to do that is to listen for the selection change and build up the NSIndexPath as things are selected.
Afterward when the delete button is pressed you will have a pre-built index ready to delete and you won't have to loop to search for what is selected.

Related

Best Approach to make Calculation in tableviewcells and sums up in UILabel

My Cart looks like this
What should be the best approach to do calculation of all cells and sums up total Amount label
Cart Working like this :
Increment in cell's item doubles the value of price label but when i dequeue new cell it already has that increment value
When tried to work with custom delegate , Delegate always shows nil
What should I do ? why my delegate is always nil ?
TableViewCell
class ShoppingCartCell: UITableViewCell {
#IBOutlet weak var cellView:UIView!
#IBOutlet weak var productImageView:UIImageView!
#IBOutlet weak var productName:UILabel!
#IBOutlet weak var brandName:UILabel!
#IBOutlet weak var productPrice:UILabel!
#IBOutlet weak var modifier1Lbl:UILabel!
#IBOutlet weak var modifier2Lbl:UILabel!
#IBOutlet var counterBtns:[UIButton]!
#IBOutlet weak var counterLbl:UILabel!
var delegate : cellDelegateFunc?
override func layoutMarginsDidChange() {
super.layoutMarginsDidChange()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
productImageView.layer.cornerRadius = productImageView.frame.height / 4
cellView.roundUIViewWithShadow(cornerRadius: 4, shadowColor: .darkGray)
cellView.layer.masksToBounds = false
cellView.layer.shadowColor = UIColor.lightGray.cgColor
cellView.layer.shadowOpacity = 1
cellView.layer.shadowOffset = .zero
}
override func awakeFromNib() {
super.awakeFromNib()
cellView.layer.cornerRadius = cellView.frame.height / 16
productImageView.layer.cornerRadius = productImageView.frame.height / 16
}
#IBAction func counter(_ sender:UIButton){
self.delegate?.countItems(self)
}
}
CartViewController (Particular Portion)
class ShoppingBagVC: UIViewController , cellDelegateFunc {
func countItems(_ cell: ShoppingCartCell) {
print("print")
}
}
Protocol
protocol cellDelegateFunc : class {
func countItems(_ cell:ShoppingCartCell)
}
CellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if cartAllData[indexPath.row].deal.data != nil {
let cell = cartTableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath) as! ShoppingCartDealCell
cell.originalPrice = Int(cartAllData[indexPath.row].deal.data!.dealPrice)
cell.productName.text = cartAllData[indexPath.row].deal.data?.dealName
cell.productPrice.text = "Rs.\(String(cell.originalPrice))"
cell.freeItem.text = cartAllData[indexPath.row].deal.data?.freeProduct
cell.productImageView?.sd_setImage(with: URL(string: cartAllData[indexPath.row].deal.data!.imageURL), completed: nil)
return cell
} else {
let cell = cartTableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! ShoppingCartCell
var originalPrice = Int()
var price : Int = 2{
didSet {
cell.productPrice.text = "Rs.\(String(price))"
}
}
var count : Int = 1{
didSet {
cell.counterLbl.text = String(count)
price = originalPrice * count
}
}
if let value = cartAllData[indexPath.row].deal.data?.quantity {
cell.counterLbl.text = String(value)
}
if let value = cartAllData[indexPath.row].product.data?.quantity {
cell.counterLbl.text = String(value)
}
originalPrice = Int(cartAllData[indexPath.row].product.data!.productBasePrice)
cell.productPrice.text = "Rs.\(String(originalPrice))"
cell.productName.text = cartAllData[indexPath.row].product.data?.productName
cell.productImageView?.sd_setImage(with: URL(string: cartAllData[indexPath.row].product.data!.imageURL), completed: nil)
cell.modifier1Lbl.text = cartAllData[indexPath.row].product.data?.modifier1
cell.modifier2Lbl.text = cartAllData[indexPath.row].product.data?.modifier2
return cell
}
}
As #joakim said in comment you are doing calculations in a UI! and it's not a correct way
When a UITableView scrolls every cell will reload because of reusing and every cell will lose its state because it loads again. so you must store state of each cell in a Model and pass it to your cell each time a cell loads.
As you requested The Best approach would be to use a ViewModel or a Presenter to store state of a View (here your cell) and in every load you feed that View (for example in your cellForRow) with the stored States or Properties

How to print value in UILabel from the values of TableView dynamically

I'm having a tableView with a custom cell, whenever I click the checkbox button the value inside the cell increases i.e.,(0 to 1) in cell, and on uncheck value decreases, that works perfectly. But whenever I try to print those values from the cell to a UILabel outside tableView, the values are not changing.
This is the below code I have Used
var data = [[String: AnyObject]]()
func getDetails() {
let paymentURL = paymentListURL + String(28) + "&student_id=" + String(33)
Alamofire.request(paymentURL).responseJSON { (response) in
if ((response.result.value) != nil) {
var jsonVar = JSON(response.result.value!)
print(jsonVar)
if let da = jsonVar["types"].arrayObject {
self.data = da as! [[String:AnyObject]]
}
if self.data.count > 0 {
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TabCell
cell.checkB.tag = indexPath.row
let ip = data[indexPath.row]
cell.nameText.text = ip["title"] as? String
if cell.nameText.text == "Mandatory testing" {
cell.checkB.checkState = .checked
cell.backgroundColor = UIColor.lightGray
cell.checkB.backgroundColor = UIColor.lightGray
}
if ip["mandatory"] as? String == "yes" {
moneyText.text = ip["amount"] as? String
//moneyText is UILabel outside Tableview
cell.amountValue.text = ip["amount"] as? String
cell.checkB.isEnabled = false
} else {
moneyText.text = "0"
if cell.amountValue.text == "1"{
print("ONE")
}
}
return cell
}
func didPressButton(_ tag: Int) {
let indexPath = IndexPath.init(row: 0, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! TabCell
moneyText.text = String(cell.someValue)
}
And for TableviewCell I Used
protocol TabCellDelegate {
func didPressButton(_ tag: Int)
}
class TabCell: UITableViewCell {
#IBOutlet weak var checkB: M13Checkbox!
#IBOutlet weak var nameText: UILabel!
#IBOutlet weak var amountValue: UILabel!
var someValue: Int = 0 {
didSet {
amountValue.text = "\(someValue)"
}
}
#IBAction func checkBAction(_ sender: M13Checkbox) {
cellDelegate?.didPressButton(sender.tag)
if checkB.checkState == .checked {
someValue += 1
} else if checkB.checkState == .unchecked {
someValue -= 1
}
}
}
I tried first adding those values from cell to an Array, and then adding all the values in array and printing in UILabel, but the values are not changing, it was only incrementing.i.e., even after unchecking the checkbox the value is increasing.
I tried even using protocol it did not work for me
Any Help will be appreciated.
You are updating someValue from the checkBAction handler inside the TabCell. The property didSet handler will then update the amountValue label. This is the reason, why the cell's label is being updated.
You do not have any code that updates the moneyText after someValue changed. You only set moneyText.text from tableView(_:cellForRow:), but this is called when a cell is being displayed, maybe multiple times after scrolling etc.
You could do the following:
Create a delegate property inside the cell (use a custom protocol as type)
Set the controller to be that delegate
When the value changes, call a function of that delegate (e.g. call the controller)
Inside that, update the moneyText
As an idea (might not compile because I don't have all your classes):
protocol MyTabCellProtocol {
func checkboxChanged(_ checkbox:M13Checkbox, atRow:Integer)
}
class TabCell: UITableViewCell {
weak delegate:MyTabCellProtocol?
// ...
#IBAction
func checkBAction(_ sender: M13Checkbox) {
if checkB.checkState == .checked {
someValue += 1
} else if checkB.checkState == .unchecked {
someValue -= 1
}
delegate?.checkboxChanged(self, checkB.tag)
}
}
class MyController : UIViewController, MyTabCellProtocol {
func checkboxChanged(_ checkbox:M13Checkbox, atRow:Integer) {
moneyText.text = "\(checkbox.someValue)"
}
}
But if I think further, I would suggest to refactor the whole code a little. The problem I see is that your action handler inside the cell does update the someValue property of the cell, but does not update the outside model (ip["amount"]). I think what you should do is:
Inside the cell's checkBAction, just call the delegate and provide the information about the row that has been modified (self.checkB.tag) and the check state. Do not update the amountValue here!
In the delegate implementation, update the model ip["amount"]
Call reloadRows(at:with:) of the table view to refresh the cell
Then, cellForRow is automatically being called, in which you then update the cell and the outer label.

Adding the sum of labels from a table cell in Swift

I cannot figure this out since I do not know enough about table cells. I am building an invoice app for myself. In my tableview custom cell I made a label on the right side that is for amount due. when you fill out the invoice it prints out the amount in that label.
I have an empty label at the very top call totalDue that I want to have the sum of every amount in the table. I am struggling with this.
What I have is
import UIKit
var clientName = [String]()
var dueDate = [String]()
var projecDescript = [String]()
var dateStamp = Date()
var invoiceNum = [String]()
var amountDue = [String]()
var clientPicker = [""]
// Custom cell to make all input fields custom
class CustomCell: UITableViewCell {
//Make your outlets here, connect the outlets from cell in your storyboard
#IBOutlet var clientNameLabel: UILabel!
#IBOutlet var descriptionLabel: UILabel!
#IBOutlet var dateLabel: UILabel!
#IBOutlet var amountLabel: UILabel!
#IBOutlet var invoiceNum: UILabel!
#IBOutlet var dateStamp: UILabel!
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var clientTableList: UITableView!
#IBOutlet var totalDue: UILabel!
#IBOutlet var totalBillsLabel: UILabel!
func calculateSum() {
var sum = 0
for amount in amountDue {
sum += amount
}
totalDue.text = "\(sum)"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (clientName.count)
return (dueDate.count)
return (projecDescript.count)
return (invoiceNum.count)
return (amountDue.count)
}
// This is the new items added into the inputs
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "clientCell", for: indexPath) as! CustomCell
// Adds Clients Name
let companyName = clientName[indexPath.row]
cell.clientNameLabel?.text = companyName
// Adds Clients Description
let descriptionName = projecDescript[indexPath.row]
cell.descriptionLabel?.text = descriptionName
// Adds the amount due
let amountName = amountDue[indexPath.row]
cell.amountLabel?.text = "$\(amountName)"
//Adds the total number of bills that you have in invoice
totalBillsLabel.text = "\(indexPath.row + 1)"
//Adding sum of all bills
sum += Int((amountName as NSString).floatValue)
//sum = Int((amountName as NSString).floatValue)
totalDue.text = "\(sum)"
//Adds DueDate
let invoiceDate = "Due \(dueDate[indexPath.row])"
cell.dateLabel?.text = invoiceDate
//Adds invoice Number
let invoiceNum = "Invoice #BMCS \(indexPath.row + 1)"
cell.invoiceNum.text = invoiceNum
//TimeStamp in the label datestamp
let timeStamp = "\(DateFormatter.localizedString(from: Date(), dateStyle: .short, timeStyle: .short))"
cell.dateStamp?.text = timeStamp
return cell
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let editAction = UITableViewRowAction(style: .default, title: "Edit") { (action, index) in
//tableView.isEditing = true
DispatchQueue.main.async() {
self.performSegue(withIdentifier: "EditDetails", sender: self)
}
print("Edit Button Pressed")
}
editAction.backgroundColor = UIColor.green
let deleteAction = UITableViewRowAction(style: .destructive, title: "Remove") { (action, indexPath) in
//Remove the labels in the custom cell
clientName.remove(at: indexPath.row)
//dueDate.remove(at: indexPath.row)
projecDescript.remove(at: indexPath.row)
amountDue.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
//minus one total bill when deleting one
self.totalBillsLabel.text = "\(indexPath.row - 1)"
if indexPath.row == 0 {
self.totalBillsLabel.text = "0"
}
self.clientTableList.reloadData()
}
let emailAction = UITableViewRowAction(style: .default, title: "Email") { (action, index) in
print("Email Button Pressed")
}
emailAction.backgroundColor = UIColor.orange
let phoneCallAction = UITableViewRowAction(style: .default, title: "Call") { (action, index) in
print("Call Button Pressed")
}
phoneCallAction.backgroundColor = UIColor.blue
return [deleteAction,editAction,emailAction,phoneCallAction]
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func viewDidAppear(_ animated: Bool) {
clientTableList.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Second controller
#IBAction func addInvoice(_ sender: Any) {
if clientNameInput.text != "" && descriptionNameInput.text != "" && amountInput.text != ""
{
clientName.append(clientNameInput.text!)
//clientInput.text = ""
projecDescript.append(descriptionNameInput.text!)
//descriptionFieldInput.text = ""
//dueDate.append(dateInput.text!)
//dateInput.text = ""
amountDue.append(amountInput.text!)
//amountList.text = ""
dueDate.append(newDueDateLabel.text!)
// After hit send this is the button that takes you back without having to back out yourself
_ = navigationController?.popViewController(animated: true)
}
}
Do not compute the total in cellForRowAt. That is called every time the row is made visible on the screen, so even if it were summing everything, it would be wrong. Create a separate function that computes the sum and return that to populate the label. Something like:
func calculateSum() {
var sum = 0
for amount in amountDue {
sum+= Int(amount) // more practical to convert to float here
}
totalDue.text = "\(sum)"
}
Then call this method in your viewDidLoad and other appropriate places, such as after a new row is added.
Please add this
var clientName = [String]()
var dueDate = [String]()
var projecDescript = [String]()
var dateStamp = Date()
var invoiceNum = [String]()
var amountDue = [String]()
var sum = 0.0
remove sum from other places in your viewcontroller
then in your second view controller
after amountDue.append(amountInput.text!)
add this
sum += Double(amountInput.text!)!
then in your ViewController
add this
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
totalDue.text = "\(sum)"
}

How can I display different products from an array in each custom cell?

I need to display three or less products from an array in every custom cell of a tableview, I putted three image views and three labels in each cell to show the products. This is my TableviewDataSource Code.
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return products.count / 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeCell", for: indexPath) as! HomeTableViewCell
let sectionIndex = indexPath.section
if (sectionIndex + index + 2) <= products.count && (sectionIndex + index + 1) <= products.count && currentUser != nil {
cell.product1 = self.products[sectionIndex + index]
cell.product2 = self.products[sectionIndex + index + 1]
cell.product3 = self.products[sectionIndex + index + 2]
cell.selectionStyle = .none
index += 2
}
cell.delegate = self
return cell
}
And this is my TableviewCell Code.
protocol CustomCell : class {
func accessToProduct(product: Product)
func performSegueToProduct()
}
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var image1 : UIImageView!
#IBOutlet weak var image2 : UIImageView!
#IBOutlet weak var image3 : UIImageView!
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var label2: UILabel!
#IBOutlet weak var label3: UILabel!
weak var delegate: CustomCell?
var product1: Product! {
didSet {
updateUI1()
}
}
var product2: Product! {
didSet {
updateUI2()
}
}
var product3: Product! {
didSet {
updateUI3()
}
}
var cache = SAMCache.shared()
func downloadPopularImages (product: Product, imageView: UIImageView, label: UILabel) {
imageView.image = nil
let productuid = product.uid
let profileImageKey = "\(productuid)"
if let image = cache?.object(forKey: profileImageKey) as? UIImage {
imageView.image = image
} else {
product.downloadPopularProductImage { [weak self] (image, error) in
if let error = error {
print(error.localizedDescription)
} else {
imageView.image = image
self?.cache?.setObject(image, forKey: profileImageKey)
}
}
}
}
// MARK: - Update the UI downloading product images and adding tapGestureRecognizer
func updateUI1() {
downloadPopularImages(product: product1, imageView: image1, label: label1)
label1.text = product1.name + " \(product1.subName)"
let tapGestureRecognizer1 = UITapGestureRecognizer(target: self, action: #selector(self.image1IsTapped))
image1.addGestureRecognizer(tapGestureRecognizer1)
}
func image1IsTapped () {
delegate?.accessToProduct(product: product1)
print(product1)
delegate?.performSegueToProduct()
}
func updateUI2() {
downloadPopularImages(product: product2, imageView: image2, label: label2)
label2.text = product2.name + " \(product2.subName)"
let tapGestureRecognizer2 = UITapGestureRecognizer(target: self, action: #selector(self.image2IsTapped))
image2.addGestureRecognizer(tapGestureRecognizer2)
}
func image2IsTapped() {
delegate?.accessToProduct(product: product2)
delegate?.performSegueToProduct()
}
func updateUI3() {
downloadPopularImages(product: product3, imageView: image3, label: label3)
label3.text = product3.name + " \(product3.subName)"
let tapGestureRecognizer3 = UITapGestureRecognizer(target: self, action: #selector(self.image3IsTapped))
image3.addGestureRecognizer(tapGestureRecognizer3)
}
func image3IsTapped() {
delegate?.accessToProduct(product: product3)
delegate?.performSegueToProduct()
}
}
However, if I have an array with a number of products that is not divisible by 3, does not correctly display all products. Imagine that we have 5 products, so in the first cell it will display the three first products and in the second cell it will display the two remaining products and there will be one image view and one label empties. But this doesn't happen, the code fills the above with one of the products already displayed.
I tried to use a collection view with three cells y each tableview cell but I need the cells of the uicollectionview to be fixed and not cut with the edges, so for that reason i implemented the three image views and three labels.
How can I fix this?
This is what I want:

Selecting a custom cell activates another cell

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