使用 RxSwift 从 UITableViewCell 发送回调到 UIViewController
Sending callback using RxSwift from UITableViewCell to UIViewController
我有一个 UITableViewCell,其中我有 customContentView,点击它我想向 viewController 发送回调。为此,我正在使用 RxSwift。
class GISThemeFormTableCell: UITableViewCell {
@IBOutlet weak var customContentView: UIView!
var index = -1
var cellSelected: Observable<(Int, Bool)>?
private var observer: AnyObserver<(Int, Bool)>?
override func awakeFromNib() {
super.awakeFromNib()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.didSelectCell))
customContentView.addGestureRecognizer(tapGesture)
cellSelected = Observable<(Int, Bool)>.create { (observer) -> Disposable in
self.observer = observer
return Disposables.create()
}
}
@objc private func didSelectCell() {
let newImage = selectionImageView.image! == Images.uncheckedRound ? Images.checkedRound : Images.uncheckedRound
selectionImageView.image = newImage
observer?.onNext((index, selectionImageView.image! == Images.checkedRound))
}
}
class GISFormListView: UIView {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "GISThemeFormTableCell", for: indexPath) as! GISThemeFormTableCell
cell.index = indexPath.row
cell.formTitle.text = presenter.getFormName(indexPath.row)
cell.cellSelected?.subscribe { (event) in
let index = event.element!.0
let isSelected = event.element!.1
print(index, isSelected)
}.disposed(by: disposeBag)
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingTableViewCell", for: indexPath) as! LoadingTableViewCell
return cell
default:
return UITableViewCell()
}
}
}
在上面的代码中,我在 awakeFromNib 方法中创建了 Observable,并在那里初始化了观察者。调用 didSelectCell 方法后,我将在观察者中传递单元格和布尔值的索引。
如果我不想使用闭包和委托,我只是想知道这是否是实现此目标的正确方法。
在您的单元格中创建 disposeBag
。然后,在 prepareForReuse
中重置它。
cell.cellSelected?.subscribe { (event) in
let index = event.element!.0
let isSelected = event.element!.1
}.disposed(by: cell.disposeBag)
在您的情况下,经过一些滚动后,每个单元格将被订阅多次。这应该会产生一些魔力 :)
一般情况下,您只会订阅可见的单元格,不会出现内存泄漏或过度计算的情况。我觉得还可以。
如果您有兴趣,这里有一个更完整的答案。我看到@Erumaru 警告过您有关单元格被多次打印的信息。如果您有足够的表单项目,某些单元格会被重复使用,并且您没有正确处理,那么请记住这一点。
class GISThemeFormTableCell: UITableViewCell {
@IBOutlet weak var formTitle: UILabel!
@IBOutlet weak var selectionImageView: UIImageView!
@IBOutlet weak var customContentView: UIView!
var disposeBag = DisposeBag()
private var tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: nil, action: nil)
override func awakeFromNib() {
super.awakeFromNib()
customContentView.addGestureRecognizer(tapGesture)
}
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
func configure(title: String, initial: Bool = false) -> Observable<Bool> {
// you shouldn't be inspecting your views to figure out the state of your model. The code below avoids that by making the state its own thing.
let state = tapGesture.rx.event
.filter { [=10=].state == .ended }
.scan(false) { current, _ in !current }
.startWith(initial)
state
.map { [=10=] ? Images.checkedRound : Images.uncheckedRound }
.bind(to: selectionImageView.rx.image)
.disposed(by: disposeBag)
return state
}
}
class GISFormListView: UIView {
var presenter: Presenter!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "GISThemeFormTableCell", for: indexPath) as! GISThemeFormTableCell
// the cell doesn't need to know its row, because that information is needed here, and it's already here.
cell.configure(title: presenter.getFormName(indexPath.row))
.map { (indexPath.row, [=10=]) }
.subscribe(onNext: { index, isSelected in
print(index, isSelected)
})
.disposed(by: cell.disposeBag)
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingTableViewCell", for: indexPath) as! LoadingTableViewCell
return cell
default:
return UITableViewCell()
}
}
}
我有一个 UITableViewCell,其中我有 customContentView,点击它我想向 viewController 发送回调。为此,我正在使用 RxSwift。
class GISThemeFormTableCell: UITableViewCell {
@IBOutlet weak var customContentView: UIView!
var index = -1
var cellSelected: Observable<(Int, Bool)>?
private var observer: AnyObserver<(Int, Bool)>?
override func awakeFromNib() {
super.awakeFromNib()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.didSelectCell))
customContentView.addGestureRecognizer(tapGesture)
cellSelected = Observable<(Int, Bool)>.create { (observer) -> Disposable in
self.observer = observer
return Disposables.create()
}
}
@objc private func didSelectCell() {
let newImage = selectionImageView.image! == Images.uncheckedRound ? Images.checkedRound : Images.uncheckedRound
selectionImageView.image = newImage
observer?.onNext((index, selectionImageView.image! == Images.checkedRound))
}
}
class GISFormListView: UIView {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "GISThemeFormTableCell", for: indexPath) as! GISThemeFormTableCell
cell.index = indexPath.row
cell.formTitle.text = presenter.getFormName(indexPath.row)
cell.cellSelected?.subscribe { (event) in
let index = event.element!.0
let isSelected = event.element!.1
print(index, isSelected)
}.disposed(by: disposeBag)
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingTableViewCell", for: indexPath) as! LoadingTableViewCell
return cell
default:
return UITableViewCell()
}
}
}
在上面的代码中,我在 awakeFromNib 方法中创建了 Observable,并在那里初始化了观察者。调用 didSelectCell 方法后,我将在观察者中传递单元格和布尔值的索引。
如果我不想使用闭包和委托,我只是想知道这是否是实现此目标的正确方法。
在您的单元格中创建 disposeBag
。然后,在 prepareForReuse
中重置它。
cell.cellSelected?.subscribe { (event) in
let index = event.element!.0
let isSelected = event.element!.1
}.disposed(by: cell.disposeBag)
在您的情况下,经过一些滚动后,每个单元格将被订阅多次。这应该会产生一些魔力 :)
一般情况下,您只会订阅可见的单元格,不会出现内存泄漏或过度计算的情况。我觉得还可以。
如果您有兴趣,这里有一个更完整的答案。我看到@Erumaru 警告过您有关单元格被多次打印的信息。如果您有足够的表单项目,某些单元格会被重复使用,并且您没有正确处理,那么请记住这一点。
class GISThemeFormTableCell: UITableViewCell {
@IBOutlet weak var formTitle: UILabel!
@IBOutlet weak var selectionImageView: UIImageView!
@IBOutlet weak var customContentView: UIView!
var disposeBag = DisposeBag()
private var tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: nil, action: nil)
override func awakeFromNib() {
super.awakeFromNib()
customContentView.addGestureRecognizer(tapGesture)
}
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
func configure(title: String, initial: Bool = false) -> Observable<Bool> {
// you shouldn't be inspecting your views to figure out the state of your model. The code below avoids that by making the state its own thing.
let state = tapGesture.rx.event
.filter { [=10=].state == .ended }
.scan(false) { current, _ in !current }
.startWith(initial)
state
.map { [=10=] ? Images.checkedRound : Images.uncheckedRound }
.bind(to: selectionImageView.rx.image)
.disposed(by: disposeBag)
return state
}
}
class GISFormListView: UIView {
var presenter: Presenter!
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "GISThemeFormTableCell", for: indexPath) as! GISThemeFormTableCell
// the cell doesn't need to know its row, because that information is needed here, and it's already here.
cell.configure(title: presenter.getFormName(indexPath.row))
.map { (indexPath.row, [=10=]) }
.subscribe(onNext: { index, isSelected in
print(index, isSelected)
})
.disposed(by: cell.disposeBag)
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "LoadingTableViewCell", for: indexPath) as! LoadingTableViewCell
return cell
default:
return UITableViewCell()
}
}
}