从特定单元获取开关状态

时间:2018-08-06 16:50:54

标签: ios swift uitableview uiswitch

我正在使用一个具有两个itemSelectedService#setAsSelected()的iOS应用。第一个是ViewControllers,它为数组中的每个索引创建一行。 TableView的每个单元格显示数组中与索引相对应的内容,并具有一个开关。第二个TableView有一个图像和一个标签,它们应该根据开关状态而改变。那么,如何从特定单元获取开关状态?

ViewController

}

3 个答案:

答案 0 :(得分:2)

为开关使用附件视图似乎是一个简单的解决方案,但是访问该视图非常麻烦。 for v in cell?.subviews ?? []之类的处理标签的事情太可怕了。

更有效的解决方案是自定义单元格类。

在Interface Builder中,将单元格的样式设置为custom,然后将UILabelUISwitch拖到画布中。将单元格的类别设置为TableViewCell

添加一个新的CocoaTouch类TableViewCell作为UITableViewCell的子类。您需要两个IBOutlet,一个IBAction和一个callback变量。回调对于在模型中保持开关状态很重要。连接插座和IB中的操作。

class TableViewCell: UITableViewCell {

    @IBOutlet weak var switcher : UISwitch!
    @IBOutlet weak var label : UILabel!

    var callback : ((Bool)->())?

    @IBAction func switchChanged(_ sender : UISwitch) {
        callback?(sender.isOn)
    }
}

创建一个包含文本和开关状态的数据源模型

struct Item {
    var text : String
    var isSelected : Bool 

    init(text : String, isSelected : Bool = false {
        self.text = text
        self.isSelected = isSelected
    }
}

声明数据源数组

var arr : [Item] = [Item(text: "Text1"), Item(text: "Text2"), Item(text: "Text3"), Item(text: "Text4")]

代替cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
    let item = arr[indexPath.row]
    cell.label.text = item.text
    cell.switcher.isOn = item.isSelected

    // the callback updates the model and is called when the value of the switch changes
    cell.callback = { newValue in
         item.isSelected = newValue
    }

    return cell
}

didSelectRow替换为(是,只有一行,它通过索引路径作为sender参数)

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    self.performSegue(withIdentifier: "segue", sender: indexPath)
}

最终实施prepare(for segue

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "segue" {
        let viewController = segue.destination as! ViewController // the class of the second view controller
        // get the current index path
        let indexPath = sender as! IndexPath
        let item = arr[indexPath.row]
        // get the state of the switch from the model, not from the view 
        let isSelected = item.isSelected
        // do something with `isSelected`  
        }
    }
}

答案 1 :(得分:1)

为了正确实现所需的功能,您将需要设置一个自定义单元。

下面是一个示例,并假定使用了Storyboard / XIB UI:

import UIKit

class SwitchTableViewCell: UITableViewCell {
    @IBOutlet weak var textLabel: UILabel!
    @IBOutlet weak var contentSwitch: UISwitch!

    // So that we can identify the cell in our table view controller.
    static let identifier: String {
        return String(describing: type(of: self))
    }
}

,以便将其用于表格视图。您将必须注册要在SwitchTableViewController.viewDidLoad()中使用的单元格:

tableView.register(SwitchTableViewCell.self, forCellReuseIdentifier: SwitchTableViewCell.identifier)

接下来,您将要修改cellForRowAt

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(SwitchTableViewCell.identifier, forIndexPath: indexPath) as! SwitchTableViewCell
    cell.textLabel?.text = arr[indexPath.row]
    // cell.contentSwitch will be setup as an outlet via Storyboard / XIB.
    return cell
}

完成后,继续并将变量添加到SwitchTableViewController

fileprivate var selectedState: UIControl.State?

并更新didSelectRowAt以存储单元格中的状态:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRowAtIndexPath(indexPath) as! SwitchTableViewCell
    selectedState = cell.contentSwitch.state
    segueIdentifier = "segue" // probably want a more meaningful segue name here.
    self.performSegue(withIdentifier: segueIdentifier, sender: self)
}

