我在可比较数据源初始值设定项中的节提供程序中有一个闭包,我在闭包内部设置变量
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 in
if checkIsSelected {
checkCellView?.checkImageView.image = UIImage(named: "check-box-selected")!
} else {
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
}
}
这个闭包中使用了两个变量;
checkIsSelected
,这是传递给闭包的参数,checkCellView
,这是您要修改的单元格。
现在
checkCellView
是视图控制器的一个属性。 如果我们重写您的闭包以显式添加您在引用 self.
时省略的 checkCellView
,您将看到问题的原因:
{ checkIsSelected in
if checkIsSelected {
self.checkCellView?.checkImageView.image = UIImage(named: "check-box-selected")!
} else {
self.checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
}
}
您认为因为您在
cellForRowAt
中分配了闭包,所以闭包将捕获 checkCellView
的当前值,但实际捕获的是 self
,并且在闭包执行时对 self.checkCellView
进行求值。 由于每次调用 diffable 数据源闭包时都会重新分配 self.checkCellView
,因此 self.checkCellView
不太可能实际引用所需的单元格。
一种解决方案是在可比较数据源闭包中使用局部变量,就像处理性别单元格一样:
类似:
case .indwellingCardiacDeviceOrProsthesis(let bool):
let cell = tableView.dequeueReusableCell(withIdentifier: CheckCellView.reuseID, for: indexPath) as! CheckCellView
cell.checkImageView.image = UIImage(named: "check-box-deselected")!
cell.switchCheckSelection = { checkIsSelected in
if checkIsSelected {
checkCellView?.checkImageView.image = UIImage(named: "check-box-selected")!
} else {
checkCellView?.checkImageView.image = UIImage(named: "check-box-deselected")!
}
}
return cell
但我可能会更改
CheckCellView
,使其仅具有两个 UImage
属性,一个用于选定,一个用于取消选择,并一起消除闭包。 这只是重复的代码。