1 //  Copyright (c) 2018, Applidium. All rights reserved
2 //  OverlayContainerViewControllerDelegateWrapper.swift
3 //  OverlayContainer
4 //
5 //  Created by Gaétan Zanella on 20/11/2018.
6 //
7 
8 import UIKit
9 
10 class OverlayContainerConfigurationImplementation: OverlayContainerConfigurationInvalidating {
11 
12     private weak var overlayContainerViewController: OverlayContainerViewController?
13 
14     weak var delegate: OverlayContainerViewControllerDelegate?
15 
16     private(set) var notchHeightByIndex: [Int: CGFloat] = [:]
17 
18     private var needsMetricUpdate = false
19 
20     // MARK: - Life Cycle
21 
22     init(overlayContainerViewController: OverlayContainerViewController) {
23         self.overlayContainerViewController = overlayContainerViewController
24     }
25 
26     // MARK: - OverlayContainerConfigurationInvalidating
27 
invalidateOverlayMetricsnull28     func invalidateOverlayMetrics() {
29         needsMetricUpdate = true
30     }
31 
requestOverlayMetricsIfNeedednull32     func requestOverlayMetricsIfNeeded() {
33         guard needsMetricUpdate else { return }
34         needsMetricUpdate = false
35         let numberOfNotches = requestNumberOfNotches()
36         assert(numberOfNotches >= 0, "The number of notches must be positive.")
37         let heights = (0..<numberOfNotches).map { requestHeightForNotch(at: $0) }
38         assert(heights.sorted() == heights, "The notches should be sorted by height. The notch at the first index must be the smaller one. \(heights)")
39         let values = heights.enumerated().map { ($0, $1) }
40         notchHeightByIndex = Dictionary(uniqueKeysWithValues: values)
41     }
42 
43     // MARK: - OverlayContainerViewControllerConfiguration
44 
numberOfNotchesnull45     func numberOfNotches() -> Int {
46         return notchHeightByIndex.count
47     }
48 
heightForNotchnull49     func heightForNotch(at index: Int) -> CGFloat {
50         return notchHeightByIndex[index] ?? 0
51     }
52 
animationControllernull53     func animationController(forOverlay overlay: UIViewController) -> OverlayAnimatedTransitioning {
54         guard let controller = overlayContainerViewController else {
55             return SpringOverlayTranslationAnimationController()
56         }
57         let transitioningDelegate = delegate?.overlayContainerViewController(
58             controller,
59             transitioningDelegateForOverlay: overlay
60         )
61         let defaultController = SpringOverlayTranslationAnimationController(style: controller.style)
62         return transitioningDelegate?.animationController(for: overlay) ?? defaultController
63     }
64 
overlayTargetNotchPolicynull65     func overlayTargetNotchPolicy(forOverlay overlay: UIViewController) -> OverlayTranslationTargetNotchPolicy {
66         guard let controller = overlayContainerViewController else {
67             return RushingForwardTargetNotchPolicy()
68         }
69         let transitioningDelegate = delegate?.overlayContainerViewController(
70             controller,
71             transitioningDelegateForOverlay: overlay
72         )
73         return transitioningDelegate?.overlayTargetNotchPolicy(for: overlay) ?? RushingForwardTargetNotchPolicy()
74     }
75 
scrollViewnull76     func scrollView(drivingOverlay controller: UIViewController) -> UIScrollView? {
77         guard let containerController = overlayContainerViewController else { return nil }
78         return delegate?.overlayContainerViewController(containerController, scrollViewDrivingOverlay: controller)
79     }
80 
81     func shouldStartDraggingOverlay(_ viewController: UIViewController,
82                                     at point: CGPoint,
83                                     in coordinateSpace: UICoordinateSpace) -> Bool {
84         guard let containerController = overlayContainerViewController else { return false }
85         return delegate?.overlayContainerViewController(
86             containerController,
87             shouldStartDraggingOverlay: viewController,
88             at: point,
89             in: coordinateSpace
90         ) ?? true
91     }
92 
93     func overlayTranslationFunction(using context: OverlayTranslationParameters,
94                                     for overlayViewController: UIViewController) -> OverlayTranslationFunction {
95         guard let containerController = overlayContainerViewController else {
96             return RubberBandOverlayTranslationFunction()
97         }
98         return delegate?.overlayContainerViewController(
99             containerController,
100             overlayTranslationFunctionForOverlay: overlayViewController
101         ) ?? RubberBandOverlayTranslationFunction()
102     }
103 
canReachNotchnull104     func canReachNotch(at index: Int, for overlayViewController: UIViewController) -> Bool {
105         guard let containerController = overlayContainerViewController else {
106             return true
107         }
108         return delegate?.overlayContainerViewController(
109             containerController,
110             canReachNotchAt: index,
111             forOverlay: overlayViewController
112         ) ?? true
113     }
114 
115     // MARK: - Private
116 
requestHeightForNotchnull117     private func requestHeightForNotch(at index: Int) -> CGFloat {
118         guard let controller = overlayContainerViewController else { return 0 }
119         return delegate?.overlayContainerViewController(
120             controller,
121             heightForNotchAt: index,
122             availableSpace: controller.availableSpace
123         ) ?? 0
124     }
125 
requestNumberOfNotchesnull126     private func requestNumberOfNotches() -> Int {
127         guard let controller = overlayContainerViewController else { return 0 }
128         return delegate?.numberOfNotches(in: controller) ?? 0
129     }
130 }
131