Search code examples
rx-swiftrx-cocoa

UICollectionViewDelegate in RxCocoa


I write an extension for UICollectionView which will listen the delegate's shouldHighlightItemAt method,but it don't call.

public var shouldHighlightItem: ControlEvent<IndexPath> {

    let source = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:shouldHighlightItemAt:)))
        .map { a in
            return try self.castOrThrow(IndexPath.self, a[1])
    }

    return ControlEvent(events: source)
}

}

how to write an extension for UICollectionView of rx shouldHighlightItemAt?


Solution

  • You cannot use methodInvoked(_:) with a delegate method that has a non void return type.

    collectionView(_:shouldHighlightItemAt:) expects you to return a Bool value. So you cannot use methodInvoked(_:).

    If you have a look at the implementation of methodInvoked(_:) it gives you an explanation why this does not work:

    Delegate methods that have non void return value can't be observed directly using this method because:

    • those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism

    • there is no sensible automatic way to determine a default return value

    There is however a suggestion how you could achieve what you are trying to do:

    In case observing of delegate methods that have return type is required, it can be done by manually installing a PublishSubject or BehaviorSubject and implementing delegate method.

    In your case it would work like this:

    In RxCollectionViewDelegateProxy you add the 'PublishSubject' and implement the UICollectionViewDelegate method:

    let shouldHighlightItemAtIndexPathSubject = PublishSubject<IndexPath>
    
    public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
        shouldHighlightItemAtIndexPathSubject.on(.next(indexPath))
        return self._forwardToDelegate?.collectionView(collectionView, shouldHighlightItemAt: indexPath) ?? true // default value
    }
    

    In your UICollectionView RxExtension you can expose the desired Observable like this:

    public var property: Observable<IndexPath> {
        let proxy = RxCollectionViewDelegateProxy.proxy(for: base)
        return proxy.shouldHighlightItemAtIndexPathSubject.asObservable()
    }
    

    I have not tested this, I merely took it from the RxCocoa source code and modified it to fit your needs. So in theory this should work, but you might have to tweak it a little bit ;-)