How to implement favorite button in tableView cell? - swift

I need to implement a save button. When a user taps on the button, it has to be filled, by default it's unfilled. But when I run my app some of the buttons in tableView cell are already filled like that
Here's code from TableViewCell
var isFavorite: Bool = false
private let addToFavorites: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(systemName: "heart"), for: .normal)
button.tintColor = UIColor.white.withAlphaComponent(0.85)
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill
return button
}()
override func awakeFromNib() {
super.awakeFromNib()
setFavoriteButtonUI()
addToFavorites.addTarget(self, action: #selector(markFavorite), for: .touchUpInside)
}
#objc func markFavorite() {
setFavoriteButtonImage()
}
private func setFavoriteButtonImage() {
isFavorite = !isFavorite
let imgName = isFavorite ? "heart" : "heart.fill"
let favoriteButtonImage = UIImage(systemName: imgName)
self.addToFavorites.setImage(favoriteButtonImage, for: .normal)
}
private func setFavoriteButtonUI() {
addToFavorites.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(addToFavorites)
addToFavorites.topAnchor.constraint(equalTo: filmImgView.topAnchor, constant: 40).isActive = true
addToFavorites.trailingAnchor.constraint(equalTo: filmImgView.trailingAnchor, constant: -20).isActive = true
addToFavorites.heightAnchor.constraint(equalToConstant: 30).isActive = true
addToFavorites.widthAnchor.constraint(equalToConstant: 40).isActive = true
}
In cellForRowAt indexPath method I added
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.top)

It is a classical problem for iOS beginners.
TableViewCell has a mechanism of reusing object pool.
When some cell with filled heart slides off the screen, the cell enters the reusing object pool.
The new cell appeared on the screen, maybe is created, or pulled from the reusing object pool.
The simple solution is Mark & Config.
Just to maintain the cell status data out the scope of cell, we usually have the status data source on the controller.
#objc func markFavorite() changes the status data source, then table reload data.
in func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell , config the cell heart status.
in Controller:
var selected = [Int]()
func select(index idx: Int){
selected.append(idx)
table.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
// ...
cell.proxy = self
cell.config(willShow: selected.contains(indexPath.row), idx: indexPath.row)
}
in Cell:
var proxy: XXXProxy? // learn sth proxy yourself
var idx: Int?
func config(willShow toShow: Bool, idx index: Int){
idx = index
btn.isHidden = !toShow
}
// add the rest logic yourself

Related

How to use a UISwitch to add and remove data from an Array of Data

I have a tableview with a UISwitch in each of the cells. What I am trying to do is that whenever the UISwitch is Toggled On, it adds that cell into an array and when the switch is Toggled Off it removes it. Right now it only adds and doesn't remove.
Once this is complete I need the CollectionView that is also within this ViewController to update and visually show the newStringArray Cells based on the number in that array and that also is able to appear and disappear based on the cells that have their UISwitch toggled on.
import UIKit
class NewMoveViewController: UIViewController {
private var stringSource = ["String 1,", "String 2", "String 3"]
var newStringArray = Array<String>()
private let tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = 100
return tableView
}()
private var collectionView: UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = CGSize(width: 50, height: 50)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView?.register(NewMoveCollectionViewCell.self, forCellWithReuseIdentifier: NewMoveCollectionViewCell.identifier)
collectionView?.showsHorizontalScrollIndicator = false
title = "Add to Group"
tableView.register(NewMoveTableViewCell.self, forCellReuseIdentifier: NewMoveTableViewCell.identifier)
tableView.delegate = self
tableView.dataSource = self
collectionView?.backgroundColor = .systemBlue
collectionView?.dataSource = self
collectionView?.delegate = self
guard let myCollection = collectionView else {
return
}
view.addSubview(myCollection)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView?.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: 50)
tableView.frame = CGRect(x: 0, y: 200, width: view.frame.size.width, height: view.frame.size.height)
}
}
extension NewMoveViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: NewMoveTableViewCell.identifier, for: indexPath) as! NewMoveTableViewCell
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row
switchView.addTarget(self, action: #selector(self.switchDidChange(_:)), for: .valueChanged)
cell.accessoryView = switchView
cell.configure(with: "", label: "test")
return cell
}
#objc func switchDidChange(_ sender: UISwitch) {
newStringArray.append(stringSource[sender.tag])
// newStringArray.remove(stringSource.remove(at: [s]))
print(newStringArray)
}
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return false
}
}
extension NewMoveViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return newStringArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NewMoveCollectionViewCell.identifier, for: indexPath) as! NewMoveCollectionViewCell
return cell
}
}
the hardest part is to remove an object from an array, my approach on these situations is to transform my array in a NSMutableArray because it have a function to remove an specific object, then make a delegate in the cell that informs the viewController to remove the object from the list and reload the tableView.
the delegate wil be something like this:
protocol RemoveObjectFromCell {
func removeObjectIncell(object: MyObject)
}
class myCell: UITableViewCell {
//your outlets and variables
var object: MyObject?
var delegate: removeObjectIncell?
func setupCell(object: myObject) {
//configure your cell with the specific object
}
}
make sure of calling the delegate on the switch action inside you cell class like this:
#IBAction func switchPressed(sender: UISwitch) {
if !sender.isOn {
self.delegate?.removeObjectIncell(object: self.object)
}
in the view controller implement your protocol and use the required function like this:
class myViewController: UIViewController, RemoveObjectFromCell {
//everything that goes in your class
func removeObjectIncell(object: MyObject) {
self.myArray.remove(object)
DispatchQueue.main.async {
self.myTableView.reloadData()
}
}
}
In order to get changes you want you have to set property which is gonna indicate whether switch is on or off
Something like: var switchIsActive = false
and simply change it in function and if it is turned on you perform one action when it is off you perform another one. Also after function you have to reload your tableView tableView.reloadData()
You can remove elements in your array by their tag by calling Array.remove(at: Int). It can be done by the cells [indexPath.row]

