// // UIScrollView+Rx.swift // RxCocoa // // Created by Krunoslav Zaher on 4/3/15. // Copyright © 2015 Krunoslav Zaher. All rights reserved. // #if os(iOS) || os(tvOS) import RxSwift import UIKit extension Reactive where Base: UIScrollView { public typealias EndZoomEvent = (view: UIView?, scale: CGFloat) public typealias WillEndDraggingEvent = (velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) /// Reactive wrapper for `delegate`. /// /// For more information take a look at `DelegateProxyType` protocol documentation. public var delegate: DelegateProxy { return RxScrollViewDelegateProxy.proxy(for: base) } /// Reactive wrapper for `contentOffset`. public var contentOffset: ControlProperty { let proxy = RxScrollViewDelegateProxy.proxy(for: base) let bindingObserver = Binder(self.base) { scrollView, contentOffset in scrollView.contentOffset = contentOffset } return ControlProperty(values: proxy.contentOffsetBehaviorSubject, valueSink: bindingObserver) } /// Bindable sink for `scrollEnabled` property. public var isScrollEnabled: Binder { return Binder(self.base) { scrollView, scrollEnabled in scrollView.isScrollEnabled = scrollEnabled } } /// Reactive wrapper for delegate method `scrollViewDidScroll` public var didScroll: ControlEvent { let source = RxScrollViewDelegateProxy.proxy(for: base).contentOffsetPublishSubject return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillBeginDecelerating` public var willBeginDecelerating: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDecelerating(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndDecelerating` public var didEndDecelerating: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndDecelerating(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillBeginDragging` public var willBeginDragging: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginDragging(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)` public var willEndDragging: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillEndDragging(_:withVelocity:targetContentOffset:))) .map { value -> WillEndDraggingEvent in let velocity = try castOrThrow(CGPoint.self, value[1]) let targetContentOffsetValue = try castOrThrow(NSValue.self, value[2]) guard let rawPointer = targetContentOffsetValue.pointerValue else { throw RxCocoaError.unknown } let typedPointer = rawPointer.bindMemory(to: CGPoint.self, capacity: MemoryLayout.size) return (velocity, typedPointer) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndDragging(_:willDecelerate:)` public var didEndDragging: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndDragging(_:willDecelerate:))).map { value -> Bool in return try castOrThrow(Bool.self, value[1]) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidZoom` public var didZoom: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidZoom)).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidScrollToTop` public var didScrollToTop: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidScrollToTop(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndScrollingAnimation` public var didEndScrollingAnimation: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation(_:))).map { _ in } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewWillBeginZooming(_:with:)` public var willBeginZooming: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewWillBeginZooming(_:with:))).map { value -> UIView? in return try castOptionalOrThrow(UIView.self, value[1] as AnyObject) } return ControlEvent(events: source) } /// Reactive wrapper for delegate method `scrollViewDidEndZooming(_:with:atScale:)` public var didEndZooming: ControlEvent { let source = delegate.methodInvoked(#selector(UIScrollViewDelegate.scrollViewDidEndZooming(_:with:atScale:))).map { value -> EndZoomEvent in return (try castOptionalOrThrow(UIView.self, value[1] as AnyObject), try castOrThrow(CGFloat.self, value[2])) } return ControlEvent(events: source) } /// Installs delegate as forwarding delegate on `delegate`. /// Delegate won't be retained. /// /// It enables using normal delegate mechanism with reactive delegate mechanism. /// /// - parameter delegate: Delegate object. /// - returns: Disposable object that can be used to unbind the delegate. public func setDelegate(_ delegate: UIScrollViewDelegate) -> Disposable { return RxScrollViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base) } } #endif