airbnb / epoxy-ios

Epoxy is a suite of declarative UI APIs for building UIKit applications in Swift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

When using EpoxyCollectionView, how to know that the Cell is reused?

rakuyoMo opened this issue · comments

I am using RxSwift and need to cancel the binding operation when the cell is reused.

But EpoxyableView is not a Cell, so it doesn't have a prepareForReuse method.

So how do I know that the view is reused?

There is no callback for when the cell is reused. We believe that views should not contain any owned state, and instead should be dumb renderers that render exactly what is set on them. I haven't used RxSwift before so I'm not sure the best way to integrate it with Epoxy.

There is no callback for when the cell is reused. We believe that views should not contain any owned state, and instead should be dumb renderers that render exactly what is set on them. I haven't used RxSwift before so I'm not sure the best way to integrate it with Epoxy.

What about adding a didReused(_:) callback like the didSelect(_:) method?

Not only RxSwift, but also responsive frameworks like Combine need a reused callback.

Perhaps you could discuss your use case—maybe there is a more idiomatic "Epoxy" way to accomplish what you're trying to do. Across thousands of screens we haven't yet needed the ability to know when a cell is about to be reused to Tyler's point.

Perhaps you could discuss your use case—maybe there is a more idiomatic "Epoxy" way to accomplish what you're trying to do. Across thousands of screens we haven't yet needed the ability to know when a cell is about to be reused to Tyler's point.

I am using Combine as an example. I have a list where there is a button on each cell.

I have written an extension for UIControl as follows:

extension XX where Base: UIControl {
    public func controlEvents(_ event: UIControl.Event) -> UIControlPublisher<Base> {
        return UIControlPublisher<Base>(control: base, event: event)
    }
}

A more realistic use case for it is shown below:

class ARow: UIView, EpoxyableView {
    private lazy var button = UIButton(type: .custom)
    
    private lazy var cancellable = Set<AnyCancellable>()
    
    // ...
    
    func setBehaviors(_ behaviors: Behaviors?) {
        guard let behaviors = behaviors else { return }
        
        button.xx
              .controlEvents(.touchUpInside)
              .sink(receiveValue: behaviors.didClickButton)
              .store(in: &cancellable)
    }
}

If I am in a Cell, then I can cancel this subscription in the prepareForReuse method to prevent problems with reuse. Something like the following:

override func prepareForReuse() {
    super.prepareForReuse()
    
    cancellable.forEarch { $0.cancel() }
}

setBehaviors is set whenever a cell is about to be displayed as well as when cells are reordered in the CollectionView so you should be able to do this without issue:

class ARow: UIView, EpoxyableView {
    private lazy var button = UIButton(type: .custom)
    
    private lazy var cancellable = Set<AnyCancellable>()
    
    // ...
    
    func setBehaviors(_ behaviors: Behaviors?) {
        cancellable.forEach { $0.cancel() }

        guard let behaviors = behaviors else { return }
        
        button.xx
              .controlEvents(.touchUpInside)
              .sink(receiveValue: behaviors.didClickButton)
              .store(in: &cancellable)
    }
}