Did select show check/image using custom tableview cell - swift

Ok so this seems like a really simple question, but I can't seem to solve it efficiently or find anything on the web that has the correct answer. So here goes.
I have a tableViewController with a prototype cell running off a custom class. Inside the prototype cell I have an image of a checkmark which I want to display when the user selects the cell and hide the image when another cell gets selected. I've got an approach working but it requires me to reloadData on the table which seems really inefficient. So there must be a better way right?
My code...
//CUSTOM CLASS (trimmed code down to just show relevant code)
class GoalsTableViewCell: UITableViewCell {
#IBOutlet weak var gNameLabel: UILabel!
#IBOutlet weak var gIsSelectedImage: UIImageView!
}
}
//TABLE VIEW CONTROLLER (trimmed it down to just show relevant code)
class GoalsTableVC: UITableViewController {
var selectedGoalId = ""
var selectedInd = 0
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let gCell = tableView.dequeueReusableCell(withIdentifier: "gCell", for: indexPath) as! GoalsTableViewCell
gCell.gNameLabel.text = goalsData[indexPath.row].gName
if indexPath.row == selectedInd {
gCell.gIsSelectedImage.isHidden = false
} else {
gCell.gIsSelectedImage.isHidden = true
}
return gCell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedGoalId = goalsData[indexPath.row].id!
selectedInd = indexPath.row
tableView.reloadData()
}
}

It's possible to reload only the affected rows
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedGoalId = goalsData[indexPath.row].id!
let indexPathsToReload = [indexPath, IndexPath(row: selectedInd, section: 0)]
selectedInd = indexPath.row
tableView.reloadRows(at: indexPathsToReload, with: .automatic)
}

Related

UITableViewCell not updating UISwitch when selecting row

I have a UITableViewCell that contains a bunch of UISwitches. I want the switch to toggle on/off based on what row the user selects, the data is passing to my cell but the switch state is not updating.
I am using a basic MVC, the Storyboard has a TableView > TableViewCell > Label | UISwitch
Controller:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var testList = [1,2,3,4,5]
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.tableFooterView = UIView()
table.delegate = self
table.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
//Turn them all on at start
cell.setSwitch(rowSelected: true)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
}
SwitchCell
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
#IBOutlet weak var label: UILabel!
func setCell(number: Int){
label.text = String(number)
}
func setSwitch(rowSelected:Bool) {
uiswitch.setOn(rowSelected, animated: true)
}
}
I know I can just make the UISwitch intractable, but I am looking at changing it's state when the user selects the row.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! SwitchCell
cell.setSwitch(rowSelected: false)
}
is incorrect. Using this code you don't pick the cell you want.
You should do simething like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.cellForRow(at: indexPath) as! SwitchCell
cell.setSwitch(rowSelected: false)
}
First of all, we don't dequeue the cell in didSelectRowAt method. Never do that. This will dequeue a brand new cell and won't reflect any changes that you do in it.
So remove the code for tableView(_: didSelectRowAt:) method.
Secondly, you can simply handle the UISwitch state based on cell selection in SwitchCell's definition using setSelected(_:animate:) method like so,
class SwitchCell: UITableViewCell {
#IBOutlet weak var uiswitch: UISwitch!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
uiswitch.setOn(selected, animated: true)
}
//rest of the code...
}
To avoid bulky code, do all the cell layout in custom cell itself instead of doing it in the delegate or dataSource methods. That will only make you ViewController heavier and non-modular.

How can I call the action of a checkbox button when the cell is touched?

I'm using a custom cell which contains a label and a button. In my button, i've an action associated with it i.e when the button is touched the button changes its image and changed to checked image. But i want that action to be call when entire cell is touched.
extension HostOptionViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Question.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let question = Question[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionOptionCell") as! ViewCellTableViewCell
cell.setText(option: question)
arrayOfCells.append(cell)
return cell
}
In my Custom cell
import UIKit
class ViewCellTableViewCell: UITableViewCell {
#IBOutlet weak var DataCell: UILabel!
#IBOutlet weak var checkBox: UIButton!
#IBAction func CheckBoxFunc(_ sender: Any) {
if checkBox.isSelected{
checkBox.isSelected = false
}
else{
checkBox.isSelected = true
}
}
func setText(option: Data){
DataCell.text = option.data
}
}
I want to call "CheckBoxFunc" when the cell is touched.
If you want the CheckBoxFunc to be called when selecting cell, then you should call it in didSelectRowAtIndexPath like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get your Custom cell here using dequeueReusableCell
// Call the CheckBoxFunc
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell: ViewCellTableViewCell = tableView.cellForRow(at: indexPath)! as! ViewCellTableViewCell
selectedCell.checkBox.addTarget(self, action: #selector(TableViewController.checkBoxTapped(_:)), for: .touchUpInside)
}
func checkBoxTapped(sender:UIButton) {
let question = Question[sender.tag]
print(question)
}
Don’t forget to add tag for checkbox button in cellForRowIndex method .

Connect Prototype Cells to View Controller [Swift 4]

I am new to programming and currently working on a newsfeed like app. I had a normal Table view up and running fine, but want to try it now with a costume cell type. So I created one and thought connecting the labels the usual way would be perfectly fine, but I was wrong. So I am wondering how I can let my Text label connect to my view controller, so I can use my custom cell.
class ViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var newsfeedTableView: UITableView!
var ref: DatabaseReference!
var posts = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (posts.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "cell")
//here is where I need the custom label to get the posts
cell.textLabel?.text = posts[indexPath.row]
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 18.0)
return cell
}
}
Create subclass of UITableViewCell and connect IBOutlets to this class
class YourCell: UITableViewCell {
#IBOutlet weak var customLabel: UILabel!
...
}
don't forget to set class of your prototype cell in storyboard:
then in cellForRowAt data source method downcast your dequeued cell as YourCell
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCell
then you have access to YourCell outlets
cell.customLabel.text = "SomeText"
...
I'm assuming that you are using Storyboard.
First of all, you should understand that there is little difference when you use own custom table cell. In that case, in the method "cellForRowAtIndexPath", after dequeue your cell, you just have to typecast table cell like 'as! YourCustomTableCellClass'. After this line, you can access each property of this class.
First, design your table cell on Storyboard whatever you want.
Now, make a subclass of UITableViewCell and assign this class to your prototype custom cell which you have designed on Storyboard. Also, don't forget to set "reuse identifier in Storyboard table cell.
Then connect your outlets with custom cell class from Storyboard.
Now you can use code like this:
class YourTableCellClass: UITableViewCell {
// I'm using these outlets as a sample, but you have to connect them from Storyboard.
var leftTextLabel: UILabel!
var rightTextLabel: UILabel!
}
class YourTableController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - TableView Delegate & DataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // return your number of rows here...
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // return the height for the row here.....or you can set height from Storyboard if you have fix height of all rows.
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! YourTableCellClass
cell.leftTextLabel.text = "Text" // Set text here....
cell.rightTextLabel.text = "Text" // Set text here....
return cell
}
}

How to maintain highlighted multiple UITableView rows after reloading UITableView?

I have a UITableView.
I can select multiple cells make them highlighted.
But, whenever I reload App, the selected and highlighted state disappears.
I think I need to put some codes in 'viewDidAppear' and 'didSelectRowAt'. But I don't have any idea of specific codes.
What do I have to put in this syntax?
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
I recommend u add to dataSource item field selected, and in method cellForRowAtindexPath configure by your dataSource items.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cellClass = (self.cellClass(for: indexPath) ?? self.cellClasses.first) else { return UITableViewCell() }
let cell = tableView.dequeueReusableCell(withIdentifier: cellClass.identifier, for: indexPath)
if let configurable = cell as? ConfigurableComponent, let object = self.object(at: indexPath) {
let item = self.dataSource[indexPath.row]
configurable.selected = item.selected
}
return cell
}
When call didSelectRowAt, just take item and change selected field, sample:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = self.dataSource[indexPath.row]
item.selected = !item.selected
tableView.reloadData()
}

Setting imageView in cell

i am new to Swift and I have seen tons of guides on the topic. However in my case, it's working. I have a custom cell file:
class FileCell: UITableViewCell {
#IBOutlet weak var firstName: UILabel!
#IBOutlet weak var cellImage: UIImageView!
func updateImage(name: String) {
cellImage.image = UIImage(named: name)
}}
In the view controller I am using "willDisplay" function as follows:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "FileCell") as! FileCell
let user = users[indexPath.row]
if user.email.isEmpty {
//cell.cellImage.image = UIImage(named: "folder.png")
cell.updateImage(name: "folder.png")
} else {
//cell.cellImage.image = UIImage(named: "file.png")
cell.updateImage(name: "file.png")
}
}
where i try to change imageView in cell depending on the data incoming to cell. However, the images either don't display at all or cells are not showing up.
Thanks in advance for tips on what i am doing wrong.
You should not dequeue a new cell, since you already have one.
Just skip the first statement:
// let cell = tableView.dequeueReusableCell(withIdentifier: "FileCell") as! FileCell
But one question: Why are you using the willDisplay delegate method? I would suggest to set up the cell in tableView(_:cellForRowAt:):
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "FileCell") as! FileCell
let user = users[indexPath.row]
cell.firstName.text = user.name // or something
if user.email.isEmpty {
cell.updateImage(name: "folder.png")
} else {
cell.updateImage(name: "file.png")
}
return cell
}