如何调用正确子视图的闭包?

问题描述 投票:0回答:1

我在可比较数据源初始值设定项中的节提供程序中有一个闭包,我在闭包内部设置变量

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)
    }

}

swift
1个回答
0
投票

当声明闭包时,它会“捕获”它引用的变量的值。

就您而言,您的闭包看起来像这样

{ 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
属性,一个用于选定,一个用于取消选择,并一起消除闭包。 这只是重复的代码。

© www.soinside.com 2019 - 2024. All rights reserved.