Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import Foundation
- import UIKit
- class ScrollWatcher {
- private weak var scrollView: UIScrollView?
- private var keyObservation: NSKeyValueObservation!
- private var range: ClosedRange<CGFloat>
- var topBounces: Bool
- var bottomBounces: Bool
- private var cancelsInRange: Bool
- private var watchOnlyDelta: Bool
- private var update: (CGFloat) -> ()
- private var current: CGFloat = 0
- private var lastUpdateValue = CGFloat.infinity
- private static var stopRecursive = false
- init(
- _ scrollView: UIScrollView,
- _ range: ClosedRange<CGFloat>,
- _ bounces: Bool,
- _ cancelsInRange: Bool,
- _ watchOnlyDelta: Bool,
- _ update: @escaping (_ t: CGFloat) -> ()) {
- self.scrollView = scrollView
- self.range = range
- self.topBounces = bounces
- self.bottomBounces = bounces
- self.cancelsInRange = cancelsInRange
- self.watchOnlyDelta = watchOnlyDelta
- self.update = update
- current = self.range.lowerBound
- keyObservation = self.scrollView?.observe(\.contentOffset, options: [.new, .old]) {
- [weak self] scrollView, changedValue in
- guard let `self` = self, !ScrollWatcher.stopRecursive, self.scrollView != nil else { return }
- let value = changedValue.newValue!.y
- let oldValue = changedValue.oldValue!.y
- let delta = value - oldValue
- let scrollContentSize = scrollView.contentSize
- let scrollViewSize = scrollView.frame.size
- let watchValueTopBound = value >= self.range.lowerBound || !self.topBounces
- let watchValueBottomBound = value <= self.range.upperBound || !self.bottomBounces
- let watchValue = watchValueTopBound || watchValueBottomBound
- let watchCurrentTopBound = self.current >= self.range.lowerBound || !self.topBounces
- let watchCurrentBottomBound = self.current <= self.range.upperBound || !self.bottomBounces
- let watchCurrent = watchCurrentTopBound && watchCurrentBottomBound
- if watchValue && !watchOnlyDelta || watchOnlyDelta, watchCurrent {
- var delta = delta
- if oldValue <= self.range.lowerBound, self.topBounces && !self.watchOnlyDelta {
- delta = value - self.range.lowerBound
- }
- if oldValue >= self.range.upperBound, self.bottomBounces && !self.watchOnlyDelta {
- delta = value - self.range.upperBound
- }
- if watchOnlyDelta && delta > 0 && oldValue < 0 {
- delta = value
- }
- if watchOnlyDelta && delta < 0 && oldValue > scrollContentSize.height - scrollViewSize.height {
- delta = value - (scrollContentSize.height - scrollViewSize.height)
- }
- if delta < 0 && self.current + delta < self.range.lowerBound && self.topBounces {
- delta = self.range.lowerBound - self.current
- }
- if delta > 0 && self.current + delta > self.range.upperBound && self.bottomBounces {
- delta = self.range.upperBound - self.current
- }
- self.current += delta
- if self.cancelsInRange {
- ScrollWatcher.stopRecursive = true
- scrollView.contentOffset.y -= delta
- ScrollWatcher.stopRecursive = false
- }
- }
- let updatedValue = (self.current - self.range.lowerBound) / (self.range.upperBound - self.range.lowerBound)
- if updatedValue != self.lastUpdateValue {
- self.lastUpdateValue = updatedValue
- self.update(updatedValue)
- }
- }
- }
- deinit {
- keyObservation.invalidate()
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement