UITableViewCell not updating UISwitch when selecting row - swift

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.

Related

How to user userdefaults to open a tableview with last cell pressed on top position

I'm trying that when I use close an app and start it again a tableview put the last cell pressed on the top. I'm able to store the last cell position with the next code and even move to the top position when press the cell. But I don't now how force the table move to that position when the user comes back.The table has only one section. I'm reading several posts but I don't know how to adapt the solution to my code. Please help!
My code is:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UserDefaults.standard.set(indexPath.row, forKey: "celdaPulsada")
let celdaPulsada = UserDefaults.standard.integer(forKey: "celdaPulsada")
tableView.beginUpdates()
tableView.moveRow(at:[0,celdaPulsada], to: [0, 0])
tableView.endUpdates()
}
This is the demo code please modify it according to your requirement
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
scrollTableViewToLastSelectedPosition()
}
private func setupView() {
tableView.dataSource = self
tableView.delegate = self
}
private func scrollTableViewToLastSelectedPosition() {
let celdaPulsada = UserDefaults.standard.integer(forKey: "celdaPulsada")
tableView.scrollToRow(at: IndexPath(row: celdaPulsada, section: 0), at: .top, animated: false)
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Cell No \(indexPath.row + 1)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UserDefaults.standard.set(indexPath.row, forKey: "celdaPulsada")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
}

Swift UITableview Reload Cell

