In swift, how to manage two buttons in same custom tableview cell? - ios

I am trying to manage two buttons in same custom tableview cell.
Added two buttons named Yes and No. If yes button is selected the No button will be inactive and Yes button became active.
Here is the image what I need
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell") as! TableViewCell
cell.yesButton.tag = 101
cell.noButton.tag = 102
cell.yesButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
cell.noButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
#objc func buttonClicked(sender: AnyObject) {
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableList)
let indexPath = tableList.indexPathForRow(at: buttonPosition)
if sender.tag == 101 {
if indexPath != nil {
print("Cell indexpath = \(String(describing: indexPath?.row))")
}
}
if sender.tag == 102 {
if indexPath != nil {
print("Cell indexpath = \(String(describing: indexPath?.row))")
}
}
}

Create a model to main the state of yesButton and noButton for each tableViewCell, i.e.
class Model {
var isYesSelected = false
var isNoSelected = false
}
Create a custom UITableViewCell with Outlets of yesButton and noButton.
Create a single #IBAction for both the buttons and handle their UI based on which button is tapped.
Also, use a buttonTapHandler to identify the row in which the button is tapped. It will be called everytime a button is tapped. We'll be setting this when creating the instance of TableViewCell in tableView(_:cellForRowAt:).
class TableViewCell: UITableViewCell {
#IBOutlet weak var yesButton: UIButton!
#IBOutlet weak var noButton: UIButton!
var buttonTapHandler: (()->())?
var model: Model?
override func prepareForReuse() {
super.prepareForReuse()
yesButton.backgroundColor = .gray
noButton.backgroundColor = .gray
}
func configure(with model: Model) {
self.model = model
self.updateUI()
}
#IBAction func onTapButton(_ sender: UIButton) {
model?.isYesSelected = (sender == yesButton)
model?.isNoSelected = !(sender == yesButton)
self.updateUI()
}
func updateUI() {
yesButton.backgroundColor = (model?.isYesSelected ?? false) ? .green : .gray
noButton.backgroundColor = (model?.isNoSelected ?? false) ? .green : .gray
}
}
UITableViewDataSource's tableView(_:cellForRowAt:) method goes like,
let numberOfCells = 10
var models = [Model]()
override func viewDidLoad() {
super.viewDidLoad()
(0..<numberOfCells).forEach { _ in
self.models.append(Model())
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCells
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! TableViewCell
cell.configure(with: models[indexPath.row])
cell.buttonTapHandler = {
print(indexPath.row)
}
return cell
}
To get the totalPoints, count the models with isYesSelected = true, i.e.
let totalPoints = models.reduce(0) { (result, model) -> Int in
if model.isYesSelected {
return result + 1
}
return 0
}
print(totalPoints)

Get that Button using your Tag like below and after that, you can change the value as per you want.
var tmpButton = self.view.viewWithTag(tmpTag) as? UIButton

Simple 3 step process...!!
Define Model Class
Prepare tableView Cell & handle actions
Set up tableView in view controller
Let's start implementation:
1) Define Model Class
In UI, we have a information like question & it's answer (Yes/No). So design model respectively.
//MARK:- Class Declaration -
class Question {
let questionText: String
var answerState: Bool?
init(question: String) {
self.questionText = question
}
}
2. Prepare tableView Cell & handle actions
Create a custom tableView cell with Question Label, Yes Button & No Button. Link that view with respected #IBOutlets & #IBActions.
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var yesButton: UIButton!
#IBOutlet weak var noButton: UIButton!
var question: Question?
var toggle: Bool? {
didSet {
question?.answerState = toggle
//Do buttons operations like...
if let isToggle = toggle {
yesButton.backgroundColor = isToggle ? .green : .gray
noButton.backgroundColor = isToggle ? .gray : .green
} else {
yesButton.backgroundColor = .gray
noButton.backgroundColor = .gray
}
}
}
func prepareView(forQuestion question: Question) {
self.question = question
questionLabel.text = question.questionText
toggle = question.answerState
}
//Yes Button - IBAction Method
#IBAction func yesButtonTapped(_ sender: UIButton) {
toggle = true
}
//No Button - IBAction Method
#IBAction func noButtonTapped(_ sender: UIButton) {
toggle = false
}
}
3. Set up tableView in view controller
class ViewController: UIViewController {
//Prepare questions model array to design our tableView data source
let arrQuestions: [Question] = [Question(question: "Do you speak English?"), Question(question: "Do you live in Chicago?")]
}
//MARK:- UITableView Data Source & Delegate Methods -
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrQuestions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TableViewCell else {
return UITableViewCell()
}
tableViewCell.prepareView(forQuestion: arrQuestions[indexPath.row])
return tableViewCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80.0
}
}

