Swift开发中如何在自定义函数内获取indexPath解决作用域报错
问题说明
实现UICollectionView点击选中逻辑时,需要在第一个UIAlertController选择操作选项后弹出第二个UIAlertController,读取第二个弹窗中textField的输入内容设置对应位置数据的属性,但自定义alert2函数中无法获取到indexPath,在代码行let person = people[indexPath.item]处收到报错:Cannot find 'indexPath' in scope。
原错误代码如下:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let ac = UIAlertController(title: "Choose an action", message: nil, preferredStyle: .alert) ac.addAction(UIAlertAction(title: "Rename a person", style: .default, handler: alert2)) ac.addAction(UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in self?.people.remove(at: indexPath.item) self?.collectionView.reloadData() }) ac.addAction(UIAlertAction(title: "Cancel", style: .cancel)) present(ac, animated: true) } func alert2(alert: UIAlertAction!) { let ac2 = UIAlertController(title: "Type in a name", message: nil, preferredStyle: .alert) ac2.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) self.present(ac2, animated: true) guard let newName = ac2.textFields?[0].text else { return } let person = people[indexPath.item] person.name = newName self.collectionView.reloadData() ac2.addTextField() }
错误原因
indexPath是collectionView(_:didSelectItemAt:)方法的局部参数,作用域仅限该方法内部,全局定义的alert2函数不在这个作用域范围内,无法直接访问该变量。- 原有
alert2逻辑本身还有两处顺序错误:一是先弹出ac2再添加textField,会导致输入框不显示;二是把读取输入、更新数据的逻辑写在了弹窗弹出的主线程流程里,没有放到「Ok」按钮的点击回调中,会导致弹窗刚弹出就立刻读取输入,此时用户还未输入内容,永远拿不到正确值。
修正方案
不需要单独抽离alert2函数,直接用闭包捕获indexPath变量,同时调整第二个弹窗的逻辑顺序即可:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let ac = UIAlertController(title: "Choose an action", message: nil, preferredStyle: .alert) // 重命名逻辑直接写在闭包中,自动捕获作用域内的indexPath ac.addAction(UIAlertAction(title: "Rename a person", style: .default) { [weak self] _ in guard let self = self else { return } let ac2 = UIAlertController(title: "Type in a name", message: nil, preferredStyle: .alert) // 先添加输入框 ac2.addTextField() // 把读取输入、更新数据的逻辑放到Ok按钮的回调中 ac2.addAction(UIAlertAction(title: "Ok", style: .default) { _ in guard let newName = ac2.textFields?[0].text, !newName.isEmpty else { return } let person = self.people[indexPath.item] person.name = newName self.collectionView.reloadData() }) ac2.addAction(UIAlertAction(title: "Cancel", style: .cancel)) // 所有配置完成后再弹出弹窗 self.present(ac2, animated: true) }) ac.addAction(UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in self?.people.remove(at: indexPath.item) self?.collectionView.reloadData() }) ac.addAction(UIAlertAction(title: "Cancel", style: .cancel)) present(ac, animated: true) }
如果确实需要抽离单独的方法处理重命名逻辑,只需要给方法增加indexPath: IndexPath入参,在闭包中调用时把捕获到的indexPath传入即可。
内容的提问来源于stack exchange,提问作者Nikita Chechnev
相关产品推荐
相关产品推荐

