UICollectionViewCells 中的 UIPickerViews 中的 3 个 UILabel 之间不需要的交互

Posted

技术标签:

【中文标题】UICollectionViewCells 中的 UIPickerViews 中的 3 个 UILabel 之间不需要的交互【英文标题】:Unwanted interactions between 3 UILabels within UIPickerViews within UICollectionViewCells 【发布时间】:2019-07-26 09:30:48 【问题描述】:

使用 UICollectionViewCells 网格中列顶部的选择器来独立控制每列的​​内容:

每个选择器应仅更新该列中单元格的值(仅更新图像中的第 1 列)。

在数据更新的同时,pickers的标签变得很混乱!

1) 当用户在第 1 列或第 4 列的顶部选择一个新值时 - 例如从阳性变为中性。

1a) 列的数据更新为中性 - 但使用的选择器上的标签恢复为之前的值 - 阳性。

1b) 同时,未选择的选择器的标签(第 1 列或第 4 列 - 非 3)更新为在选择的选择器中选择的值(不更改未选择列中的值)

在图片中,第 1 列已从阳性更新为中性(Das Kind 为中性;den Ball 为阳性):

2) 选择值时第 3 列不同。

2a) 数据和选择器标签正确更新(不会恢复到之前的选择器标签值)。

2b) 如果第 1 列和第 4 列有不同的标签 - 例如 col 1 Masculine 和 col 4 Neutral,它们会交换选择器标签值(不会更改它们的数据)!

我正在使用 item 方法的集合视图单元格在自定义集合视图单元格中实例化我的选择器。我正在使用 indexPath.item 来区分单元格和选择器 (caseOfPickers) - 至少似乎可以控制数据。

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

    var cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseI4CVCell, for: indexPath) as! CVCell

    switch indexPath.item
    
        case 0, 2, 3:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseI4PickerCVCell, for: indexPath) as! PickerCVCell
            cell.caseOfPicker = indexPath.item
            cell.reference = self
            return cell
    ...

然后,选择器所在的整个自定义集合视图单元格是:

import UIKit
import CoreData

class PickerCVCell: UICollectionViewCell, UIPickerViewDelegate, UIPickerViewDataSource

var reference: EgPickerDelegate?
var caseOfPicker: Int?

private var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>?

@IBOutlet weak var pickerInCVCell: UIPickerView!

override func awakeFromNib()

    super.awakeFromNib()
    configureFetchedResultsController()

    pickerInCVCell.delegate = self
    pickerInCVCell.dataSource = self


private func configureFetchedResultsController()

    ...


func numberOfComponents(in pickerView: UIPickerView) -> Int
 return 1 

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
 return 4 

func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView

    var pickerLabel: UILabel? = (view as? UILabel)
    let path = NSIndexPath(item: row, section: 0)
    if let gender = fetchedResultsController?.object(at: path as IndexPath) as? Gender
    
        let text = gender.text ?? "Text not found"
        print("pickerText", text)

        if pickerLabel == nil
        
            pickerLabel = UILabel()
            pickerLabel?.textColor = UIColor.black
            pickerLabel?.textAlignment = NSTextAlignment.center
            pickerLabel?.font = pickerLabel?.font.with( .traitBold, .traitItalic)
        
        pickerLabel?.text = text
    

    return pickerLabel!


reference?.myPickerDidSelectRowInCase(selectedRowValue: row, caseOfPicker: caseOfPicker) 

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
 reference?.myPickerDidSelectRowInCase(selectedRowValue: row, caseOfPicker: caseOfPicker) 

func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat
 return CGFloat(120.0) 

回到控制collection view的自定义类中,picker委托方法是:

extension EgEndingsCVTVCell: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, EgPickerDelegate

func myPickerDidSelectRowInCase(selectedRowValue: Int?, caseOfPicker: Int?)

    switch caseOfPicker
    
    case 0:
        col0GenderPredicate = genderPredicator(selectedRowValue: selectedRowValue ?? 0)
    case 2:
        col2GenderPredicate = genderPredicator(selectedRowValue: selectedRowValue ?? 0)
    case 3:
        col3GenderPredicate = genderPredicator(selectedRowValue: selectedRowValue ?? 0)
    default:
        col0GenderPredicate = genderPredicator(selectedRowValue: 0)
    
loadSavedData()     // load data using new predicates
cView.reloadData()

不知道为什么选择器的标签会混淆 - 它们不是作为单独的对象实例化的吗?

【问题讨论】:

【参考方案1】:

出现问题的原因是整个集合视图在选取器委托方法结束时重新加载。发生这种情况时,UIPickerViews 本身会被重新加载,并且它们在选择器上的当前显示状态(而不是其他值)会变得混乱。

从第 2 行开始只更新现有的集合视图单元格而不重新加载选择器视图,基本上:

let indexPath = IndexPath(item: 4, section: 0)     // item 4 defines the index to column 1 row 2 
let cell = myCollectionView.cellForItem(at: indexPath) as! CollectionViewCell     // use the indexPath to get the existing cell at this position
cell.label.text = theNewText    // and just update this cell's label text.

【讨论】:

以上是关于UICollectionViewCells 中的 UIPickerViews 中的 3 个 UILabel 之间不需要的交互的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewCells 中的 CABasicAnimation 在屏幕外完成

UICollectionViewCells 中的 UIPickerViews 中的 3 个 UILabel 之间不需要的交互

你如何垂直对齐 UICollectionView 中的 UICollectionViewCells?

水平滚动时如何左对齐 UICollectionViewCells?

如何实现显示“有状态”内容的 UICollectionViewCells?

用于删除UICollectionViewCells的类似Springboard的动画