在UITableView的自定义原型单元格中,UITextField的值被其他原型单元格替换

3
我已经使用了一个UITableView,在其中使用了2个prototype cell,在这些原型单元格中又使用了1个UITextField
现在当我运行应用程序并在第一个原型单元格中输入值时,然后滚动UITableView,那么第一个原型单元格的textfield值就会替换为最后一个原型单元格的textfield值。所以每次我向下或向上滚动时都会发生这种情况。有时它会变成空白。
这是我的代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   if(indexPath.row==0)
   {

    Client_MobileCell *cell     = [tableView dequeueReusableCellWithIdentifier:cellIdentifier_MobileCell forIndexPath:indexPath];
    cell.lblTitle.text          = [arrTitle objectAtIndex:indexPath.row];
    cell.txtMobile.placeholder  = [arrPlaceholder objectAtIndex:indexPath.row];
   // cell.txtMobile.tag = indexPath.row+100;
    return cell;
}
else
{
//        Client_Common_Cell *cell = (Client_Common_Cell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier_CommonCell forIndexPath:indexPath];
    Client_Common_Cell *cell = (Client_Common_Cell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier_CommonCell];

   // cell.txtCommon.tag = indexPath.row+100;
    cell.delegate = self;

    if(indexPath.row == 1 || indexPath.row == 2 || indexPath.row == 3 || indexPath.row == 4){
        if(indexPath.row == 2)
            cell.txtCommon.secureTextEntry = YES;
        else if(indexPath.row == 4){
            cell.txtCommon.keyboardType = UIKeyboardTypeEmailAddress;
        }
        cell.imgArrow.hidden = YES;
        cell.btnSelection.hidden = YES;
    }
    else{
        cell.btnSelection.hidden = NO;
        cell.imgArrow.hidden = NO;
    }
        cell.lblTitle.text       = [arrTitle objectAtIndex:indexPath.row];
        cell.txtCommon.placeholder      = [arrPlaceholder objectAtIndex:indexPath.row];
        return cell;
}

}


将NSDictionary中的值保存起来,并在滚动时从那里分配。 - Fahad Jamal
@James:怎么做?你能给我一个例子吗? - Nirav
创建一个NSMutableDictionary,并在您输入数据时将setObject添加到字典中,我指的是textfield代理。 - Fahad Jamal
那么如何将这些值分配给单元格文本字段?我的意思是,我是否需要将字典存储在NSMutableArray中? - Nirav
你需要声明一个包含两个值的nsmutable字典的nsmutable数组。将要显示的文本存储在“text”键下,并将文本字段的索引存储在“index”键下。现在,在uitextfield的委托函数中,通过从cellforrowatindexpath设置textfield的标记来检测用户正在输入哪个textfield,并将更改后的textfield值存储在相应键的字典中。同时,不要忘记在cellforrowatindexpath函数中填充textfield中的值。如果还有不理解的地方,请告诉我。 - Mahesh Agrawal
@MaheshAgrawal:已经解决了,非常感谢。 - Nirav
2个回答

3

TL;DR (只给我代码)

Github 链接: https://github.com/starkindustries/CustomTableView

问题

正如其他人指出的那样,dequeueReusableCellWithIdentifier在滚动时会重用相同的单元格引用。请查看下面的 GIF 动画以演示此问题。

GIF 中的表格有 20 行(比屏幕能容纳的行数多)。每一行都有一个文本字段。我在第一行到第四行分别输入了 "one"、"two"、"three" 和 "four"。

当我向下滚动时,"two"、"three" 和 "one" 神奇地出现在底部(不需要的行为)。这是因为它们在从顶部滚动时被取消排队并在底部再次重用了。

enter image description here

讨论

经过大量关于网络和堆栈溢出的研究后,建议您在更改文本字段时将其值存储在数组中。然后,您可以在 cellForRowAt 方法中设置文本字段文本。@Rob 在这里的答案中完美地解释了如何使用模型-视图-控制器模式来解决这个问题:https://stackoverflow.com/a/38272077/2179970。我已经在下面的示例中实现了他的建议。

