在 UITableView 中使用文本字段时如何避免记录重影值

问题描述 投票:0回答:1

quantityTextfields 获取幽灵值(用户未在文本字段中输入的值)即使仅更改一个文本字段,我认为这是因为 TableView 单元格如何重用,但如何解决它

下面是quantityTextFields的声明

  private var quantityTextFields: [Int:[String: UITextField]] = [:] // Map material codes to text fields




extension CompetitorSaleThruVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    selectedMaterials.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
   let cell = tblView.dequeueReusableCell(withIdentifier: "competitorCell", for: indexPath) as! CompetitorCell
    
    let material = selectedMaterials[indexPath.row]
    
    cell.quantityTextField.text = ""
   
    cell.label.text = material.value
    
    //quantityTextFields[material.value] = cell.quantityTextField
    
 
    quantityTextFields[indexPath.row] = [material.value:cell.quantityTextField]
  

    
   // cell.quantityTextField.text = textFieldValues[indexPath.row]
    cell.quantityTextField.tag = indexPath.row
    
    cell.selectionStyle = .none
    
    cell.quantityTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    
    cell.quantityTextField.delegate = self
    
    return cell
    
    if navigatedFromCompetitorDisplayCell == true && material.value == competitorMaterial {
        cell.quantityTextField.text = String(compSaleQuantity)
    }
    
    
    
    
}
}
ios swift mobile uikit tableview
1个回答
0
投票

单元格被重用...尝试跟踪单元格中的 UI 元素(例如文本字段)并按照您的方式处理其更改很容易出现错误和“不匹配”。

更好的方法是在单元格类中处理文本字段编辑,然后使用“回调”闭包让控制器知道该字段已被编辑。

因此,我们向单元类添加这样的闭包:

class CompetitorCell: UITableViewCell, UITextFieldDelegate {
    
    // "callback" closure
    var quantityEdited: ((CompetitorCell, Int) -> ())?

在单元类中,我们添加目标并处理编辑:

// in cell init
{
    // action when field is edited
    quantityTextField.addTarget(self, action: #selector(edited(_:)), for: .editingChanged)
}   

@objc func edited(_ sender: UITextField) {
    let str = sender.text ?? ""
    // execute the "callback" closure
    self.quantityEdited?(self, Int(str) ?? 0)
}

然后,在您的控制器中

cellForRowAt

    // set the "callback" closure
    cell.quantityEdited = { [weak self] theCell, theValue in
        
        // safely unwrap optionals
        guard let self = self else { return }
        guard let indexPath = self.tableView.indexPath(for: theCell) else { return }
        
        // update our data
        self.materials[indexPath.row].quantity = theValue
        
    }

这是一个完整的示例 - 没有

@IBOutlet
@IBAction
连接...只需将空白视图控制器的自定义类分配给
CompTableVC


简单数据结构

struct Material {
    var name: String = ""
    var quantity: Int = 0
}

单元格类 - 带有标签和文本字段

class CompetitorCell: UITableViewCell, UITextFieldDelegate {
    
    // "callback" closure
    var quantityEdited: ((CompetitorCell, Int) -> ())?
    
    static let identifier: String = "CompetitorCell"
    
    let label = UILabel()
    let quantityTextField = UITextField()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        label.translatesAutoresizingMaskIntoConstraints = false
        quantityTextField.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(label)
        contentView.addSubview(quantityTextField)
        
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            
            label.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            label.heightAnchor.constraint(equalToConstant: 42.0),

            quantityTextField.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 8.0),
            
            quantityTextField.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            quantityTextField.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            quantityTextField.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            
        ])
        
        quantityTextField.borderStyle = .roundedRect
        quantityTextField.keyboardType = .numberPad
        
        // action when field is edited
        quantityTextField.addTarget(self, action: #selector(edited(_:)), for: .editingChanged)
        
        // so we can see the framing
        label.backgroundColor = .cyan
        quantityTextField.backgroundColor = .yellow
        
    }
    
    @objc func edited(_ sender: UITextField) {
        let str = sender.text ?? ""
        // execute the "callback" closure
        self.quantityEdited?(self, Int(str) ?? 0)
    }
}

