我在可区分数据源初始化程序中的部分提供程序中有一个闭包,我checkCellView
从闭包内部设置变量。当我从错误的单元格switchCheckSelectionFunc()
中调用闭包时CheckCellView
,最终选择了错误的单元格。我该如何修复这个问题?
class QuestionnaireViewController: UIViewController {
var checkCellView: CheckCellView? {
didSet {
if checkCellView != nil {
print("type of closure,", type(of: checkCellView!.switchCheckSelection))
} else {
}
}
}
func configureDataSource() {
dataSource = CustomTableViewDiffableDataSource(tableView: tableView, cellProvider: { [self] tableView, indexPath, formItem in
// print("section,", indexPath.section)
self.checkCellView?.switchCheckSelection = { checkIsSelected in
if checkIsSelected {
checkCellView?.checkImageView.image = UIImage(named: "check-box-selected")!
} else {
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
}
}
switch formItem {
case .name(let name):
let cell = tableView.dequeueReusableCell(withIdentifier: PatientInfoCell.reuseID, for: indexPath) as! PatientInfoCell
cell.configure(title: "First Name")
return cell
case .lastName(let lastName):
let cell = tableView.dequeueReusableCell(withIdentifier: PatientInfoCell.reuseID) as! PatientInfoCell
cell.configure(title: "Last Name")
return cell
case .age(let age):
let cell = tableView.dequeueReusableCell(withIdentifier: PatientInfoCell.reuseID) as! PatientInfoCell
cell.configure(title: "Age")
return cell
case .gender(let gender):
let cell = tableView.dequeueReusableCell(withIdentifier: GenderCell.reuseID) as! GenderCell
cell.titleView.text = "Gender"
cell.femaleIcon.image = UIImage(named: "female-icon-deselected")!
cell.switchFemaleIconTapped = { isSelected in
if isSelected {
cell.femaleIcon.image = UIImage(named: "female-icon-selected")!
} else {
cell.femaleIcon.image = UIImage(named: "female-icon-deselected")!
}
}
cell.maleIcon.image = UIImage(named: "male-icon-deselected")!
cell.switchMaleIconTapped = {
if $0 {
cell.maleIcon.image = UIImage(named: "male-icon-selected")!
} else {
cell.maleIcon.image = UIImage(named: "male-icon-deselected")!
}
}
return cell
case .indwellingCardiacDeviceOrProsthesis(let bool):
checkCellView = tableView.dequeueReusableCell(withIdentifier: CheckCellView.reuseID, for: indexPath) as? CheckCellView
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
checkCellView?.switchCheckSelection = { checkIsSelected in
if checkIsSelected {
checkCellView?.checkImageView.image = UIImage(named: "check-box-selected")!
} else {
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
}
}
return checkCellView
case .predisposingHeartCondition(let bool):
checkCellView = tableView.dequeueReusableCell(withIdentifier: CheckCellView.reuseID, for: indexPath) as? CheckCellView
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
return checkCellView
case .intravenousDrugUse(let bool):
checkCellView = tableView.dequeueReusableCell(withIdentifier: CheckCellView.reuseID, for: indexPath) as? CheckCellView
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
return checkCellView
case .cardiacStructuralDisorder(let bool):
checkCellView = tableView.dequeueReusableCell(withIdentifier: CheckCellView.reuseID, for: indexPath) as? CheckCellView
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
return checkCellView
case .diabetic(let bool):
checkCellView = tableView.dequeueReusableCell(withIdentifier: CheckCellView.reuseID, for: indexPath) as? CheckCellView
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
return checkCellView
}
})
}
}
class CheckCellView: UITableViewCell {
var titleView: UILabel = UILabel()
var checkImageView: UIImageView = UIImageView()
static var reuseID = "check-cell-reuseID"
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUp()
}
var switchCheckSelection: ((_ isSelected: Bool) -> Void)?
var checkIsSelected: Bool = false
func setUp() {
checkImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(switchCheckSelectionFunc)))
checkImageView.isUserInteractionEnabled = true
let stackView = UIStackView(arrangedSubviews: [
titleView,
checkImageView
])
stackView.axis = .horizontal
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.topAnchor.constraint(equalTo: topAnchor)
])
}
@objc func switchCheckSelectionFunc() {
checkIsSelected.toggle()
switchCheckSelection?(checkIsSelected)
}
func applySnapshot() {
guard case let .questionnaireData(formData) = data,
let formData = formData else {
return
}
var snapshot = NSDiffableDataSourceSnapshot<Section, FormItem>()
snapshot.appendSections(Section.allCases)
snapshot.appendItems(formData.patientInfoData, toSection: .patientInformation)
snapshot.appendItems(formData.checkAllThatApplyData, toSection: .checkAllThatApply)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
当声明闭包时,它会“捕获”它引用的变量的值。
就你的情况而言,你的闭包看起来像
这个闭包中使用了两个变量;
checkIsSelected
,这是传递给闭包的参数,以及checkCellView
,这是您想要修改的单元格。现在
checkCellView
是视图控制器的一个属性。如果我们重写闭包以明确添加self.
您在引用时省略的内容,checkCellView
您将看到问题的原因:您认为,由于您在 中分配了闭包
cellForRowAt
,因此闭包将捕获 的当前值checkCellView
,但实际捕获的是self
,并self.checkCellView
在闭包执行时进行评估。由于self.checkCellView
每次调用可区分数据源闭包时都会重新分配,因此 不太可能self.checkCellView
真正引用所需的单元格。一种解决方案是在可差异数据源闭包中使用局部变量,就像您对性别单元所做的那样:
类似于:
但我可能会进行修改
CheckCellView
,让它只具有两个UImage
属性,一个用于选中,一个用于取消选中,然后完全消除闭包。这只是重复的代码。