Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
342 views
in Technique[技术] by (71.8m points)

ios - Swift 3 UISwitch in TableViewCell loses State when scrolling

That's weird: I just set up a new Single-View iOS Project and put a TableView into the Main.storyboard. In this TableView I put a TableViewCell and into this Cell, I put an UILabel and an UISwitch. For this TableViewCell I created a CocoaTouchClass MyTableViewCell and set this for the TableViewCell's Class in Interfacebuilder. I connected Outlets for the UILabel and UISwitch to MyTableViewCell, as well as an action for the switch. Also I connected the TableView's dataSource and delegate to the ViewController.

So, as I think, basic stuff for setting up a table.

My ViewController looks like this:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var tableData = [[String: Bool]]()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    for index in 1...40 {
        self.tableData.append([String(index): index%2 == 0])
    }
}

func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

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


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

    let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyTableViewCell
    let object = tableData[indexPath.row].first!

    cell.myLabel.text = object.key
    cell.mySwitch.setOn(object.value, animated: false)

    return cell
}

}

So, I populate the Table with some Rows of Data and switch every second UISwitch to on.

The MyTableViewCell-Class is nothing special as well:

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

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 switched(_ sender: UISwitch) {
    print("Switched: (sender.isOn)")
}

}

Ok, fire up the iOS-Simulator and I see the Table as expected. 40 Table-Lines don't fit on one screen, so the TableView makes itself scrollable.

Now: when I change the state of one UISwitch, and drag the TableView so the changed UISwitch gets out of view and then drag the TableView so the changed UISwitch gets visible again, it is changed back to its initial state. The Switch-event gets fired like it should.

So, what am I doing wrong? Am I missing something?

I recorded a 4-seconds-Screencast to demonstrate, what's going on: http://b-bereich.de/download/swiftSwitch.mov

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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
    }


}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...