Create basic tableView and configure dataSource functions
Create tableView cell with two buttons
Create cell class with buttons outlets and actions
Result of this code
Enjoy!

Related

How to change UITableviewCell Label Text after UIButton click?

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
}

UISwitch state in Tableviewcell resets when user scrolls - Swift

I've searched for a solutions on this issue but none seem to work for my use case.
I have a table inside a viewcontroller and the issue I am facing is that when scrolling the UISwitch state is reset to OFF. I understand table cells are reused, but how do I implement a solution that will restore the state of UISwitch when a user scrolls based on my code below
import UIKit
class StirrViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var mylabel: UILabel!
var myString = String()
#IBAction func stirrBtn(_ sender: AnyObject) {
}
var timeSelected = String()
var selectedTimeArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mylabel.text = myString
self.timeSelected = myString
}
func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
let kValue = (sender.tag + 1)
let keyValue = String(kValue)
if sender.isOn {
recipeSettings.boolStirrSwitch[keyValue] = true
recipeSettings.switchedOnArr.append(keyValue)
} else {
recipeSettings.boolStirrSwitch[keyValue] = false
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let stringNum = Int(self.timeSelected)
recipeSettings.recipeTimeSet2 = stringNum!
return(stringNum)!
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UITableViewCell
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
// Process data displayed in rows(minutes)
let endTime = Int(self.timeSelected)
let startTime = Int(1)
// Recipe time array
let timeArray: [Int] = Array(startTime...endTime!)
let stringTimeArr = timeArray.map{String($0)}
// Save time array to global variable
recipeSettings.recipeTimeSetArr = stringTimeArr
// Create a boolean Array to hold all default false booleans
let defBool: Bool = false
var defBoolArr: [Bool] = []
// Fill the array with the defaults boolean
for _ in 0..<stringTimeArr.count{defBoolArr.append(defBool)}
// Map the array to global dictionary containing the Time in an array and default "false" value
for i in 0..<stringTimeArr.count {
recipeSettings.boolStirrSwitch[stringTimeArr[i]] = defBoolArr[i]
}
// Add the minutes to cell table
cell.textLabel?.text = stringTimeArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
As you can see in my code I do save the state of each UI switch in a global variable dictionary. How can I solve the issue of UISwitch changing states based on this code? All help is appreciated. Thanks in advance
var switchState = [String : Bool]()
your recipeSettings.boolStirrSwitch should be decleard like that.
As you are using timeSelected as numberOfRowsInSection as showing
your cell.textLabel from that so you don't need extra stringTimeArr
for that.
All the processing you do in cellForRowAt it will happen again and
again table cells are reused so for setting up data do it in another
function then reload TableView.
Solution for your problem should be look like that.
import UIKit
class StirrViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
//make tableView IBOutlet for reloading data
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var mylabel: UILabel!
var myString = String()
#IBAction func stirrBtn(_ sender: AnyObject) {
}
var timeSelected = String()
var selectedTimeArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mylabel.text = myString
self.timeSelected = myString
self.setdefaultSwitchState()
}
//recipeSettings.boolStirrSwitch should be decleard like that
var switchState = [String : Bool]()
//setDeaultSwitchState
func setdefaultSwitchState(){
if let timeSelected = Int(self.timeSelected){
for value in 0..<timeSelected{
switchState["\(value)"] = false
//or
//recipeSettings.boolStirrSwitch["\(value)"] = false
}
}
self.tableView.reloadData()
}
#objc func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
let kValue = (sender.tag + 1)
let keyValue = String(kValue)
if sender.isOn {
switchState[keyValue] = true
} else {
switchState[keyValue] = false
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let stringNum = Int(self.timeSelected)
recipeSettings.recipeTimeSet2 = stringNum!
return(stringNum)!
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UITableViewCell
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
cell.textLabel?.text = "\(indexPath.row + 1)"
if let switchState = switchState["\(indexPath.row)"] {
if switchState{
switchView.isOn = true
}else{
switchView.isOn = false
}
}else{
switchView.isOn = false
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

radio buttons in a tableview cells swift

Currently I am trying to make a store-like view that shows multiple different listing (spaceships) and you can only select one at a time. It would be for a game where you can select your different spaceships. I am sing a UITableView with a UITableViewCell Xib file. I have only one table cell Xib file to format all of the different listings. The style of the Select button is suppose to be like a Radio Button.
The difficulty I am having is that when I click one button, it makes all the buttons say "Selected."
This is the code that I have:
TableViewController:
import UIKit
struct CellData {
var image: UIImage?
var message: String?
var id: Int?
}
var array = [String]()
var buttonArray = [ShopButton]()
class ViewController: UITableViewController {
var data = [CellData]()
override func viewDidLoad() {
super.viewDidLoad()
data = [CellData.init(image: #imageLiteral(resourceName: "ship_0"), message: "Red Ship", id: 0), CellData.init(image: #imageLiteral(resourceName: "ship_1"), message: "Blue Ship", id: 1), CellData.init(image: #imageLiteral(resourceName: "ship_3"), message: "Yellow Ship", id: 2)]
array = ["Select", "Select", "Select"]
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("TableViewCell", owner: self, options: nil)?.first as! TableViewCell
cell.mainImageView.image = data[indexPath.row].image
cell.mainLabel.text = data[indexPath.row].message
cell.buttonOutlet.setTitle(array[indexPath.row], for: .normal)
cell.buttonOutlet.tag = data[indexPath.row].id!
buttonArray.append(cell.buttonOutlet)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
}
TableViewCell
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var mainImageView: UIImageView!
#IBOutlet weak var mainLabel: UILabel!
#IBOutlet weak var buttonOutlet: ShopButton!
#IBAction func button(_ sender: ShopButton) {
buttonOutlet.makeSelected()
}
override func awakeFromNib() {
super.awakeFromNib()
}
My SubClass for the UIButton:
import UIKit
class ShopButton: UIButton {
var buttonID = Int()
func makeSelected() {
switch self.buttonID {
case 0:
array = ["Selected", "Select", "Select"]
buttonTitle()
print("Ship: Red Ship")
case 1:
array = ["Select", "Selected", "Select"]
buttonTitle()
print("Ship: Blue Ship")
case 2:
array = ["Select", "Select", "Selected"]
buttonTitle()
print("Ship: Yellow Ship")
default:
print("Error: Default Initiated")
}
}
func buttonTitle() {
for button:ShopButton in buttonArray {
button.setTitle(array[button.buttonID], for: .normal)
}
}
override func awakeFromNib() {
}
}
I realize this might not be the best approach to making this shop setup so if anyone has an answer to how to fix this or a different way that would be much better than what I have currently done, it would be much appreciated. Thanks.
You can possibly use this logic in didSelectRowAt in table view like:
You can make the tag of button equivalent to the indexpath.row of tableview
for (button in buttons) {
button.isSelected = false;
}
if button.tag == indexpath.row{ //Here sender.tag would be your indexPath.row
button.isSelected = true;
button.setImage(#imageLiteral(resourceName: "ic_Radio_filled"), for: .normal)
}else{
button.isSelected = false;
button.setImage(#imageLiteral(resourceName: "ic_Radio_Empty"), for: .normal)
}
Let me know if it works for you.

Loop through a uitableviewcell on submit in swift 4

I am trying to access each value of a text field in a prototype cell within a UITableView on Submit. I know I should be doing this in a better way (model) but for now, I just need to access these fields and cannot find a way to do this in Swift 3/4. Would anyone be able to assist?
Code:
import UIKit
import Firebase
class FormTableViewController: UITableViewController {
var formLabels = [String]()
var formPlaceholders = [String]()
override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
formLabels = ["Name","Email","Password", "Phone"]
formPlaceholders = ["John Smith","example#email.com","Enter Password", "8585551234"]
tableView.estimatedRowHeight = 30
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return formLabels.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier:
"FormTableCell", for: indexPath)
as! FormTableViewCell
let row = indexPath.row
cell.formLabel.font =
UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
cell.formLabel.text = formLabels[row]
cell.formTextField.placeholder = formPlaceholders[row]
return cell
}
#IBAction func submitButtonPressed(_ sender: Any) {
// Need to do something with the Name, Email, Phone and Password fields here
}
}
You seem to acknowledge that updating the model directly probably makes sense. So why not do that? Just:
Have model collection for the responses;
Set up delegate for the text field in the cell;
Have cellForRowAt set that delegate; and
Make the table view controller conform to that class.
So, something quick and dirty, set up the cell to hook up editChanged event from the text field and set up protocol to inform the view controller:
protocol FormTableViewCellDelegate: class {
func fieldValueChanged(cell: UITableViewCell, textField: UITextField)
}
class FormTableViewCell: UITableViewCell {
weak var delegate: FormTableViewCellDelegate?
#IBOutlet weak var formLabel: UILabel!
#IBOutlet weak var formTextField: UITextField!
#IBAction func editingChanged(_ sender: UITextField) {
delegate?.fieldValueChanged(cell: self, textField: sender)
}
}
And then have the view controller set up model object and conform to your new protocol:
class FormTableViewController: UITableViewController {
var formLabels = [String]()
var formPlaceholders = [String]()
var values = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
...
formLabels = ["Name","Email","Password", "Phone"]
formPlaceholders = ["John Smith","example#email.com","Enter Password", "8585551234"]
values = [nil, nil, nil, nil]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FormTableCell", for: indexPath) as! FormTableViewCell
let row = indexPath.row
cell.formLabel.font = .preferredFont(forTextStyle: .headline)
cell.formLabel.text = formLabels[row]
cell.formTextField.placeholder = formPlaceholders[row]
cell.formTextField.text = values[row]
cell.delegate = self // set the delegate, too
return cell
}
#IBAction func submitButtonPressed(_ sender: Any) {
print(#function, values)
}
}
// delegate protocol to update model as text fields change
extension FormTableViewController: FormTableViewCellDelegate {
func fieldValueChanged(cell: UITableViewCell, textField: UITextField) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
values[indexPath.row] = textField.text
}
}
Then that's it, your model is updated as the text fields are updated. Plus this has the advantage that it now supports cell reuse, conforms to MVC patterns, etc.
If you want to just loop through cells, you can create an array of ‘IndexPath’.
let array = (0..<formLabels.count).map { IndexPath(row: $0, section:0) }
After that you can loop over this array and access individual cell using tableview method:- tableView.cellForIndexPath
Hope this helps. (Not on my laptop, so didn’t test the syntax)

