iOS Swift3 动态分区行UICollectionView的TextField位置检测问题
解决UICollectionView动态布局下TextField对应IndexPath的获取问题
嘿,这个问题我之前做动态CollectionView的时候也踩过坑,用tag确实容易因为分区和行的动态变化出现重复或者错乱的情况,给你几个实用的解决思路,都是我实际用过的:
方法一:自定义TextField绑定IndexPath
这种方法最直接,给TextField加个属性专门存对应的IndexPath,回调时直接取用就行,完全不用担心tag冲突的问题。
首先创建自定义的TextField类:
class IndexPathTextField: UITextField { // 用来存储当前TextField对应的位置 var indexPath: IndexPath? }
然后在你的CollectionView数据源方法里,配置cell的时候给TextField赋值IndexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellIdentifier", for: indexPath) as! YourCustomCell // 假设你的cell里的TextField已经改成了IndexPathTextField类型 let textField = cell.yourTextField as! IndexPathTextField textField.indexPath = indexPath textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) // 别忘了设置TextField的初始值,从你的数据数组里取 textField.text = yourDataArray[indexPath.section][indexPath.row] return cell }
最后在回调方法里直接获取IndexPath:
@objc func textFieldDidChange(_ textField: UITextField) { guard let customTextField = textField as? IndexPathTextField, let indexPath = customTextField.indexPath else { return } let section = indexPath.section let row = indexPath.row // 把TextField的值存到你的数组里 yourDataArray[section][row] = textField.text ?? "" }
方法二:通过父视图层级找到Cell再获取IndexPath
如果不想自定义TextField,可以通过遍历TextField的父视图找到对应的CollectionViewCell,再用CollectionView的方法拿到IndexPath。
@objc func textFieldDidChange(_ textField: UITextField) { // 向上遍历父视图,找到对应的CollectionViewCell var currentView = textField.superview while currentView != nil && !(currentView is UICollectionViewCell) { currentView = currentView?.superview } guard let cell = currentView as? UICollectionViewCell, let indexPath = yourCollectionView.indexPath(for: cell) else { return } // 这里就可以拿到section和row了 let section = indexPath.section let row = indexPath.row yourDataArray[section][row] = textField.text ?? "" }
注意:如果你的TextField嵌套在多层视图里,这个遍历逻辑要确保能找到Cell,不过一般CollectionViewCell的层级不会太复杂,这个方法还是挺好用的。
方法三:用闭包代替Target-Action
这种方法把回调逻辑封装在Cell里,代码更模块化,也能直接拿到IndexPath。
首先自定义CollectionViewCell:
class YourCustomCell: UICollectionViewCell { @IBOutlet weak var yourTextField: UITextField! // 定义一个闭包,用来传递文本变化和对应的IndexPath var textChangedHandler: ((String?, IndexPath) -> Void)? var currentIndexPath: IndexPath? override func awakeFromNib() { super.awakeFromNib() // 给TextField添加编辑变化的监听 yourTextField.addTarget(self, action: #selector(onTextFieldChanged), for: .editingChanged) } @objc private func onTextFieldChanged() { guard let indexPath = currentIndexPath else { return } // 调用闭包传递数据 textChangedHandler?(yourTextField.text, indexPath) } }
然后在数据源方法里配置闭包:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellIdentifier", for: indexPath) as! YourCustomCell cell.currentIndexPath = indexPath cell.yourTextField.text = yourDataArray[indexPath.section][indexPath.row] // 设置闭包,这里直接更新数据数组 cell.textChangedHandler = { [weak self] text, indexPath in guard let self = self else { return } self.yourDataArray[indexPath.section][indexPath.row] = text ?? "" } return cell }
这三种方法里,我个人更推荐第一种和第三种,因为它们不受视图层级或者tag的限制,在动态添加/删除分区和行的时候也不会出问题,适配性更强。
内容的提问来源于stack exchange,提问作者Rupshikha anand
相关产品推荐
相关产品推荐