它是如何工作的

  1. 首先,您需要设置一个协议,以便自定义 UITableViewCell(视图)可以向 ViewController 发送消息,通知其其文本字段已更改。ViewController 将实现该协议。单元格将调用协议方法。
  2. 您的应用程序用户会输入文本。这将触发自定义 UITableViewCell 类中的 textFieldDidChange(_:)
  3. textFieldDidChange(_:) 将调用 TableViewController 的代理方法 cell(cell: UITableViewCell, updatedCustomTextField textField: UITextField) 以通知控制器其文本已更改。
  4. 控制器将更新其文本数组(模型)以保存更改。
  5. 如果需要重新使用,cellForRowAt 将适当地更新单元格的文本字段内容。

解决方案

协议:

// Protocol from Step 1
protocol CustomCellDelegate: class {
    // This is the function that the ViewController will implement
    func cell(cell: UITableViewCell, updatedCustomTextField textField: UITextField)
}

自定义UITableViewCell:

class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
    // Protocol reference (Step 1) 
    weak var delegate: CustomCellDelegate?
    @IBOutlet weak var textField: UITextField!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        // (Step 2) Add a "textFieldDidChange" notification method to the text field control.
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: UIControlEvents.editingChanged)
    }

    // Step 2
    func textFieldDidChange(_ textField: UITextField) {
        delegate?.cell(cell: self, updatedCustomTextField: textField)
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        // Configure the view for the selected state
    }

}

视图控制器:

class MyTableViewController: UITableViewController, CustomCellDelegate {

    var myTexts: [String?]!

    override func viewDidLoad() {
        super.viewDidLoad()

        myTexts = Array(repeating: nil, count: 20)

        let nib = UINib(nibName: "MyTableViewCell", bundle: nil)
        self.tableView.register(nib, forCellReuseIdentifier: "MyTableViewCell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

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

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("CellForRowAt: " + indexPath.row.description)
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath) as! MyTableViewCell
        // Step 5: TableView updates cell contents if reused
        cell.delegate = self
        cell.textField.text = myTexts[indexPath.row]
        return cell
    }

    // MARK: - CustomCellDelegate Method 
    // Step 3: The cell will call this protocol method to message the controller
    func cell(cell: UITableViewCell, updatedCustomTextField textField: UITextField) {
        // when the cell tells us that its text field's value changed, update our own model
        if let indexPath = tableView.indexPath(for: cell), let string = textField.text {
            // Step 4: The controller updates the model:
            print("delegate method cell updatedCustomTextField")
            myTexts[indexPath.row] = string
        }
    }
}

结果

我像之前一样在前四行中输入相同的“one”,“two”,“three”,“four”。这次,表格的行为符合预期:文本字段的内容出现在我放置它们的位置,并且它们不会随机消失/出现。

enter image description here

Github链接

这里是项目的链接:https://github.com/starkindustries/CustomTableView

参考资料

初始化带有nib的自定义UITableViewCell而不使用dequeueReusableCellWithIdentifier

在UITableView中的UITextFields中,输入的值会重新出现在其他单元格中

UITableViewCell中的UITextField和模态视图中的验证


1
当您使用dequeueReusableCellWithIdentifier时,您的单元格将被重用。这意味着当用户滚动tableview时,移出屏幕的cell将被重用以显示即将移动到屏幕上的cell的内容。
尽管这有助于节省内存,但它引起的问题是在加载新cell的内容之前,需要准备显示cell
在您的情况下,似乎您需要维护用户在celltextfield中输入的值。
因此,要解决您的问题,如果tableview中没有那么多cells,只需停止重用cell(为每个单元格实例使用alloc-init/new)。从您的代码中看,您少于10个单元格,这将是最快解决问题的方法。
否则,如果有大量的单元格,每当用户在单元格的textfield中输入一个值时,请将其保存在一个array中。然后从数组中获取该值,并将其设置为cellForRowAtIndexPath方法中的textfield的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接