最后,覆盖prepare(for:sender:)

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "segue" {
        if let vc = segue.destination as? ContentViewController { // cast accordingly, 'ContentViewController' is placeholder
            // pass the state to the destination view controller
            vc.state = selectedState
            selectedState = nil
        }
    }
}

到此就完成了!

答案 2 :(得分:-1)

有许多方法可以从单元中读取开关的状态。您可以创建一个自定义单元类并使用IBOutlets访问该单元类,甚至可以使用委托从“自定义单元”类返回到View Controller。如果您出于学习目的而使用此代码,则可以像这样在单元格中使用并添加任何类型的控件,但是在实际项目中,您可以尝试使用“自定义”单元格。

查看代码中的注释区域

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet weak var myimg: UIImageView!
   var arr: [String] = ["bla", "blablabla", "blabla"]

   override func viewDidLoad() {
    super.viewDidLoad()
    myimg?.image = UIImage(named: "Image1")
}

override func didReceiveMemoryWarning() {
     super.didReceiveMemoryWarning()
}

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

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->   UITableViewCell {
    let cell = UITableViewCell(style: UITableViewCellStyle.default,
    reuseIdentifier: "cell")
    cell.textLabel?.text = arr[indexPath.row]
    let mySwitch = UISwitch()

    // Add a tag to your switch so later on you can access the switch using this tag
    mySwitch.tag = 1001

    cell.accessoryView = mySwitch
    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
     segueIdentifier = "segue"

     // Get the selected Cell and Iterate through it's subviews to find the switch using the tag
     let cell = tableView.cellForRow(at: indexPath)

     // Iterate through subviews of Cell
     for v in cell?.subviews ?? [] {

        // If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
        if v.tag == 1001 {

            // One last check we cast the view to UISwitch if it succeed then it's the switch view
            if let mySwitch = v as? UISwitch {

               // Here you can get the state of the switch
                let switchState = mySwitch.state

            }
        }
    }
      self.performSegue(withIdentifier: segueIdentifier, sender: self)
   }
}

就像我说的那样,这不是使用标签添加和读取视图的最佳方法,但仍然很高兴知道

编辑:

这是您的项目正常运行的完整解决方案。您已经有一个ViewController,但没有要隔离的DetailViewController

查看控制器代码

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {


let rooms: [String] = ["Kitchen","Living Room", "Master's Bedroom", "Guest's Bedroom"]
let segueIdentifier = "segueIdentifier"
var switch_isOn = false


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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
    cell.textLabel?.text = rooms[indexPath.row]

    let mySwitch = UISwitch()
    cell.accessoryView = mySwitch

    mySwitch.tag = 1001

    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    // Get the selected Cell and Iterate through it's subviews to find the switch using the tag
    let cell = tableView.cellForRow(at: indexPath)

    // Iterate through subviews of Cell
    for v in cell?.subviews ?? [] {

        // If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
        if v.tag == 1001 {
            // One last check we cast the view to UISwitch if it succeed then it's the switch view
            if let mySwitch = v as? UISwitch {
                // Assign the current state of the switch to switch_isOn variable
                self.switch_isOn = mySwitch.isOn
            }
        }
    }
    self.performSegue(withIdentifier: segueIdentifier, sender: indexPath)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == segueIdentifier {
        if let detailViewController = segue.destination as? DetailViewController {
            // used guard let to be on safe side
            guard let indexPath = sender as? IndexPath else { return }
            // pass in the data needs to the detail view controller
            detailViewController.descr = rooms[indexPath.row]
            detailViewController.isOn = switch_isOn
        }
    }
}
}

详细信息视图控制器代码

import UIKit

class DetailViewController: UIViewController {


@IBOutlet weak var descr_label: UILabel!
@IBOutlet weak var state_label: UILabel!
@IBOutlet weak var myImageView: UIImageView!

var descr = ""
var isOn = false

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    descr_label.text = descr
    // for UIImage you can use UIImage(named: "on_image") but i have used the imageLiteral which is pics directly the image from xcassets 
    myImageView.image = isOn ? #imageLiteral(resourceName: "on_image") : #imageLiteral(resourceName: "off_image")
    state_label.text = isOn ? "Switch is ON" : "Switch is Off"

}

}

Example Project download here