如何区分原型 UITableViewCells 中同一视图的实例

Posted

技术标签:

【中文标题】如何区分原型 UITableViewCells 中同一视图的实例【英文标题】:How to differentiate between instances of the same view in prototyped UITableViewCells 【发布时间】:2016-06-22 04:31:53 【问题描述】:

我正在使用包含UIPickerViewUITableViewCell 原型,并将该原型用于tableView 中具有4 个不同PickerView 的4 个不同单元。我使用以下代码将单元格提供给 tableView (tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) 并将每个选择器设置为不同的实例变量,以便稍后区分选择器(因为相同的 UITableViewController 实例是所有它们的委托/数据源,例如)。

但是,在运行代码时,所有 4 个实例变量最终都指向同一个 UIPickerView。如何确保它使用 4 个不同的 UIPickerViews 来代替?

func PickerCell(tableView: UITableView, indexPath: NSIndexPath, inout picker: UIPickerView?) -> UITableViewCell 
    let cell = tableView.dequeueReusableCellWithIdentifier("PickerCell")
    if let pickerFromCell = cell?.contentView.viewWithTag(1) as? UIPickerView 
        pickerFromCell.reloadAllComponents()
        pickerFromCell.dataSource = self
        pickerFromCell.delegate = self
        picker = pickerFromCell
    
    return cell!

【问题讨论】:

您必须设置不同的标签以进行识别 不,tag 是一种非常糟糕的跟踪细胞的方法,Apple 现在不鼓励这种做法。您应该继承 UITableViewCell 并在单元格中拥有一个属性,而不是视图控制器。 问题似乎在于原型单元在使用该重用标识符出队的每个单元中重用了相同的 UIPickerView。这似乎与 Apple 的文档相矛盾,所以我不得不假设这是一个错误。我通过在情节提要中创建 4 个原型单元格来解决这个问题,每个单元格都有不同的重用标识符,然后从不同的重用标识符中取出每个单元格,并以不同的方式标记每个选择器。 (出于同样的原因,标记也不起作用。) Matt,您不必创建四个不同的单元原型。 【参考方案1】:

不要使用tag,而是尝试这样的事情。像这样更改您的didSelectRowPickerViewDelegate

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
    let cell = imageView.superview?.superview as! UITableViewCell //You have to use as may super as your UITableViewCell hierarchy 
    let indexPath = self.tabelView.indexPathForCell(cell)
    self.pickerSelectedIndexArr[indexpath.row] = row

还可以在文件中添加pickerSelectedIndexArr 数组,并按照以下方式在viewDidLoad 中分配它

var pickerSelectedIndexArr: [Int] = [Int]()

override func viewDidLoad() 
    super.viewDidLoad()
    self.pickerSelectedIndexArr = [0, 0, 0 ,0]

现在您可以随时轻松获取所有选择器选择的值 希望这会对你有所帮助。

【讨论】:

【参考方案2】:
var dict: [Int, UIPickerView]

...

if dict[indexPath.row] == nil 
    // Create UIPickerView and assign it to dict[indexPath.row].

【讨论】:

【参考方案3】:

由于这个问题被标记为Objective C,所以我在 OBJC 中给出了你的答案。

这是你可以做的,处理可重用单元格中的控件,

解决此类问题的提示,when you have controls in UICollectionViewCell or UITableViewCell, give tag to your controls depending upon your indexPath

collectionViewCell 举例,您可以使用tableViewCell 案例 1:如果 CollectionView 是分段的,那么您的标签将类似于 [0][0]、[1][0] ...在这种情况下,请执行以下操作,

collectionViewCell.picker.tag = [[NSString stringWithFormat:@"%ld%ld",(long)indexPath.section,(long)indexPath.item] intValue]; // If it is sectional collection view

案例 2:如果 Collection View 是非分段的,请执行以下操作,

collectionViewCell.picker.tag = [[NSString stringWithFormat:@"%ld",(long)indexPath.item] intValue]; // If it is non-sectional collection view