I'm stuck with the following, any inputs would be highly appreciated.
My app has a UITableViewController with custom cells. Let's name its UITableView as TABLE VIEW-1. I am using XIB as its custom cell. Inside that xib, there is another UITableView, (TABLE VIEW-2), with another XIB as its custom cell.
My question is, How can I reload cells of (TABLE VIEW-2) from (TABLE VIEW-1) once I get data from an API in (TABLE VIEW-1). I want to use delegate and protocols to do this.
OR, What would be the correct way of performing this and how would I go about it?
Create a reference of TableView1 in the TableView2 ViewController. You can do this by
-- Open the Assistant Editor
-- Right-click and drag from your UI element (i.e. label) to the code file
-- Xcode auto inserts code for you to create the name and connection
Then call tableView2.reloadData(), where tableView2 is the name of the outlet you just created.
Create a reference of the TABLE VIEW-2 in the TABLE VIEW-1 custom cell (TABLE VIEW-1 CELL)
Create a method inside TABLE VIEW-1 CELL to reload its data alone with the TABLE VIEW-2 (Optional but useful to organize your code)
Inside the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell of the TABLE VIEW-1 call reload function
class ViewController: UIViewController {
#IBOutlet weak var mainTable: UITableView!
let sampleData = [
(section: "News", SubSections: [
"Breaking news",
"Current Affairs",
"Local"
]) ]
override func viewDidLoad() {
super.viewDidLoad()
self.mainTable.estimatedRowHeight = 200
self.mainTable.rowHeight = UITableView.automaticDimension
let nib: UINib = UINib(nibName: String(describing: MainCell.self), bundle: Bundle.main)
self.mainTable.register(nib, forCellReuseIdentifier: String(describing: MainCell.self))
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.sampleData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MainCell = tableView.dequeueReusableCell(withIdentifier: String(describing: MainCell.self), for: indexPath) as! MainCell
let sectionData = self.sampleData[indexPath.row]
cell.setUpData(title: sectionData.section, data: sectionData.SubSections)
return cell
}
}
class MainCell: UITableViewCell {
#IBOutlet weak var sectionTitle: UILabel!
#IBOutlet weak var internalTable: UITableView!
var tableData:[String]?
override func awakeFromNib() {
super.awakeFromNib()
self.internalTable.estimatedRowHeight = 200
self.internalTable.rowHeight = UITableView.automaticDimension
let nib: UINib = UINib(nibName: String(describing: InsideCell.self), bundle: Bundle.main)
self.internalTable.register(nib, forCellReuseIdentifier: String(describing: InsideCell.self))
}
func setUpData(title: String, data:[String]) {
self.sectionTitle.text = title
self.tableData = data
self.internalTable.reloadData()
}
}
extension MainCell: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.tableData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: InsideCell = tableView.dequeueReusableCell(withIdentifier: String(describing: InsideCell.self), for: indexPath) as! InsideCell
if let subSectionData = self.tableData?[indexPath.row] {
cell.subSectionTitle.text = subSectionData
}
return cell
}
}
class InsideCell: UITableViewCell {
#IBOutlet weak var subSectionTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

How to pass selected data from a PopUpTableViewController to other TableViewController in Swift?

I have 2 View Controllers: In the first ViewController that has a TableView, I have a button in each TableViewCell to select a shipping option. When clicking the button, another TableViewController will pop up with a list of shipping options. Upon choosing a shipping option, I need to pass this data back to TableViewCell in the first ViewController. I wrote below code but the shipping option selected in the second TableViewController still didn't pass to the first controller. Other things work fine. Could anyone help to let me know how to improve this code? Thanks a million!
//First ViewController:
class PaymentMethodViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//MARK: - IBOutlets
#IBOutlet weak var PurchasedReviewItemsTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PurchasedReviewItemsTableViewCell") as! PurchasedReviewItemsTableViewCell
cell.delegate = self
}
}
extension PaymentMethodViewController: PurchasedReviewItemsTableViewCellDelegate {
func chooseShippingOptionButtonPressed() {
let chooseShippingOptionVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(identifier: "ShippingOptionsSelectionPopUpViewController") as! ShippingOptionsSelectionPopUpViewController
chooseShippingOptionVC.modalPresentationStyle = .overCurrentContext
self.present(chooseShippingOptionVC, animated: true, completion: nil)
}
//MARK: Pass data from popUpView
func popUpShippingOptionsSelected(shippingOption: String) {
let cell = PurchasedReviewItemsTableView.dequeueReusableCell(withIdentifier: "PurchasedReviewItemsTableViewCell") as! PurchasedReviewItemsTableViewCell
cell.shippingOptionsLabel.text = shippingOption
}
}
//TableViewCell of the first ViewController:
protocol PurchasedReviewItemsTableViewCellDelegate {
func chooseShippingOptionButtonPressed()
func popUpShippingOptionsSelected(shippingOption: String)
}
class PurchasedReviewItemsTableViewCell: UITableViewCell {
#IBOutlet weak var shippingOptionsLabel: UILabel!
var delegate: PurchasedReviewItemsTableViewCellDelegate?
#IBAction func changeShippingOptionButtonPressed(_ sender: Any) {
delegate?.chooseShippingOptionButtonPressed()
}
}
//Second TableViewController:
class ShippingOptionsSelectionPopUpViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var selectedShippingOption : String?
var shippingOption = ["X", "Y"]
var delegate: ShippingOptionsSelectionPopUpDelegate?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShippingOptionsSelectionPopUpTableViewCell", for: indexPath) as! ShippingOptionsSelectionPopUpTableViewCell
cell.selectShippingOption(shippingOption: shippingOption[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedShippingOption = shippingOption[indexPath.row]
delegate?.popUpShippingOptionsSelected(shippingOption: selectedShippingOption!)
dismiss(animated: true, completion: nil)
}
}
Define separate protocol for popUpShippingOptionsSelected
protocol ShippingOptionsDelegate {
func popUpShippingOptionsSelected(shippingOption: String)
}
class ShippingOptionsSelectionPopUpViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var selectedShippingOption : String?
var shippingOption = ["X", "Y"]
var shippingOptiondelegate: ShippingOptionsSelectionPopUpDelegate?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShippingOptionsSelectionPopUpTableViewCell", for: indexPath) as! ShippingOptionsSelectionPopUpTableViewCell
cell.selectShippingOption(shippingOption: shippingOption[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedShippingOption = shippingOption[indexPath.row]
shippingOptiondelegate?.popUpShippingOptionsSelected(shippingOption: selectedShippingOption!)
dismiss(animated: true, completion: nil)
}
}
Add tag to cell
class PaymentMethodViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//MARK: - IBOutlets
#IBOutlet weak var PurchasedReviewItemsTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PurchasedReviewItemsTableViewCell") as! PurchasedReviewItemsTableViewCell
cell.delegate = self
cell.tag = 100 // Set tag
}
}
Update extension with ShippingOptionsDelegate
extension PaymentMethodViewController: PurchasedReviewItemsTableViewCellDelegate, ShippingOptionsDelegate {
func chooseShippingOptionButtonPressed() {
let chooseShippingOptionVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(identifier: "ShippingOptionsSelectionPopUpViewController") as! ShippingOptionsSelectionPopUpViewController
chooseShippingOptionVC.shippingOptiondelegate = self
chooseShippingOptionVC.modalPresentationStyle = .overCurrentContext
self.present(chooseShippingOptionVC, animated: true, completion: nil)
}
//MARK: Pass data from popUpView
func popUpShippingOptionsSelected(shippingOption: String) {
let cell = PurchasedReviewItemsTableView.viewWithTag(100) as! PurchasedReviewItemsTableViewCell // use tag to get cell
cell.shippingOptionsLabel.text = shippingOption
}
}

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 .

I want to create a signup Form with n number of UITextFields in custom UITableViewCell

I m creating a UITableViewCell which can display n numbers of UITextfields.Please suggest me how to declare these textfields . And also mention how to implement the cellForRowAt function for it.
Heres my UITableViewCell file
import UIKit
class CustomTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var textlab: UITextField!
func configure(text: String?, placeholder: String) {
textlab.text = text
textlab.placeholder = placeholder
textlab.accessibilityValue = text
textlab.accessibilityLabel = placeholder
}
#IBOutlet weak var underlineLabel: UILabel!
#IBOutlet weak var AlertMessage: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
AND THE VIEW CONTROLLER FOR IT IS AS FOLLOWS:
import UIKit
class CustomViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var blanklabel = ["","","",""]
var textfields = ["First Name","Last Name","Email","Contact Number"]
var alertmsg = ["Please enter your First Name","Please enter your Last Name","Please enter your Email","Please enter your Contact number",]
var textfield = UITextField()
var AlertMessage = UILabel()
#IBAction func nextLabel(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomTableViewCell
return cell
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return nil
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
It's not good idea to do it in UITableView with dynamic cells. Try to use custom view (change your cell class to subclass of UIView) in UIStackView or UITableView with static cells. And set uitextfields as properties in your viewcontroller.