【问题标题】:UITableView Custom Cell button selects other buttons out of the main view?UITableView 自定义单元格按钮从主视图中选择其他按钮?
【发布时间】:2018-09-04 08:16:36
【问题描述】:

在我的自定义 tableviewcell 上,我有一个按钮,用户可以按下该按钮在选中和未选中之间切换。这只是一个简单的 UIButton,这个按钮包含在我的 customtableviewcell 中作为插座。

import UIKit

class CustomCellAssessment: UITableViewCell {

@IBOutlet weak var name: UILabel!
@IBOutlet weak var dateTaken: UILabel!
@IBOutlet weak var id: UILabel!
@IBOutlet weak var score: UILabel!
@IBOutlet weak var button: UIButton!


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 selectButton(_ sender: UIButton) {

if sender.isSelected{

  sender.isSelected = false

}else{
  sender.isSelected = true
} 
} 
}

奇怪的是,当我按下第一个单元格上的按钮时,它会在同一个表格视图中的另一个单元格(视图外)上选择一个按钮 8 个单元格。每个单元格都有自己的按钮,但好像使用 dequeReusableCell 会导致系统以这种方式运行。为什么要这样做,这是否与 UIbuttons 在 tableviewcells 上的工作方式有关?

谢谢

【问题讨论】:

    标签: ios swift uitableview uibutton tableview


    【解决方案1】:

    每个单元格都有自己的按钮,但好像使用 dequeReusableCell 会导致系统以这种方式运行。

    错了。 UITableViewCells 是可重用的,所以如果你的 tableView 有 8 个可见的单元格,当加载第 9 个时,单元格 nr 1 将被重用。

    解决方案:您需要跟踪单元格的状态。调用cellForRowAtIndexPath方法时,需要从头开始配置单元格。

    你可以在你的 ViewController 中有一个包含单元状态的小数组:

    var cellsState: [CellState]
    

    并在此处存储每个 indexPath 的选定状态。然后在 cellForRowAtIndexPath 中,为单元格配置状态。

    cell.selected = self.cellsState[indexPath.row].selected
    

    所以,我会做的概述是:

    1 - 在cellForRowAtIndexPath,我会设置

    cell.button.tag = indexPath.row
    cell.selected = cellsState[indexPath.row].selected
    

    2 - 将 IBAction 移动到您的 ViewController 或 TableViewController

    3 - 当点击方法被调用时,更新单元格的选中状态

    self.cellsState[sender.tag].selected = true
    

    记得始终在cellForRowAtIndexPath配置整个单元格

    编辑:

    import UIKit
    
    struct CellState {
        var selected:Bool
        init(){
            selected = false
        }
    }
    
    class MyCell: UITableViewCell {
    
        @IBOutlet weak var button: UIButton!
    
        override func awakeFromNib() {
            self.button.setTitleColor(.red, for: .selected)
            self.button.setTitleColor(.black, for: .normal)
        }
    
    }
    
    class ViewController: UIViewController, UITableViewDataSource {
    
        var cellsState:[CellState] = []
    
        @IBOutlet weak var tableView: UITableView!
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            //Add state for 5 cells.
            for _ in 0...5 {
                self.cellsState.append(CellState())
            }
        }   
    
    
        @IBAction func didClick(_ sender: UIButton) {
    
            self.cellsState[sender.tag].selected = true
            self.tableView.reloadData()
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return cellsState.count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! MyCell
            cell.button.isSelected = self.cellsState[indexPath.row].selected
            cell.button.tag = indexPath.row
            return cell
        }
    }
    

    【讨论】:

    • 感谢 crom87 的回答!如何将每个索引路径的选定状态存储到 cellsState 数组中?在 numberOfRowsInSection 中?
    • CellState 可以有一个名为 .selected 的属性。默认可以为false,当用户点击按钮时,变为true。当你知道你有多少项目时,我会创建这个数组。如果表格视图有固定数量的单元格,比如说 5,您可以在 viewDidLoad() 上创建数组。你怎么看?
    • 我刚刚用工作代码编辑了答案。你可以试试看。
    • 您的见解非常有帮助,我现在已经完成所有工作和设置。感谢您对 crom87 的帮助!
    【解决方案2】:

    当您点击按钮时,您正在设置按钮的选定状态。当单元格被重用时,按钮仍处于选中状态 - 您没有做任何事情来重置或更新该状态。

    如果按钮选择与表格单元格选择是分开的,那么您需要跟踪选定按钮的索引路径作为数据模型的一部分,然后在 @987654321 中更新按钮的选定状态@。

    表格视图只会创建所需数量的单元格来显示屏幕和一点点信息。之后,细胞被重复使用。如果单元格滚动到屏幕顶部,表格视图会将其放入重用队列中,然后当您滚动时它需要在底部显示新行时,它会将其拉出队列并将其提供给您tableView(_: cellForRowAt:)

    您在此处获得的单元格将与您之前使用 8 行左右的单元格完全相同,完全由您来配置它。您可以在cellForRow 中执行此操作,并且您还可以通过实现prepareForReuse 在单元格本身中获得机会,这在单元格出列之前被调用。

    【讨论】:

      【解决方案3】:

      在您的tableView(_: cellForRowAt:) 中为按钮单击采取行动并添加目标

      cell.button.addTarget(self, action: #selector(self.selectedButton), for: .touchUpInside)
      

      在目标方法使用下面的行来获取indexPath

      func selectedButton(sender: UIButton){
             let hitPoint: CGPoint = sender.convert(CGPoint.zero, to: self.tableView)
             let indexPath: NSIndexPath = self.tableView.indexPathForRow(at: hitPoint)! as NSIndexPath
      }
      

      然后使用 indexPath 做你的事情。 实际上,您的方法无法找到单击了哪个 indexPath 按钮,这就是为什么无法使用所需按钮的原因。

      【讨论】:

        【解决方案4】:

        解决问题的一种方法如下

        1. First create a protocol to know when button is clicked

        protocol MyCellDelegate: class {
            func cellButtonClicked(_ indexPath: IndexPath)
        }
        

        2. Now in your cell class, you could do something like following

        class MyCell: UITableViewCell {
        
        var indexPath: IndexPath?
        weak var cellButtonDelegate: MyCellDelegate?
        
        
        func configureCell(with value: String, atIndexPath indexPath: IndexPath, selected: [IndexPath]) {
                self.indexPath = indexPath  //set the indexPath
                self.textLabel?.text = value
                if selected.contains(indexPath) {
                    //this one is selected so do the stuff
                    //here we will chnage only the background color
                    backgroundColor = .red
                    self.textLabel?.textColor = .white
        
                } else {
                    //unselected
                    backgroundColor = .white
                    self.textLabel?.textColor = .red
                }
        }
        
            @IBAction func buttonClicked(_ sender: UIButton) {
                guard let delegate = cellButtonDelegate, let indexPath = indexPath else { return }
                delegate.cellButtonClicked(indexPath)
            }
        }
        

        3. Now in your controller. I'm using UItableViewController here

        class TheTableViewController: UITableViewController, MyCellDelegate {
        
            let cellData = ["cell1","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4"]
            var selectedIndexpaths = [IndexPath]()
        
        
            override func viewDidLoad() {
                super.viewDidLoad()
                tableView.register(MyCell.self, forCellReuseIdentifier: "MyCell")
            }
        
        
            override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                return cellData.count
            }
        
            override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
                return 55.0
            }
        
            override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
                cell.cellButtonDelegate = self
                cell.configureCell(with: cellData[indexPath.row], atIndexPath: indexPath, selected: selectedIndexpaths)
                return cell
            }
        
            override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
                return 30.0
            }
        
            override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                let cell = tableView.cellForRow(at: indexPath) as! MyCell
                cell.buttonClicked(UIButton())
            }
        
            func cellButtonClicked(_ indexPath: IndexPath) {
                if selectedIndexpaths.contains(indexPath) {
                    //this means cell has already selected state
                    //now we will toggle the state here from selected to unselected by removing indexPath
                    if let index = selectedIndexpaths.index(of: indexPath) {
                        selectedIndexpaths.remove(at: index)
                    }
        
                } else {
                    //this is new selection so add it
                    selectedIndexpaths.append(indexPath)
                }
        
                tableView.reloadData()
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2015-02-10
          • 1970-01-01
          • 1970-01-01
          • 2013-09-03
          • 1970-01-01
          • 1970-01-01
          • 2015-02-27
          • 2012-12-28
          • 1970-01-01
          相关资源
          最近更新 更多