Skip to content

Commit

Permalink
Exposes UIScrollViewDelegate to clients (#133)
Browse files Browse the repository at this point in the history
*Have you read the [Contributing
Guidelines](https://github.com/jessesquires/.github/blob/master/CONTRIBUTING.md)?*
🖤

Closes #131 

## Describe your changes

- Allows clients to set a `weak var scrollViewDelegate:
UIScrollViewDelegate?` on `CollectionViewDriver`
- Implement the `UIScrollViewDelegate` methods in `CollectionViewDriver`
and forwards them to this delegate
- Keeps the method signatures in the same order and groupings as
`UIScrollViewDelegate`

### Example
- Adds example to `ListViewController`, demonstrating an override of
`scrollViewShouldScrollToTop(scrollView:)`

| Before | After |
| ------ | ----- |
|
![rf_131_before](https://github.com/user-attachments/assets/7d0dfd8b-1be2-4eef-8899-65503b783ed0)
|
![rf_131_after](https://github.com/user-attachments/assets/3ec172c8-ad56-47ce-8e23-89cf0828ab66)
|

## Todo

- Add unit tests.

---------

Co-authored-by: Jesse Squires <jessesquires@users.noreply.github.com>
  • Loading branch information
ruddfawcett and jessesquires authored Oct 2, 2024
1 parent ec6e83e commit c3ca6c7
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ NEXT
- Reverted back to Swift 5 language mode because of issues in UIKit. ([@jessesquires](https://github.com/jessesquires), [#116](https://github.com/jessesquires/ReactiveCollectionsKit/pull/116))
- Applying a snapshot using `reloadData` now always occurs on the main thread. ([@jessesquires](https://github.com/jessesquires), [#116](https://github.com/jessesquires/ReactiveCollectionsKit/pull/116))
- Implemented additional selection APIs for `CellViewModel`: `shouldSelect`, `shouldDeselect`, `didDeselect()`. ([@nuomi1](https://github.com/nuomi1), [#127](https://github.com/jessesquires/ReactiveCollectionsKit/pull/127))
- Allow setting a `UIScrollViewDelegate` object to receive scroll view events from the collection view. ([@ruddfawcett](https://github.com/ruddfawcett), [#131](https://github.com/jessesquires/ReactiveCollectionsKit/pull/131), [#133](https://github.com/jessesquires/ReactiveCollectionsKit/pull/133))

0.1.6
-----
Expand Down
27 changes: 21 additions & 6 deletions Example/Sources/List/ListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ import UIKit

final class ListViewController: ExampleViewController, CellEventCoordinator {

lazy var driver = CollectionViewDriver(
view: self.collectionView,
options: .init(diffOnBackgroundQueue: true),
emptyViewProvider: sharedEmptyViewProvider,
cellEventCoordinator: self
)
lazy var driver: CollectionViewDriver = {
let driver = CollectionViewDriver(
view: self.collectionView,
options: .init(diffOnBackgroundQueue: true),
emptyViewProvider: sharedEmptyViewProvider,
cellEventCoordinator: self
)

// Access `UIScrollViewDelegate` and handle protocol
driver.scrollViewDelegate = self

return driver
}()

override var model: Model {
didSet {
Expand Down Expand Up @@ -131,3 +138,11 @@ final class ListViewController: ExampleViewController, CellEventCoordinator {
return CollectionViewModel(id: "list_view", sections: [peopleSection, colorSection])
}
}

extension ListViewController: UIScrollViewDelegate {

// Demonstrate delegate override; tapping status bar does not scroll to top
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
false
}
}
96 changes: 96 additions & 0 deletions Sources/CollectionViewDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public final class CollectionViewDriver: NSObject {

/// The collection view model.
@Published public private(set) var viewModel: CollectionViewModel

/// The scroll view delegate to forward.
public weak var scrollViewDelegate: UIScrollViewDelegate?

private let _emptyViewProvider: EmptyViewProvider?

Expand Down Expand Up @@ -373,3 +376,96 @@ extension CollectionViewDriver: UICollectionViewDelegate {
self.viewModel._safeSupplementaryViewModel(ofKind: elementKind, at: indexPath)?.didEndDisplaying()
}
}

// MARK: UIScrollViewDelegate

extension CollectionViewDriver: UIScrollViewDelegate {
// MARK: Managing offset and zoom scale changes

/// :nodoc:
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewDidScroll?(scrollView)
}

/// :nodoc:
public func scrollViewDidZoom(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewDidZoom?(scrollView)
}

// MARK: Tracking dragging

/// :nodoc:
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewWillBeginDragging?(scrollView)
}

/// :nodoc:
public func scrollViewWillEndDragging(_ scrollView: UIScrollView,
withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>) {
self.scrollViewDelegate?.scrollViewWillEndDragging?(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset)
}

/// :nodoc:
public func scrollViewDidEndDragging(_ scrollView: UIScrollView,
willDecelerate decelerate: Bool) {
self.scrollViewDelegate?.scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate)
}

// MARK: Tracking deceleration and scrolling animation

/// :nodoc:
public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewWillBeginDragging?(scrollView)
}

/// :nodoc:
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewDidEndDecelerating?(scrollView)
}

/// :nodoc:
public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewDidEndScrollingAnimation?(scrollView)
}

// MARK: Managing and tracking zooming more granularly

/// :nodoc:
public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
self.scrollViewDelegate?.viewForZooming?(in: scrollView)
}

/// :nodoc:
public func scrollViewWillBeginZooming(_ scrollView: UIScrollView,
with view: UIView?) {
self.scrollViewDelegate?.scrollViewWillBeginZooming?(scrollView, with: view)
}

/// :nodoc:
public func scrollViewDidEndZooming(_ scrollView: UIScrollView,
with view: UIView?,
atScale scale: CGFloat) {
self.scrollViewDelegate?.scrollViewDidEndZooming?(scrollView, with: view, atScale: scale)
}

// MARK: Managing if should scroll to top and tracking if done so

/// :nodoc:
public func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
self.scrollViewDelegate?.scrollViewShouldScrollToTop?(scrollView) ?? true
}

/// :nodoc:
public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewDidScrollToTop?(scrollView)
}


// MARK: Tracking adjusted content insets on scroll view

/// :nodoc:
public func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) {
self.scrollViewDelegate?.scrollViewDidChangeAdjustedContentInset?(scrollView)
}
}

0 comments on commit c3ca6c7

Please sign in to comment.