我有一个包含UICollectionViewCell
的UIButton
。
[在单元格中点击按钮时,我想切换selected
状态。
这在一定程度上有效,我看到的错误是,单击1个单元格也会切换另一个单元格的状态。
import UIKit
import RxSwift
class PersonaliseYourAppsListCell: BaseCollectionViewCell<Launcher> {
let toggleSelectedTrigger = PublishSubject<String>()
private let disposeBag = DisposeBag()
override init(frame: CGRect) {
super.init(frame: frame)
configureSubviews()
}
required init?(coder: NSCoder) {
return nil
}
override func render(with model: Launcher?) {
guard let model = model else { return }
title.text = model.name
icon.image = model.icon
toggleButton.rx.tap.bind { [weak self] in
self?.toggleButton.isSelected.toggle()
self?.toggleSelectedTrigger.onNext(model.id)
}.disposed(by: disposeBag)
if let status = model.status {
toggleButton.isSelected = model.selected ?? false
toggleButton.isUserInteractionEnabled = status != .forced
[title, icon, toggleButton].forEach {
$0.alpha = status == .forced ? 0.6 : 1
}
if status == .forced {
addSubviews(enforcedLabel)
enforcedLabel.position(top: title.bottomAnchor, leading: title.leadingAnchor, withPadding: .zero)
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
enforcedLabel.removeFromSuperview()
}
// MARK:- UI Elements
private func configureSubviews() {
addSubviews(icon, title, toggleButton, separatorView)
icon
.isCenteredY()
.position(
leading: leadingAnchor,
withPadding: .init(top: 0, left: 16, bottom: 0, right: 4)
)
title
.isCenteredY()
.position(
leading: icon.trailingAnchor, trailing: toggleButton.leadingAnchor,
withPadding: .init(top: 0, left: 8, bottom: 0, right: 8)
)
toggleButton
.isCenteredY()
.withSize(.init(width: 44, height: 44))
.position(trailing: trailingAnchor, withPadding: .init(top: 0, left: 0, bottom: 0, right: 16))
separatorView
.isCenteredX()
.withSize(.init(width: frame.width - 48, height: 1))
.position(bottom: bottomAnchor)
}
private lazy var icon: UIImageView = {
let iv = UIImageView(frame: .zero)
iv.contentMode = .scaleAspectFit
[iv.widthAnchor, iv.heightAnchor].forEach { $0.constraint(equalToConstant: 64).isActive = true }
return iv
}()
private lazy var title: UILabel = {
let label = UILabel(frame: .zero)
label.font = .systemFont(ofSize: 20)
label.textAlignment = .left
label.numberOfLines = 2
label.textColor = .usingHex("444444")
return label
}()
private lazy var toggleButton: CheckBoxButton = {
let button = CheckBoxButton(type: .custom)
return button
}()
private lazy var enforcedLabel: UILabel = {
let label = UILabel(frame: .zero)
label.text = "This app cannot be removed"
label.font = .systemFont(ofSize: 12)
label.textColor = .lightGray
return label
}()
private lazy var separatorView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.3, alpha: 0.1)
return view
}()
}
我的CollectionView的设置如下:
override func viewDidLoad() {
super.viewDidLoad()
presenter?.viewIsReady.onNext(())
presenter?.data.bind(to: customView.collectionView.rx.items) { [weak self] (cv, row, item) -> UICollectionViewCell in
let cell = cv.dequeueReusableCell(withClass: PersonaliseYourAppsListCell.self, for: .init(row: row, section: 0))
cell.render(with: item)
if let self = self, let presenter = self.presenter {
cell.toggleSelectedTrigger.bind(to: presenter.updateUserAppsTrigger).disposed(by: self.disposeBag)
}
return cell
}.disposed(by: disposeBag)
customView.configureLayout()
}
编辑
我注意到,如果我不进行api调用,而只是让按钮状态切换,则该bug不存在。我想知道响应是否正在重新加载集合视图,并且由于重新使用而导致错误的按钮状态正在更新。
编辑2
如果我通过选择单元格而不是按钮来触发API调用,那么一切正常,但是我希望在单击按钮时发生这种情况。
我仍然怀疑这与按钮状态有关。
正如Subramanian Mariappan所建议的,可能是因为细胞被重用。在render
方法中,您订阅了按钮点击操作,而您实际上从未停止观察。替换:
private let disposeBag = DisposeBag()
with:
private var disposeBag = DisposeBag()
并且在func prepareForReuse()
中添加新的disposeBag的创建(顺便释放旧的DisposeBag)。
override func prepareForReuse() {
super.prepareForReuse()
enforcedLabel.removeFromSuperview()
disposeBag = DisposeBag()
}
正如Subramanian Mariappan所建议的,可能是因为细胞被重用。在render
方法中,您订阅了按钮点击操作,而您实际上从未停止观察。替换:
private let disposeBag = DisposeBag()
with:
private var disposeBag = DisposeBag()
并在func prepareForReuse()
中添加新的disposeBag的创建(顺便释放旧的DisposeBag)。
override func prepareForReuse() {
super.prepareForReuse()
enforcedLabel.removeFromSuperview()
disposeBag = DisposeBag()
}