示例控制器类

class CompTableVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var materials: [Material] = []
    
    let tableView = UITableView()
    let totalLabel = UILabel()
    var doneButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)

        var cfg = UIButton.Configuration.filled()
        cfg.title = "Done"
        doneButton = UIButton(configuration: cfg, primaryAction: UIAction() { _ in
            self.view.endEditing(true)
        })
        
        totalLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(totalLabel)
        
        doneButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(doneButton)
        
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            totalLabel.centerYAnchor.constraint(equalTo: doneButton.centerYAnchor, constant: 0.0),
            totalLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            totalLabel.heightAnchor.constraint(equalTo: doneButton.heightAnchor),

            doneButton.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            doneButton.leadingAnchor.constraint(equalTo: totalLabel.trailingAnchor, constant: 20.0),
            doneButton.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),

            tableView.topAnchor.constraint(equalTo: doneButton.bottomAnchor, constant: 20.0),
            
            tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            
        ])
        
        totalLabel.backgroundColor = .systemRed
        totalLabel.textColor = .white
        
        tableView.register(CompetitorCell.self, forCellReuseIdentifier: CompetitorCell.identifier)
        tableView.dataSource = self
        tableView.delegate = self
        
        // let's create some sample data
        materials = (0..<20).map {
            let formatter = NumberFormatter()
            formatter.numberStyle = .spellOut
            var m: Material = Material()
            m.name = formatter.string(for: $0)!
            m.quantity = 1
            return m
        }
        
        // handle keyboard covering cells...
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
        
        // only show "Done" button if editing is active
        doneButton.isHidden = true
        
        // update the "Total" label
        updateTotal()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return materials.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: CompetitorCell.identifier, for: indexPath) as! CompetitorCell
        
        // set the label and text field
        cell.label.text = "Material: " + materials[indexPath.row].name
        let q = materials[indexPath.row].quantity
        cell.quantityTextField.text = q > 0 ? "\(q)" : ""
        
        // set the "callback" closure
        cell.quantityEdited = { [weak self] theCell, theValue in
            
            // safely unwrap optionals
            guard let self = self else { return }
            guard let indexPath = self.tableView.indexPath(for: theCell) else { return }
            
            // update our data
            self.materials[indexPath.row].quantity = theValue
            
            // update "Total" label
            self.updateTotal()
            
        }
        
        return cell
    }
    
    func updateTotal() {
        let t = materials.reduce(0) { $0 + $1.quantity }
        totalLabel.text = "Total: \(t)"
    }
    
    @objc func keyboardWillShow(notification: NSNotification) {
        guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
        
        // Get the size of the keyboard
        let keyboardHeight = keyboardFrame.height
        
        // Adjust the tableView's contentInset and scrollIndicatorInsets
        let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
        tableView.contentInset = contentInsets
        tableView.scrollIndicatorInsets = contentInsets
        
        // Scroll to the active text field if it's hidden by the keyboard
        if let activeTextField = findActiveTextField(),
           let cell = activeTextField.superview?.superview as? UITableViewCell,
           let indexPath = tableView.indexPath(for: cell) {
            tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
        }
        
        self.doneButton.isHidden = false
    }
    
    @objc func keyboardWillHide(notification: NSNotification) {
        // Reset the tableView's contentInset and scrollIndicatorInsets
        let contentInsets = UIEdgeInsets.zero
        tableView.contentInset = contentInsets
        tableView.scrollIndicatorInsets = contentInsets

        self.doneButton.isHidden = true
    }
    
    func findActiveTextField() -> UITextField? {
        for cell in tableView.visibleCells {
            if let textField = cell.contentView.subviews.compactMap({ $0 as? UITextField }).first(where: { $0.isFirstResponder }) {
                return textField
            }
        }
        return nil
    }
}

运行时看起来像这样:

enter image description here

enter image description here

您将能够编辑每个单元格中的“数量”...它将更新数据源...并且您可以上下滚动而不会出现“幽灵值”问题。

© www.soinside.com 2019 - 2024. All rights reserved.