1 // Copyright (c) 2018, Applidium. All rights reserved 2 // OverlayScrollViewDelegateProxy.swift 3 // OverlayContainer 4 // 5 // Created by Gaétan Zanella on 20/11/2018. 6 // 7 8 import UIKit 9 10 class OverlayScrollViewDelegateProxy: NSObject, UIScrollViewDelegate { 11 12 private var scrollViewObservation: NSKeyValueObservation? 13 private weak var originalDelegate: UIScrollViewDelegate? 14 private weak var scrollView: UIScrollView? 15 private weak var delegate: OverlayScrollViewDelegate? 16 17 // MARK: - Life Cycle 18 19 deinit { 20 cancelForwarding() 21 } 22 23 // MARK: - NSObject 24 respondsnull25 override func responds(to aSelector: Selector!) -> Bool { 26 let originalDelegateRespondsToSelector = originalDelegate?.responds(to: aSelector) ?? false 27 return super.responds(to: aSelector) || originalDelegateRespondsToSelector 28 } 29 forwardingTargetnull30 override func forwardingTarget(for aSelector: Selector!) -> Any? { 31 if originalDelegate?.responds(to: aSelector) == true { 32 return originalDelegate 33 } else { 34 return super.forwardingTarget(for: aSelector) 35 } 36 } 37 38 // MARK: - Public 39 cancelForwardingnull40 func cancelForwarding() { 41 cancelForwarding(restoresDelegate: true) 42 } 43 forwardnull44 func forward(to delegate: OverlayScrollViewDelegate, delegateInvocationsFrom scrollView: UIScrollView) { 45 guard !(scrollView.delegate === self) else { return } 46 cancelForwarding() 47 self.delegate = delegate 48 self.originalDelegate = scrollView.delegate 49 self.scrollView = scrollView 50 scrollView.delegate = self 51 scrollViewObservation = scrollView.observe(\.delegate) { [weak self] (scrollView, delegate) in 52 guard !(scrollView.delegate === self) else { return } 53 if let proxy = scrollView.delegate as? OverlayScrollViewDelegateProxy { 54 proxy.originalDelegate = self?.originalDelegate 55 self?.cancelForwarding(restoresDelegate: false) 56 } else { 57 self?.originalDelegate = scrollView.delegate 58 self?.scrollView = scrollView 59 scrollView.delegate = self 60 } 61 } 62 } 63 64 // MARK: - UIScrollViewDelegate 65 scrollViewDidScrollnull66 func scrollViewDidScroll(_ scrollView: UIScrollView) { 67 delegate?.overlayScrollViewDidScroll(scrollView) 68 originalDelegate?.scrollViewDidScroll?(scrollView) 69 } 70 71 func scrollViewWillEndDragging(_ scrollView: UIScrollView, 72 withVelocity velocity: CGPoint, 73 targetContentOffset: UnsafeMutablePointer<CGPoint>) { 74 delegate?.overlayScrollView( 75 scrollView, 76 willEndDraggingwithVelocity: velocity.multiply(by: -1000), 77 targetContentOffset: targetContentOffset 78 ) 79 originalDelegate?.scrollViewWillEndDragging?( 80 scrollView, 81 withVelocity: velocity, 82 targetContentOffset: targetContentOffset 83 ) 84 } 85 scrollViewWillBeginDraggingnull86 func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 87 delegate?.overlayScrollViewWillBeginDragging(scrollView) 88 originalDelegate?.scrollViewWillBeginDragging?(scrollView) 89 } 90 cancelForwardingnull91 private func cancelForwarding(restoresDelegate: Bool) { 92 scrollViewObservation?.invalidate() 93 guard restoresDelegate else { return } 94 scrollView?.delegate = originalDelegate 95 } 96 } 97