【问题标题】:Table View Data is overridden表视图数据被覆盖
【发布时间】:2020-04-02 15:46:30
【问题描述】:

我有一个 UITableView。它的单元格包含一个标签,该标签将显示一个问题、一个是按钮和一个否按钮。目标是一一查看问题。 首先我调用 API 来获取 viewDidLoad 方法中的问题:

override func viewDidLoad() {
        super.viewDidLoad()
        tableView.allowsSelection = false

        getQuestions(baseComplainID: "1") { (questions, error) in
            self.questions = questions
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }

在 cellForRowAt 方法中,我将它们一一显示:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else {
            fatalError("Fatal Error")
        }
        cell.yesButton.isHidden = false
        cell.noButton.isHidden = false

        if indexPath.row + 1 == displayNumber {
            cell.questionLabel.text = questions[indexPath.row].question_name
        } else {
            cell.yesButton.isHidden = true
            cell.noButton.isHidden = true
        }

        cell.yesButton.addTarget(self, action: #selector(action), for: .touchUpInside)
        cell.noButton.addTarget(self, action: #selector(action), for: .touchUpInside)

        return cell
    }

这是在单击是或否时执行的操作:

@objc func action(sender: UIButton){
        let indexPath = self.tableView.indexPathForRow(at: sender.convert(CGPoint.zero, to: self.tableView))
        let cell = tableView.cellForRow(at: indexPath!) as? TableViewCell
        cell?.yesButton.isEnabled = false
        cell?.noButton.isEnabled = false

        if sender == cell?.yesButton {
            sender.setTitleColor(.black, for: .normal)
            sender.backgroundColor = .green
        } else {
            sender.setTitleColor(.black, for: .normal)
            sender.backgroundColor = .green
        }

        displayNumber += 1
        self.tableView.reloadData()
    }

这里我只是改变按钮的背景颜色并增加显示编号以显示下一个问题。

除了滚动时,所有这些都完美无缺,数据被覆盖,有时我发现问题标签为空并且问题相互替换。由于单元格可重复使用,我知道这是正常的,但我不知道如何解决。

有什么建议吗?

【问题讨论】:

  • 细胞被重复使用。将所有信息保留在数据模型中,然后修改模型并重新加载行。

标签: ios swift uitableview tableview


【解决方案1】:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else {
            fatalError("Fatal Error")
        }
        cell.yesButton.isHidden = false
        cell.noButton.isHidden = false

        if indexPath.row + 1 == displayNumber {
            cell.questionLabel.text = questions[indexPath.row].question_name
        } else {
            cell.yesButton.isHidden = true
            cell.noButton.isHidden = true
        }

        cell.yesButton.addTarget(self, action: #selector(action), for: .touchUpInside)
        cell.noButton.addTarget(self, action: #selector(action), for: .touchUpInside)

        return cell
    }

我觉得您的问题在于 cellForRowAt 函数。

你已经写好了

if indexPath.row + 1 == displayNumber { your code here }

但我不确定你为什么需要这个。

你应该在 cellForRowAt 中做这样的事情

let data = self.questions
data = data[indexPath.row]
cell.questionLabel.text = data.question_name

您不应该将 1 添加到您的 indexPath.row

【讨论】:

    【解决方案2】:

    您将需要跟踪每个单元格的“是”、“否”和“否”。我会将枚举与您的问题一起添加到另一个数据结构上。您的主要问题是您只跟踪您的问题。您还需要跟踪您的答案。这样,当你加载一个单元格时,你可以在cellForRow(at:)中为每个按钮配置你想要的颜色

    struct QuestionAndAnswer {
        enum Answer {
            case yes
            case no
            case nada
        }
    
        var question: Question
        var answer: Answer
    }
    

    并尽量不要在按下按钮时重新加载整个 tableView。 tableView.reloadData() 很昂贵并且会分散用户的注意力。您应该只重新加载按下按钮时更改的行。

    在您的单元格上添加回调,以便您知道相应按钮属于哪个单元格。请注意,在 onYes 和 onNo 回调中,我们如何跟踪您的“是”或“否”选择,然后立即重新加载下面的行。当重新加载该行时,我们终于知道该按钮的颜色。

    class AnswerCell: UITableViewCell {
        @IBOutlet weak var yesButton: UIButton!
        @IBOutlet weak var noButton: UIButton!
    
        var onYes: (() -> Void)) = {}
        var onNo: (() -> Void)) = {}
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        // ...
    
        cell.yesButton.backgroundColor = qAndA.answer == .yes ? .green : .white
        cell.noButton.backgroundColor = qAndA.answer == .no ? .green : .white
    
        cell.onYes = {
            questionsAndAnswers[indexPath.row].answer = .yes
            tableView.reloadRows(at: [indexPath], with: .fade)
        }
        cell.onNo = {
            questionsAndAnswers[indexPath.row].answer = .no
            tableView.reloadRows(at: [indexPath], with: .fade)
        }
    
        // ...
    }
    

    【讨论】:

    • 您能否详细说明您的意思?请假设我是新手:D
    • @MostafaHendawi 我用更多细节更新了我的答案。我希望澄清一下,如果没有,请问我一个具体问题,我一定会提供帮助。
    • 基本上我的主要问题是当我滚动时问题会丢失。部分数据被替换。例如,第 6 题现在在第 11 位。这是我的主要问题
    • 你需要去掉 displayNumber。那是在搞砸你。只需使用 indexPath.row 来跟踪您的单元格编号。
    • 那怎么才能一一显示问题呢?
    【解决方案3】:

    假设你有 10 个问题,所以一个非常简单的解决方法是声明一个包含 10 个元素的新数组,如下所示

    var questionIsLoaded = Array(repeating:true , count 10)
    

    上一行将声明一个包含 10 个元素的数组,每个元素都是 bool,在我们的例子中是 true

    然后声明一个处理问题是否加载的函数,如下所示,因此如果问题被加载,则带有 indexPath 的问题应标记为 true,因此,yes 和 no 按钮应隐藏否则,应该显示按钮

    func handleQuestionIfLoaded(cell:yourCellType, indexPath:IndexPath) {
    if questionIsLoaded[indexPath.row] , indexPath.row + 1 == displayNumber { {
    questionIsLoaded[indexPath.row] = false
                cell.questionLabel.text = questions[indexPath.row].question_name
                cell.yesButton.isHidden = questionIsLoaded[indexPath.row]
                cell.noButton.isHidden = questionIsLoaded[indexPath.row]
            } else {
                cell.yesButton.isHidden = questionIsLoaded[indexPath.row]
                cell.noButton.isHidden = questionIsLoaded[indexPath.row]
            }
    
            cell.yesButton.addTarget(self, action: #selector(action), for: .touchUpInside)
            cell.noButton.addTarget(self, action: #selector(action), for: .touchUpInside)
    }
    

    然后用上面的函数替换cellForRowAt的body,那么你的action函数会如下

    @objc func action(sender: UIButton){
            let indexPath = self.tableView.indexPathForRow(at: sender.convert(CGPoint.zero, to: self.tableView))
            let cell = tableView.cellForRow(at: indexPath!) as? TableViewCell
            cell?.yesButton.isEnabled = questionIsLoaded[indexPath.row]
            cell?.noButton.isEnabled = questionIsLoaded[indexPath.row]
    
        if sender == cell?.yesButton {
            sender.setTitleColor(.black, for: .normal)
            sender.backgroundColor = .green
        } else {
            sender.setTitleColor(.black, for: .normal)
            sender.backgroundColor = .green
        }
    
        displayNumber += 1
        self.tableView.reloadData()
    }
    

    现在,您的单元格依赖于一个外部依赖项,即您之前声明的数组,这意味着当单元格出列时,它们将根据问题是否已加载而被重用,方法是询问数组的元素如果元素为真或假,则首先指定 indexPath

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-19
      • 2015-10-25
      • 1970-01-01
      • 2011-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-27
      相关资源
      最近更新 更多