我们有一个视图控制器MainCollectionView
,其中包含具有多个单元格FooCell
的集合视图。在每个FooCell
中,都有一个收集视图和一个单元格BarCell
的列表。
如何在BarCell
到MainCollectionView
中传播按钮点击事件?
这就是我们拥有的:
class FooCell: ... {
private let barCellButtonTappedSubject: PublishSubject<Void> = PublishSubject<Void>()
var barCellButtonTappedObservable: Observable<Void> {
return barCellButtonTappedSubject.asObserver()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeue(...)
if let cell = cell as BarCell {
cell.button.rx.tap.bind { [weak self] in
self?.barCellButtonTappedSubject.onNext(())
}.disposed(by: cell.rx.reusableDisposeBag)
}
return cell
}
}
class MainCollectionView: ... {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeue(...)
if let cell = cell as FooCell {
cell.barCellButtonTappedObservable.subscribe { [weak self] in
// Do other stuff when the button inside bar cell is tapped.
}.disposed(by: cell.rx.reusableDisposeBag)
}
return cell
}
}
在我读到有关ControlEvent
之前,这一直有效:
在ControlEvent
中使用FooCell
似乎更合适:
private let barCellButtonTappedSubject: PublishSubject<Void> = PublishSubject<Void>()
var barCellButtonTappedObservable: Observable<Void> {
return barCellButtonTappedSubject.asObserver()
}
将此barCellButtonTappedObservable
转换为ControlEvent
的正确方法是什么?还是有其他更好的主意将嵌套单元格中的ControlEvent
传播到外部视图控制器?
答案 0 :(得分:1)
我个人更喜欢将RxAction
用于此类操作,但是由于您已经在单元格中声明了PublishSubject<Void>
,因此可以将主题转换为ControlEvent
>
controlEvent = ControlEvent<Void>(events: barCellButtonTappedSubject.asObservable())
尽可能直截了当!但是,如果您只想这样做,那么您甚至不需要barCellButtonTappedSubject
controlEvent = ControlEvent<Void>(events: cell.button.rx.tap)
实际上,您甚至不需要声明控制事件:),因为cell.button.rx.tap
本身是一个控制事件:)因此,如果您在单元格中将按钮声明为公共属性,则可以直接访问它在tableView控制器中点击控制事件
但就我个人而言,我会使用RxAction
而不是声明publishSubject
的{{1}}或controlEvent
可以要求您的FooCell
采取行动
TableViewController
最后,您的TableViewController / CollectionViewController可以通过
传递操作class FooCell: ... {
var cellTapAction : CocoaAction! = nil
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeue(...)
if let cell = cell as BarCell {
cell.button.rx.action = cellTapAction
}
return cell
}
}
您只需要处理是否将cellctionView嵌入class MainCollectionView: ... {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeue(...)
if var cell = cell as FooCell {
cell.cellTapAction = CocoaAction { _ -> Observable<Void> in
debugPrint("button in cell tapped")
return Observable.just(())
}
}
return cell
}
}
内,因为在FooCell
嵌入collectionView传递之后传递action
甚至可能在传递动作之前加载,因此您将调整逻辑,以便在将操作传递给deQueReusableCell
之后重新加载嵌入式集合视图,或解决此问题的其他解决方法:)
希望它会有所帮助:)我相信使用FooCell
可使代码更简洁,更易于理解。
答案 1 :(得分:1)
我从未使用过其他答案中提到的Action
。我也想知道为什么您似乎是在手动设置您的委托而不是使用RxCocoa来完成。最后,感觉您可能想要某种方式来了解哪个按钮被点击。在下面的代码中,我为每个Bar单元分配了一个ID整数。
class BarCell: UICollectionViewCell {
@IBOutlet weak var button: UIButton!
func configure(with viewModel: BarViewModel) {
button.rx.tap
.map { viewModel.id }
.bind(to: viewModel.buttonTap)
.disposed(by: bag)
}
override func prepareForReuse() {
super.prepareForReuse()
bag = DisposeBag()
}
private var bag = DisposeBag()
}
class FooCell: UICollectionViewCell {
@IBOutlet weak var collectionView: UICollectionView!
func configure(with viewModel: FooViewModel) {
viewModel.bars
.bind(to: collectionView.rx.items(cellIdentifier: "Bar", cellType: BarCell.self)) { index, element, cell in
cell.configure(with: element)
}
.disposed(by: bag)
}
override func prepareForReuse() {
super.prepareForReuse()
bag = DisposeBag()
}
private var bag = DisposeBag()
}
class MainCollectionView: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
var viewModel: MainViewModel!
override func viewDidLoad() {
super.viewDidLoad()
let foos = viewModel.foos
.share()
let buttonTaps = foos
.flatMap { Observable.merge($0.map { $0.bars }) }
.flatMap { Observable.merge($0.map { $0.buttonTap.asObservable() }) }
buttonTaps
.subscribe(onNext: {
print("button \($0) was tapped.")
})
.disposed(by: bag)
foos
.bind(to: collectionView.rx.items(cellIdentifier: "Foo", cellType: FooCell.self)) { index, element, cell in
cell.configure(with: element)
}
.disposed(by: bag)
}
private let bag = DisposeBag()
}
struct FooViewModel {
let bars: Observable<[BarViewModel]>
}
struct BarViewModel {
let id: Int
let buttonTap = PublishSubject<Int>()
}
struct MainViewModel {
let foos: Observable<[FooViewModel]>
}
关于代码最有趣的一点是所有buttonTaps的合并。找出来有点冒险。 :-)
答案 2 :(得分:0)
在尝试观察按钮的状态之前,我会亲自向按钮添加一个操作,然后从那里处理您的响应。
class Cell: UICollectionViewCell {
let button = UIButton(type: .custom)
func setUpButton() {
button.addTarget(self, action: #selector(Cell.buttonTapped), for: .touchUpInside)
}
@IBAction func buttonTapped(sender: UIButton) {
//This runs every time the button is tapped
//Do something here to notify the parent that your button was selected or handle it in this Cell.
print(sender.state)
}
}