I'm trying to figure out if I'm creating a retain cycle here. I want to bind the current offset of the collection view to a UIPageControl with the following:
collectionView
.rx
.contentOffset
.asDriver()
.map { Int($0.x) / Int(self.collectionView.frame.width) }
.drive(pageControl.rx.currentPage)
.disposed(by: disposeBag)
Just wondering if this is ok, or if that self will create a retain cycle?
Yes, your code creates a retain cycle because your chain keeps a strong reference to self and self keeps a strong reference to the chain (through the disposeBag.)
Also, such code is a code smell because you are depending on side effects in a map (the value of self.collectionView.frame.width changes over time) which you shouldn't do because it makes your code untestable.
Here is a more architecturally correct solution:
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var pageControl: UIPageControl!
private let collectionViewFrame = ReplaySubject<CGRect>.create(bufferSize: 1)
private let disposeBag = DisposeBag()
deinit {
collectionViewFrame.onCompleted()
}
override func viewDidLoad() {
super.viewDidLoad()
currentPage(
offset: collectionView.rx.contentOffset.asObservable(),
frame: collectionViewFrame.asObservable()
)
.bind(to: pageControl.rx.currentPage)
.disposed(by: disposeBag)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionViewFrame.onNext(self.collectionView.frame)
}
}
// This is your logic. Since it is separate from your view code, it can be tested/reused.
func currentPage(offset: Observable<CGPoint>, frame: Observable<CGRect>) -> Observable<Int> {
return Observable.combineLatest(offset, frame)
.map { Int($0.0.x) / Int($0.1.width) }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With