Swift UITableView 文本中的 UIStepper 和 UITextField 重叠且无法同步
Swift UIStepper and UITextField inside UITableView text overlapping and unable to syncronize
我需要一个单元格内的 UITextField 和一个 UIStepper,并在两者之一更改值时更新两者。我也使用 tableviewcontroller 达到了我的目标,但现在我有两个问题:
1 - 为了同步元素值,我创建了两个数组,用于维护每行的标签 ID。为确保所有标签都是唯一的,我从 800 开始计算步进器,从 900 开始计算文本字段。问题是当我在 table 中添加新行时,这些值总是重复的。例如,如果我添加一个新行,而不是在数组中找到 802,我会找到 802 和 803,因此我无法清楚地识别分配给每个步进器的文本。
2 - 当我通过步进器更新值时,文本字段上的文本与之前的文本重叠,如下图所示:
只有在创建 UITable 期间添加的值才会发生这种情况,如果我最近添加了一个值,则不会发生此问题。
我很清楚所有的问题都与Swift出于性能原因所做的单元重用有关,但我找不到解决方案。我的代码如下:
class barcodeInfo: Codable {
var code = ""
var quantity = 1
init(code: String, quantity : Int) {
self.code = code
self.quantity = quantity
}
}
class BarcodeTableViewController: UITableViewController, UITextFieldDelegate {
var barcodeList = [barcodeInfo]()
var QuantityMapping = [Int: Int]()
var currentStepperTag = 900
var currentQtyTextTag = 800
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 50;
self.tableView.dataSource = self
self.tableView.delegate = self
barcodeList = [barcodeInfo(code:"test", quantity: 4),barcodeInfo(code:"test1", quantity: 10)]
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var isBackSpace = Int32()
if let char = string.cString(using: String.Encoding.utf8) {
isBackSpace = strcmp(char, "\b")
}
if (isBackSpace != -92) {
if string.rangeOfCharacter(from: NSCharacterSet.decimalDigits) == nil {
return false
}
}
guard let textFieldText = textField.text,
let rangeOfTextToReplace = Range(range, in: textFieldText) else {
return false
}
let substringToReplace = textFieldText[rangeOfTextToReplace]
let count = textFieldText.count - substringToReplace.count + string.count
return count <= 5
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print(barcodeList.count)
return barcodeList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentBarcode = barcodeList[indexPath.row]
print(currentBarcode)
let cell = tableView.dequeueReusableCell(withIdentifier: "barcodeCell", for: indexPath) as UITableViewCell
cell.selectionStyle = .none
// cell.allowsSelection = false
cell.textLabel?.text = currentBarcode.code
let customStepper = UIStepper (frame:CGRect(x: self.view.frame.width - 225 , y: 5, width: 100, height: 20))
//let customStepper = UIStepper()
customStepper.autorepeat = true
// Add a function handler to be called when UIStepper value changes
customStepper.addTarget(self, action: #selector(stepperValueChanged(_:)), for: .valueChanged)
customStepper.tag = currentStepperTag
customStepper.maximumValue = 99999
customStepper.minimumValue = 1
customStepper.wraps = false
customStepper.value = Double(currentBarcode.quantity)
cell.contentView.addSubview(customStepper)
customStepper.translatesAutoresizingMaskIntoConstraints = false
customStepper.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
customStepper.widthAnchor.constraint(equalToConstant: 94).isActive = true
customStepper.heightAnchor.constraint(equalToConstant: 29).isActive = true
customStepper.rightAnchor.constraint(equalTo: cell.rightAnchor, constant: -30).isActive = true
let quantityTextField = UITextField(frame: CGRect(x: self.view.frame.width - 300, y: 5, width: 50, height: 20))
//let quantityTextField = UITextField()
quantityTextField.delegate = self
quantityTextField.smartInsertDeleteType = UITextSmartInsertDeleteType.no
quantityTextField.tag = currentQtyTextTag
quantityTextField.text = String(Int(customStepper.value))
cell.contentView.addSubview(quantityTextField)
quantityTextField.translatesAutoresizingMaskIntoConstraints = false
quantityTextField.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
quantityTextField.widthAnchor.constraint(equalToConstant: 60).isActive = true
quantityTextField.heightAnchor.constraint(equalToConstant: 21).isActive = true
quantityTextField.rightAnchor.constraint(equalTo: cell.rightAnchor, constant: -125).isActive = true
QuantityMapping[currentStepperTag] = currentQtyTextTag
print("currentQtyTextTag is now \(Int(currentQtyTextTag))")
print("currentStepperTag is now \(Int(currentStepperTag))")
currentQtyTextTag += 1
currentStepperTag += 1
return cell
}
@IBAction private func stepperValueChanged(_ sender:UIStepper!){
print(QuantityMapping)
var tagDecrement = 800
let textTag = QuantityMapping[sender.tag] ?? 0
if let currentQuantityTextField = self.view.viewWithTag(textTag) as? UITextField {
currentQuantityTextField.text = String(Int(sender.value))
if(textTag >= 900){
tagDecrement = 900
}
//let itmIdx = ((textTag - tagDecrement)-2)
let itmIdx = 1
self.barcodeList[itmIdx].quantity = Int(sender.value)
}
print("sender tag: \(Int(sender.tag))")
print("textTag \(Int(textTag))")
print("UIStepper is now \(Int(sender.value))")
}
因此,您的文本视图的解决方案可能是
cell.contentview.subviews.map(if [=10=].isKindOf(UITextView.self) { [=10=].removeFromSuperview() })
然后再做你的 addSubview(textView)
尽管为您的其他人添加了子视图,但仍要执行此操作 - 当然要进行正确的类型检查
我需要一个单元格内的 UITextField 和一个 UIStepper,并在两者之一更改值时更新两者。我也使用 tableviewcontroller 达到了我的目标,但现在我有两个问题:
1 - 为了同步元素值,我创建了两个数组,用于维护每行的标签 ID。为确保所有标签都是唯一的,我从 800 开始计算步进器,从 900 开始计算文本字段。问题是当我在 table 中添加新行时,这些值总是重复的。例如,如果我添加一个新行,而不是在数组中找到 802,我会找到 802 和 803,因此我无法清楚地识别分配给每个步进器的文本。
2 - 当我通过步进器更新值时,文本字段上的文本与之前的文本重叠,如下图所示:
只有在创建 UITable 期间添加的值才会发生这种情况,如果我最近添加了一个值,则不会发生此问题。
我很清楚所有的问题都与Swift出于性能原因所做的单元重用有关,但我找不到解决方案。我的代码如下:
class barcodeInfo: Codable {
var code = ""
var quantity = 1
init(code: String, quantity : Int) {
self.code = code
self.quantity = quantity
}
}
class BarcodeTableViewController: UITableViewController, UITextFieldDelegate {
var barcodeList = [barcodeInfo]()
var QuantityMapping = [Int: Int]()
var currentStepperTag = 900
var currentQtyTextTag = 800
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 50;
self.tableView.dataSource = self
self.tableView.delegate = self
barcodeList = [barcodeInfo(code:"test", quantity: 4),barcodeInfo(code:"test1", quantity: 10)]
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var isBackSpace = Int32()
if let char = string.cString(using: String.Encoding.utf8) {
isBackSpace = strcmp(char, "\b")
}
if (isBackSpace != -92) {
if string.rangeOfCharacter(from: NSCharacterSet.decimalDigits) == nil {
return false
}
}
guard let textFieldText = textField.text,
let rangeOfTextToReplace = Range(range, in: textFieldText) else {
return false
}
let substringToReplace = textFieldText[rangeOfTextToReplace]
let count = textFieldText.count - substringToReplace.count + string.count
return count <= 5
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
print(barcodeList.count)
return barcodeList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentBarcode = barcodeList[indexPath.row]
print(currentBarcode)
let cell = tableView.dequeueReusableCell(withIdentifier: "barcodeCell", for: indexPath) as UITableViewCell
cell.selectionStyle = .none
// cell.allowsSelection = false
cell.textLabel?.text = currentBarcode.code
let customStepper = UIStepper (frame:CGRect(x: self.view.frame.width - 225 , y: 5, width: 100, height: 20))
//let customStepper = UIStepper()
customStepper.autorepeat = true
// Add a function handler to be called when UIStepper value changes
customStepper.addTarget(self, action: #selector(stepperValueChanged(_:)), for: .valueChanged)
customStepper.tag = currentStepperTag
customStepper.maximumValue = 99999
customStepper.minimumValue = 1
customStepper.wraps = false
customStepper.value = Double(currentBarcode.quantity)
cell.contentView.addSubview(customStepper)
customStepper.translatesAutoresizingMaskIntoConstraints = false
customStepper.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
customStepper.widthAnchor.constraint(equalToConstant: 94).isActive = true
customStepper.heightAnchor.constraint(equalToConstant: 29).isActive = true
customStepper.rightAnchor.constraint(equalTo: cell.rightAnchor, constant: -30).isActive = true
let quantityTextField = UITextField(frame: CGRect(x: self.view.frame.width - 300, y: 5, width: 50, height: 20))
//let quantityTextField = UITextField()
quantityTextField.delegate = self
quantityTextField.smartInsertDeleteType = UITextSmartInsertDeleteType.no
quantityTextField.tag = currentQtyTextTag
quantityTextField.text = String(Int(customStepper.value))
cell.contentView.addSubview(quantityTextField)
quantityTextField.translatesAutoresizingMaskIntoConstraints = false
quantityTextField.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true
quantityTextField.widthAnchor.constraint(equalToConstant: 60).isActive = true
quantityTextField.heightAnchor.constraint(equalToConstant: 21).isActive = true
quantityTextField.rightAnchor.constraint(equalTo: cell.rightAnchor, constant: -125).isActive = true
QuantityMapping[currentStepperTag] = currentQtyTextTag
print("currentQtyTextTag is now \(Int(currentQtyTextTag))")
print("currentStepperTag is now \(Int(currentStepperTag))")
currentQtyTextTag += 1
currentStepperTag += 1
return cell
}
@IBAction private func stepperValueChanged(_ sender:UIStepper!){
print(QuantityMapping)
var tagDecrement = 800
let textTag = QuantityMapping[sender.tag] ?? 0
if let currentQuantityTextField = self.view.viewWithTag(textTag) as? UITextField {
currentQuantityTextField.text = String(Int(sender.value))
if(textTag >= 900){
tagDecrement = 900
}
//let itmIdx = ((textTag - tagDecrement)-2)
let itmIdx = 1
self.barcodeList[itmIdx].quantity = Int(sender.value)
}
print("sender tag: \(Int(sender.tag))")
print("textTag \(Int(textTag))")
print("UIStepper is now \(Int(sender.value))")
}
因此,您的文本视图的解决方案可能是
cell.contentview.subviews.map(if [=10=].isKindOf(UITextView.self) { [=10=].removeFromSuperview() })
然后再做你的 addSubview(textView)
尽管为您的其他人添加了子视图,但仍要执行此操作 - 当然要进行正确的类型检查