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