Uibutton State Changing While Scrolling The Tableview with Multiple Sections - Swift

UISwitch is changing state on other cell when scroll

You need to have data array with state for every switch and in your didSelect method change data model for switch then in cellForRow at get switch state from array and your viewController need to be SwitchControlDelegate

class UIViewController:SwitchControlDelegate{

var array = [Bool](count: 200, repeatedValue:false)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Cell
cell.switchControl.isOn = array[indexPath.row]
cell.switchDelegate = self
cell.index = indexPath.row
return cell
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 200
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
array[indexPath.row] = !array[indexPath.row]
}

func switchChangeStateWithIndex(index:Int){
array[index] = !array[index]
}

This will change switch state when you click on cell , if you need to change state when change switch control you will need delegate to update data model in viewController

In your cell file:

    protocol SwitchControlDelegate: class {
func switchChangeStateWithIndex(index:Int)
}

class Cell: UITableViewCell {

var index:Int = 0
weak var switchDelegate: SwitchControlDelegate?
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
}

@IBAction func valueChanged(_ sender: AnyObject) {
switchDelegate?.switchChangeStateWithIndex(index:index)
}

}

You need to match action value change from your switch control with this function valueChanged , so every time when you change switch state cell need to call func value Changeed

How to maintain UITableViewCell state when UITableView scrolling?

You should have an array of boolean for isExpand , and in cellForRow check if it's true change height of row .

First of all make array :

var expandingStateArray = [false,false,false,false,false,false,false,false,false,false]

Then in cellForRows check state of each cell :

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: cellWithButton, for: indexPath) as! WithButtonTableViewCell

if expandingStateArray[indexPath.row] {
titleLabel.numberOfLines = 0
expandButton.setTitle("Close.", for: .normal)
}else{
titleLabel.numberOfLines = 2
expandButton.setTitle("Show More.", for: .normal)
}
cell.titleLabel.text = text
cell.expandButton.addTarget(self, action: #selector(btnPressed), for: .touchUpInside)
cell.expandButton.tag = indexPath.row
cell.layoutIfNeeded()

return cell
}
}

And in target method for button write this :

@objc func btnPressed(sender: UIButton) {

let indexPath = IndexPath(row: sender.tag, section: 0)

expandingStateArray[sender.tag] = !expandingStateArray[sender.tag]

tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: .none)
tableView.endUpdates()

}
}

UISwitch state in Tableviewcell resets when user scrolls - Swift

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()
}
}

Swift 3 UISwitch in TableViewCell loses State when scrolling

TableViewCells are reused.

That means you need to keep track of the data you are using to fill the content of the cells. If the cell content changes - such as when you tap a Switch in the cell - you need to update your datasource. When you scroll, and that row is displayed again, your data will know how to set the state of the Switch.

Here is a simple example:

//
// TableWithSwitchTableViewController.swift
// SWTemp2
//
// Created by Don Mag on 6/5/17.
// Copyright © 2017 DonMag. All rights reserved.
//

import UIKit

class MyTableViewCell: UITableViewCell {
@IBOutlet weak var myLabel: UILabel!
@IBOutlet weak var mySwitch: UISwitch!

var switchTapAction : ((Bool)->Void)?

@IBAction func switched(_ sender: UISwitch) {
print("Switched: \(sender.isOn)")

// send the Switch state in a "call back" to the view controller
switchTapAction?(sender.isOn)
}
}

// simple data object
class MyObject: NSObject {
var theTitle = ""
var theSwitchState = false

init(_ title: String) {
theTitle = title
}
}

class TableWithSwitchTableViewController: UITableViewController {

// array of MyObjects
var myData = [MyObject]()

override func viewDidLoad() {
super.viewDidLoad()

// just to make it a little easier to see the rows scroll
tableView.rowHeight = 60

// create 40 data objects for the table
for i in 1...40 {
let d = MyObject("Data Item: \(i)")
myData.append(d)
}

tableView.reloadData()

}

// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SwitchCell", for: indexPath) as! MyTableViewCell

// Configure the cell...
let d = myData[indexPath.row]
cell.myLabel.text = d.theTitle
cell.mySwitch.isOn = d.theSwitchState

// set a "Callback Closure" in the cell
cell.switchTapAction = {
(isOn) in
// update our Data Array to the new state of the switch in the cell
self.myData[indexPath.row].theSwitchState = isOn
}

return cell
}

}

Saving uibutton state in custom cell Swift

The idea implemented in objc will look like this:

  1. Create an NSArray with size equals numbers of cells.

  2. Initialise it with objects like [NSNumber numberWithBOOL:NO] (or
    YES if you need pressed state) before you call [self.tableView reloadData];

  3. In method cellForRowAtIndexPath check if
    [array[indexPath.row] boolValue] is YES or NO and set button's
    state.

Hopes it will help.

UITableView's Items Change Color After Scrolling Down, Then Back Up Swift

The cellForRowAt method is not a for loop!

I see that you are using a tag property to control what gets shown in each cell. From the fact that you increment the tag very time cellForRowAt is called, you seem to assume that cellForRowAt will be called once for each row, in order. This is not the case, and you should not implement cellForRowAt like this.

cellForRowAt essentially asks a question: "What should the cell at this index path be?", and you provide the answer. The index path that the table view is asking about is the indexPath parameter. You should make use of this parameter instead of your own tag property, because the table view is not asking about that.

The reason why why your code doesn't work is because table view cells are reused. When cells are scrolled out of view, they are not put aside, so that when new table view cells need to be shown, they can be reconfigured to "appear as if they are new cells". Essentially what this means is that when you scroll up, cellForRowAt is called for the rows that are about to come into view. You didn't expect that, did you?

All that code that sets up each cell should be moved to the initialiser of ThemeCell. Alternatively, design the cell in a storyboard. cellForRowAt should only configure a cell specifically for an index path. ThemeCell should have the properties cellButton and cellyi so that the buttons can be accessed.

Now, cellForRowAt can be written like this:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "themeCell", for: indexPath) as! ThemeCell

cell.cellButton.setImage(UIImage(named: SingletonViewController.themes[indexPath.row]), for: UIControl.State.normal)
cell.cellButton.addTarget(self, action: #selector(CustomViewController.backBTN(sender:)), for: .touchUpInside)

cell.cellyi.addTarget(self, action: #selector(CustomViewController.backBTN(sender:)), for: .touchUpInside)

if UserDefaults.standard.integer(forKey: "like") == indexPath.row {
cell.cellyi.backgroundColor = UIColor.green
} else {
cell.cellyi.backgroundColor = UIColor.red
}

// this line should be moved to viewDidLoad
// tableView.allowsSelection = false
return cell
}


Related Topics



Leave a reply



Submit