UICollectionViewCell delegate won't fire

I'm obviously doing something wrong but unable as of yet to determine where. I setup the cell as follows:
protocol PropertyPhotoCellDelegate: class {
func deletePropertyPhoto(cell: PropertyPhotoCell)
}
class PropertyPhotoCell: UICollectionViewCell {
weak var propertyPhotoCellDelegate: PropertyPhotoCellDelegate?
let deleteButton: UIButton = {
let button = UIButton()
let image = UIImage(named: "delete.png")
button.setImage(image, for: .normal)
button.showsTouchWhenHighlighted = true
button.isHidden = true
button.addTarget(self, action: #selector(handleDeleteButton), for: .touchUpInside)
return button
}()
var isEditing: Bool = false {
didSet {
deleteButton.isHidden = !isEditing
}
}
I've omitted setting up the cell views. Here is the selector
#objc fileprivate func handleDeleteButton() {
propertyPhotoCellDelegate?.deletePropertyPhoto(cell: self)
}
In the UICollectionViewController, I assign the delegate
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kCellId, for: indexPath) as! PropertyPhotoCell
cell.photoImageView.image = photos[indexPath.item]
cell.propertyPhotoCellDelegate = self
return cell
}
This hides or shows the delete button on the cell for all the cells in view
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
navigationItem.rightBarButtonItem?.isEnabled = !editing
if let indexPaths = collectionView?.indexPathsForVisibleItems {
for indexPath in indexPaths {
if let cell = collectionView?.cellForItem(at: indexPath) as? PropertyPhotoCell {
cell.deleteButton.isHidden = !isEditing
}
}
}
}
And finally, conforming to the protocol here
extension PropertyPhotosController: PropertyPhotoCellDelegate {
func deletePropertyPhoto(cell: PropertyPhotoCell) {
if let indexPath = collectionView?.indexPath(for: cell) {
photos.remove(at: indexPath.item)
collectionView?.deleteItems(at: [indexPath])
}
}
}
I tap the UICollectionViewController Edit button and all the cells show the delete button as expected. Any of the cell's delete button highlights on tap, but I don't see the delegate getting called.
When the delegate is assigned in the UICollectionViewController, also set the selector for the cell.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kCellId, for: indexPath) as! PropertyPhotoCell
cell.photoImageView.image = photos[indexPath.item]
cell.propertyPhotoCellDelegate = self
cell.deleteButton.addTarget(cell, action: #selector(cell.handleDeleteButton), for: .touchUpInside)
cell.deleteButton.isHidden = true
return cell
}

