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
asnumberOfRowsInSection
as showing
your cell.textLabel from that so you don't need extrastringTimeArr
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 anotherfunction
thenreload
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:
Create an NSArray with size equals numbers of cells.
Initialise it with objects like
[NSNumber numberWithBOOL:NO]
(orYES
if you need pressed state) before you call[self.tableView reloadData];
- 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
Convert Gregorian Date to Hijri Date
Nsundomanager: Capturing Reference Types Possible
Data Structure for Fast Lookup with Multiple Criteria
Swift: How to Update UI When Listening to a Stream
Closure (With Default Value) as Function Parameter
Why Is a Firestore Listener Returning .Added Twice When a Single Document Is Added
Change The 2Nd and 3Rd Pickerview Acording to What Row from The 1St Picker Is Selected
Cloudkit: How to Access Main User's Attributes
Why Run Loop Is Needed When Using Dispatchqueue.Main.Async in MAC Command Line Tool in Swift
Swiftui Sheet Shows Sheet with Wrong Data
Performance Issue with Swiftui List
Functional Programming Way of Doing Array Conversion
Adding a UIvisualeffectview to Button
Double Variable in Mkmapitem Array
Trim Video Always Fail When Use Avassetexportpresetpassthrough