希望这将解决您的问题或为您提供相应管理的想法。 如果您的单元格中有多个控件,则只需提供 indexPath.item + 1 + 2 ... 之类的标签 ...

【讨论】:

【参考方案4】:

我同意 Nirav 的观点,您应该使用单元格来确定相关单元格的 NSIndexPath

就个人而言,我会将单元格子类设置为数据源和选取器的委托,并让它负责选取器。这完全消除了关于哪个选择器与哪个单元格相关联的任何混淆。但是我有一个协议,当用户从选择器中选择一个值并且视图控制器可以更新模型时,单元格可以通过该协议通知视图控制器。

例如,考虑这个UITableViewCell 子类:

//  CustomCell.swift

import UIKit

/// Protocol that the view controller will conform to in order to receive updates
/// from the cell regarding which row in the picker was picked.
///
/// - note: This is a `class` protocol so that I can use `weak` reference.

protocol CustomCellDelegate: class 
    func cell(customCell: CustomCell, pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)


/// Custom cell subclass, which is the picker's data source and delegate

class CustomCell: UITableViewCell, UIPickerViewDataSource, UIPickerViewDelegate 

    /// The delegate who we will inform of any picker changes.
    ///
    /// This is weak to avoid strong reference cycle.

    weak var delegate: CustomCellDelegate?

    /// The array of values to be shown in the picker.
    ///
    /// If the `values` changes, this reloads the picker view.

    var values: [String]? 
        didSet 
            pickerView.reloadComponent(0)
        
    

    /// The outlet to the picker in the cell.

    @IBOutlet weak var pickerView: UIPickerView!

    // MARK: UIPickerViewDataSource

    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int 
        return 1
    

    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int 
        return values?.count ?? 0
    

    // MARK: UIPickerViewDelegate

    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? 
        return values?[row] ?? ""
    

    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
        delegate?.cell(self, pickerView: pickerView, didSelectRow: row, inComponent: component)
    

然后是视图控制器:

//  ViewController.swift

import UIKit

class ViewController: UITableViewController, CustomCellDelegate 

    // an array of arrays of acceptable values

    let troupes = [
        ["Mo", "Larry", "Curly"],
        ["Abbott", "Costello"],
        ["Groucho", "Harpo", "Zeppo", "Chico", "Gummo"],
        ["Laurel", "Hardy"],
        ["Graham Chapman", "John Cleese", "Terry Gilliam", "Eric Idle", "Terry Jones", "Michael Palin"]
    ]

    /// An array that indicates which is item is selected for each table view cell

    var selectedItemForRow: [Int]!

    override func viewDidLoad() 
        super.viewDidLoad()

        selectedItemForRow = troupes.map  _ in return 0     // initialize the `selectedItemForRow` array with zeros

        // Whatever further initialization of the view controller goes here.
    

    // MARK: UITableViewDataSource

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return troupes.count
    

    // Populate cell, setting delegate, list of acceptable values, and currently selected value.

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
        cell.delegate = self
        cell.values = troupes[indexPath.row]
        cell.pickerView.selectRow(selectedItemForRow[indexPath.row], inComponent: 0, animated: false)
        return cell
    

    // MARK: CustomCellDelegate

    // When the cell tells us that the user changed the selected row, let's update our model accordingly.

    func cell(customCell: CustomCell, pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) 
        if let indexPath = tableView.indexPathForCell(customCell) 
            selectedItemForRow[indexPath.row] = row
        
    


【讨论】:

以上是关于如何区分原型 UITableViewCells 中同一视图的实例的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 Storyboard 中的静态原型 UITableViewCells 显示为空白?

如何使用 begin-endUpdates() 和多个自定义单元格展开/折叠 UITableViewCells?

一个NIB中的几个自定义UITableViewCells - 如何在代码中引用?

如何以普通样式隐藏在 UITableView 中显示的空 UITableViewCells - IOS?

如何以编程方式添加到 NSMutableArray 并将其显示在 uitableviewcells 上?

如何在 UITableView 中添加背景视频并在滚动 UITableViewCells 时使其固定?