How to get the TextField's text located inside a collectionviewcell?

Im kind of new with Swift, what I am trying to do is to get the text typed by an user in a TextField which is located inside a collection view cell. I have a CollectionViewCell named "PestañaCero" where I created the TextField, this one:
import Foundation
import UIKit
class PestañaCero: UICollectionViewCell
{
let NombreUsuarioTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Nombre de Usuario"
nombre.textAlignment = .center
return nombre
}()
let NumerodeContactoTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Numero de Contacto"
nombre.textAlignment = .center
return nombre
}()
let DireccionOrigenTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Direccion de Origen"
nombre.textAlignment = .center
return nombre
}()
let DireccionDestinoTextField: UITextField =
{
let nombre = UITextField()
nombre.borderStyle = UITextBorderStyle.roundedRect
nombre.placeholder = "Direccion de Destino"
nombre.textAlignment = .center
return nombre
}()
func setupViews()
{
addSubview(NombreUsuarioTextField)
addSubview(NumerodeContactoTextField)
addSubview(DireccionOrigenTextField)
addSubview(DireccionDestinoTextField)
//VERTICAL CONSTRAINT
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: NombreUsuarioTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: NumerodeContactoTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: DireccionOrigenTextField)
addConstraintsWithFormat("H:|-16-[v0]-16-|", views: DireccionDestinoTextField)
addConstraintsWithFormat("V:|-100-[v0(30)]-12-[v1(30)]-12-[v2(30)]-12-[v3(30)]", views:
NombreUsuarioTextField,NumerodeContactoTextField, DireccionOrigenTextField ,DireccionDestinoTextField)
}
}
Im trying to print the text when touching in a button created in my cellForItemAt, code which is located in my UICollectionViewController class
#objc func confirmarbutton()
{
print("123")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PestañaCero", for: indexPath)
myCell.backgroundColor = UIColor.black
let nombre = UIButton(frame: CGRect(x: myCell.frame.width/2-100, y: 400, width: 200, height: 25))
nombre.setTitle("Pedir Domicilio", for: .normal)
nombre.backgroundColor = UIColor.orange
nombre.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
nombre.addTarget(self, action: #selector(confirmarbutton) , for: .touchUpInside)
myCell.addSubview(nombre)
}
Any help would be really appreciated, thanks everyone
You can set the delegate of the textField inside the cell to controller when the cell is created, cell.NumerodeContactoTextField.delegate = self and then use the delegate in the controller. However, the problem with this approach is that you will have to do it for all textFields, so the better solution would be create your own delegate, in cell, like this:
protocol CollectionCellTextFieldDelegate: class {
func textDidChanged(_ textField: UITextField)
}
And then add this to your cell:
class PestañaCero: UICollectionViewCell {
weak var textFieldDelegate: CollectionCellTextFieldDelegate?
}
Now in your cell creation in the controller you do:
cell.textFieldDelegate = self
Conform and implement the delegate in the controller:
func textDidChanged(_ textField: UITextField) {
//Here you will get the textField, and you can extract the textFields text
}
This is just an example of how you would approach this situation. you should be able to modify based on your requirement.
A Small Sample of how You would go about doing this with above approach
My Cell Class
import UIKit
protocol CollectionCellTextFieldDelegate: class {
func cellTextFields(_ fields: [UITextField])
}
class Cell: UICollectionViewCell {
#IBOutlet weak var fieldOne: UITextField!
#IBOutlet weak var fieldTwo: UITextField!
#IBOutlet weak var button: UIButton!
weak var textFieldDelegate: CollectionCellTextFieldDelegate?
#IBAction func buttonClicked(_ sender: UIButton) {
guard let textFieldDelegate = textFieldDelegate else { return } //we don't have do anything if not conformed to delegate
//otherwise pass all textFields
textFieldDelegate.cellTextFields([fieldOne, fieldTwo])
}
}
My Controller Class
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, CollectionCellTextFieldDelegate {
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
//register the cell xib
collectionView.register(UINib(nibName: "Cell", bundle: nil), forCellWithReuseIdentifier: "Cell")
}
//MARK:- CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.textFieldDelegate = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width - 20.0, height: 175.0)
}
//you could write this delegate anyway you want, its just for a sample
func cellTextFields(_ fields: [UITextField]) {
//loop over each fields and get the text value
fields.forEach {
debugPrint($0.text ?? "Empty Field")
}
}
}
You will probably have to handle dequeueing of cells as well but for now test this code and modify accordingly.
#objc func confirmarbutton(sender:UIButton)
{
let indexPath = self.collView.indexPathForItem(at: sender.convert(CGPoint.zero, to: self.collView))
let cell = self.collView.cellForItem(at: indexPath!) as! PestañaCero
print(cell.NombreUsuarioTextField.text) // use textfield value like this
print("123")
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PestañaCero", for: indexPath)
myCell.backgroundColor = UIColor.black
let nombre = UIButton(frame: CGRect(x: myCell.frame.width/2-100, y: 400, width: 200, height: 25))
nombre.setTitle("Pedir Domicilio", for: .normal)
nombre.backgroundColor = UIColor.orange
nombre.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
nombre.addTarget(self, action: #selector(confirmarbutton(sender:)) , for: .touchUpInside)
myCell.addSubview(nombre)
}
you can access Any row using the indexpath you just need to pass specific indexpath in cellForItem method to get that row so i just pass my sender and find that row to get that textfield value you just replace my code with yours and it will work :)
Here is a simple solution which I personally follow :
First we have should be able to figure it out that at which index/row's button user has clicked so to know that we will set the "indexPath" to button layer like below in * cellForItemAt* method:
nombre.layer.setValue(indexPath, forKey: "indexPath")
then we need change signature of confirmarbutton method like below (as written in answer by #Mahesh Dangar):
#objc func confirmarbutton(sender:UIButton)
Then we need the indexPath in confirmarbutton method so we can get the cell First and then text field to access the value of that text field :
#objc func confirmarbutton(sender:UIButton){
let indexPath = sender.layer.value(forKey: "indexPath") as! IndexPath
let cell = collectionView.cellForItem(at: indexPath) as! PestañaCero
let number = cell.NombreUsuarioTextField.text! // make sure you have value in textfield else you will get runTime error
//below is safer alternative to above line...write one of them
if let isNumberEntered = cell.NombreUsuarioTextField.text{
//condition will be true if text field contains value
}else{
//This block will be executed if text field does not contain value/it is empty. you can show alert something like please enter the number etc.
}
}