How can i stop my tableView cell from reusing the button inside it?

I have a function inside a protocol that takes a TableViewcell as an argument.
protocol GoingButtonDelegate {
func goingButtonPressed(cell: TableViewCell)
}
class TableViewCell: UITableViewCell {
// Outlets
#IBOutlet weak var goingButton: UIButton!
var delegate: GoingButtonDelegate?
#IBAction func goingButtonTapped(_ sender: Any) {
delegate?.goingButtonPressed(cell: self)
}
I then go over to my ViewController and implement the delegate and it's function, which is to change the image of a button when tapped. The "goingSelected" is a green image and the "goingDeselected" is a red image.
This all works out fine, when tapped the button of a cell goes from red to green and vice versa. However, when the cell gets reused, the button state of the cell persists and gets reused for the new row that enters view. Is there any way to stop this from happening?
extension ViewController: GoingButtonDelegate {
func goingButtonPressed(cell: TableViewCell) {
cell.goingButton.isSelected = !cell.goingButton.isSelected
if cell.goingButton.isSelected == true {
cell.goingButton.setImage(UIImage(named: "goingSelected"), for: UIControlState.selected)
} else if cell.goingButton.isSelected == false {
cell.goingButton.setImage(UIImage(named: "goingDeselected"), for: UIControlState.normal)
}
}
}
It's possible
just replace
let cell = tableView.dequeueReusableCell(withIdentifier: identifier,
for: indexPath)
with:
let cell= Bundle.main.loadNibNamed(identifier, owner: self, options: nil)?[0]
but I think you need to change your app logic.
Set Images inside of your cell class
class TableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
self.goingButton.setImage(UIImage(named: "goingDeselected"), for:.normal)
self.goingButton.setImage(UIImage(named: "goingSelected"), for:.selected)
}
}
and in method goingButtonPressed(cell: TableViewCell) change cell to your object
and just set Bool type true or false
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.goingButton.isSelected = object.isSelected
...
}
You need to store the selected rows in an array of index paths, before that I think you should make few enhancements ... or a lot!!
the cell itself should handle it's button, the controller should just keep track of all cells status.
Add these two properties to your cell
class TableViewCell: UITableViewCell {
var indexPath:IndexPath?
var isSelected : Bool = false {
didSet{
if isSelected {
cell.goingButton.setImage(UIImage(named: "goingSelected"), for: UIControlState.normal)
} else {
cell.goingButton.setImage(UIImage(named: "goingDeselected"), for: UIControlState.normal)
}
}
}
// Outlets
#IBOutlet weak var goingButton: UIButton!
var delegate: GoingButtonDelegate?
#IBAction func goingButtonTapped(_ sender: Any) {
self.isSelected = !self.isSelected
delegate?.goingButtonPressed(cell: self)
}
..
...
}
And store the selected cells in your view controller to keep track of each cell status.
extension ViewController: GoingButtonDelegate {
var selectedCells = NSMutableArray()
func goingButtonPressed(cell: TableViewCell) {
if cell.isSelected {
selectedCells.add(cell.indexPath)
} else {
selectedCells.remove(cell.indexPath)
}
}
}
and in your "cell for row" method just add a small change
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCellIdentifier") as! TableViewCell
cell.indexPath = indexPath
cell.isSelected = selectedCells.contains(indexPath)
..
...
return cell
}

Resources