Tap button to change UICollectionView Cell size?

I have a collection view on my HomeController (UICollectionView) Class and my PostCell (UICollectionViewCell) class. HomeController has just 4 different cells. I also have a button inside my PostCell that when touched, handles a handleChangeSize function. When I touch that button, I want that specific cells size to change. Preferably change in height, similar to Instagrams "show more" feature on their cells. Any help will be highly, highly appreciated. Thank you... Here's my code:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.showsVerticalScrollIndicator = false
collectionView?.alwaysBounceVertical = true
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = (view.frame.width) * 9 / 16
return CGSize(width: view.frame.width, height: height + 50 + 50)
}
}
class PostCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
lazy var myButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Change Size", for: .normal)
button.addTarget(self, action: #selector(handleChangeSize), for: .touchUpInside)
return button
}()
func handleChangeSize() {
}
func setupViews() {
addSubview(myButton)
myButton.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
myButton.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I have done this in tableView. follow the same for collectionView.
var expandedCells = [Int]()
here is the cellForRowAtIndexPath method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RequestTableViewCell") as! RequestTableViewCell
cell.selectionStyle = .none
cell.optionButton.tag = indexPath.row
cell.optionButton?.addTarget(self, action: #selector(RequestsViewController.deleteAction), for: UIControlEvents.touchUpInside)
return cell
}
and the action for option button in tableViewCell is
func deleteAction(_ sender: UIButton){
if let button = sender as? UIButton {
// If the array contains the button that was pressed, then remove that button from the array
if expandedCells.contains(sender.tag) {
expandedCells = expandedCells.filter { $0 != sender.tag }
}
// Otherwise, add the button to the array
else {
expandedCells.removeAll()
expandedCells.append(sender.tag)
}
// Reload the tableView data anytime a button is pressed
self.requestTableView.beginUpdates()
self.requestTableView.endUpdates()
}
}
and the heightForRowAtIndexPath method is
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Set the row height based on whether or not the Int associated with that row is contained in the expandedCells array
if expandedCells.contains(indexPath.row) {
return 112
} else {
return 67
}
}
The solution to this problem is quite easy. First, you need to specify what height you want to set for the particular cell and perform that change in handleChangeSize() method (this could be done by simple frame.size.height = ...).
After that, you probably need to resize the whole collection view, otherwise, there are going to be some nasty surprises for you. Perform the calculation (to get the new height) after you have resized the cell (you might call a notification or something that triggers whenever a to resize is necessary).

Why my checkbox in custom cell shows different behaviour while selecting and scrolling in swift?

I have a xib view in which I took a tableView with a customcell xib. In this custom cell I have a checkbox button which behaves like check and uncheck using custom cell. But when ever I click the first cell checkbox as tick the multiple of 9th cell like 9th row cell, 18th row cell, .....also became ticked. and while scrolling the checkbox tick option is changing between cells. I am not able to know why this is happening..??
I have registered cell xib view as:
override func viewDidLoad() {
super.viewDidLoad()
//Register custom cell
let nib = UINib(nibName: "CustomOneCell", bundle: nil)
AddOnTableView.registerNib(nib, forCellReuseIdentifier: "addoncell")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ADDONITEMS.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomOneCell = AddOnTableView.dequeueReusableCellWithIdentifier("addoncell") as! CustomOneCell
let item: AddOnItems = ADDONITEMS[indexPath.row]
cell.addOnName.text = item.name
cell.addOnPrice.text = "£\(item.price!)"
return cell
}
For checkbox I have added a custom class as below:
var isCheckedAddOnGlobal = Bool()
class AddOnCheckBox: UIButton {
let checkedImage = UIImage(named: "checkboxredtick.png")! as UIImage
let unCheckedImage = UIImage(named:"checkbox untick.png")!as UIImage
//bool property
var ischecked:Bool = false{
didSet{
//print(ischecked)
if ischecked == true{
self.setImage(checkedImage, forState: .Normal)
}else{
self.setImage(unCheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action:#selector(CheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
self.ischecked = false
}
func buttonClicked(sender: UIButton) {
if (sender == self) {
if ischecked == true{
ischecked = false
isCheckedAddOnGlobal = false
}else{
ischecked = true
isCheckedAddOnGlobal = true
}
}
}
}
This is happening because you are reusing the TableViewCell, To solve your problem you can try something like this, first create an array of Int that give you selected row and use that array inside cellForRowAtIndexPath method.
var selectedItems = [Int]()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomOneCell = AddO nTableView.dequeueReusableCellWithIdentifier("addoncell") as! CustomOneCell
let item: AddOnItems = ADDONITEMS[indexPath.row]
cell.addOnName.text = item.name
cell.addOnPrice.text = "£\(item.price!)"
cell.checkBoxBtn.tag = indexPath.row
if (selectedItems.contains(indexPath.row)) {
cell.checkBoxBtn.setImage(UIImage(named:"checkbox untick.png"), forState: .Normal)
}
else {
cell.checkBoxBtn.setImage(UIImage(named: "checkboxredtick.png"), forState: .Normal)
}
cell.checkBoxBtn.addTarget(self, action:#selector(self.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
func buttonClicked(sender: UIButton) {
if (self.selectedItems.contains(sender.tag)) {
let index = self.selectedItems.indexOf(sender.tag)
self.selectedItems.removeAtIndex(index)
}
else {
self.selectedItems.append(sender.tag)
}
self.tableView.reloadData()
}
Best way is on selecting cell call
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomOneCell
cell.buttonClicked()
}
and change buttonClicked method to
func buttonClicked() {
...
}
I would make an object, which contains the product information and a boolean, to check if the product has been selected or not.
If you make it this way, the checkmarks will appear correct. When you are scrolling on a tableview, then it loads the data everytime it shows new cells.
Right now, it only knows that the index etc. 9 is selected, and when you scroll down and load new cells, then the index 9 will be selected automatic again.
Try something like this:
Example
class Product {
var productName = "Test"
var isSelected: Bool = false
}
Under your cellForRowAtIndexPath
if product.isSelected == true {
cell.checkBoxBtn.setImage(UIImage(named:"checkbox untick.png"), forState: .Normal)
} else {
cell.checkBoxBtn.setImage(UIImage(named: "checkboxredtick.png"), forState: .Normal)
}