added iOS source code
[wl-app.git] / iOS / Pods / FolioReaderKit / Source / ScrollScrubber.swift
diff --git a/iOS/Pods/FolioReaderKit/Source/ScrollScrubber.swift b/iOS/Pods/FolioReaderKit/Source/ScrollScrubber.swift
new file mode 100644 (file)
index 0000000..7f73b42
--- /dev/null
@@ -0,0 +1,263 @@
+//
+//  ScrollScrubber.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 7/14/16.
+//  Copyright © 2016 FolioReader. All rights reserved.
+//
+
+import UIKit
+
+func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+    switch (lhs, rhs) {
+    case let (l?, r?):
+        return l < r
+    case (nil, _?):
+        return true
+    default:
+        return false
+    }
+}
+
+func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+    switch (lhs, rhs) {
+    case let (l?, r?):
+        return l > r
+    default:
+        return rhs < lhs
+    }
+}
+
+enum ScrollType: Int {
+    case page
+    // `chapter` is only for the collection view if vertical with horizontal content is used
+    case chapter
+}
+
+enum ScrollDirection: Int {
+    case none
+    case right
+    case left
+    case up
+    case down
+
+    init() {
+        self = .none
+    }
+}
+
+class ScrollScrubber: NSObject, UIScrollViewDelegate {
+    weak var delegate: FolioReaderCenter?
+    var showSpeed = 0.6
+    var hideSpeed = 0.6
+    var hideDelay = 1.0
+
+    var visible = false
+    var usingSlider = false
+    var slider: UISlider!
+    var hideTimer: Timer!
+    var scrollStart: CGFloat!
+    var scrollDelta: CGFloat!
+    var scrollDeltaTimer: Timer!
+
+    fileprivate weak var readerContainer: FolioReaderContainer?
+
+    fileprivate var readerConfig: FolioReaderConfig {
+        guard let readerContainer = readerContainer else { return FolioReaderConfig() }
+        return readerContainer.readerConfig
+    }
+
+    fileprivate var folioReader: FolioReader {
+        guard let readerContainer = readerContainer else { return FolioReader() }
+        return readerContainer.folioReader
+    }
+
+    var frame: CGRect {
+        didSet {
+            self.slider.frame = frame
+        }
+    }
+
+    init(frame:CGRect, withReaderContainer readerContainer: FolioReaderContainer) {
+        self.frame = frame
+        self.readerContainer = readerContainer
+
+        super.init()
+
+        slider = UISlider()
+        slider.layer.anchorPoint = CGPoint(x: 0, y: 0)
+        slider.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
+        slider.alpha = 0
+        self.reloadColors()
+
+        // less obtrusive knob and fixes jump: http://stackoverflow.com/a/22301039/484780
+        let thumbImg = UIImage(readerImageNamed: "knob")
+        let thumbImgColor = thumbImg?.imageTintColor(readerConfig.tintColor)?.withRenderingMode(.alwaysOriginal)
+        slider.setThumbImage(thumbImgColor, for: UIControlState())
+        slider.setThumbImage(thumbImgColor, for: .selected)
+        slider.setThumbImage(thumbImgColor, for: .highlighted)
+
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderChange(_:)), for: .valueChanged)
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderTouchDown(_:)), for: .touchDown)
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderTouchUp(_:)), for: .touchUpInside)
+        slider.addTarget(self, action: #selector(ScrollScrubber.sliderTouchUp(_:)), for: .touchUpOutside)
+    }
+
+    func reloadColors() {
+        slider.minimumTrackTintColor = readerConfig.tintColor
+        slider.maximumTrackTintColor = folioReader.isNight(readerConfig.nightModeSeparatorColor, readerConfig.menuSeparatorColor)
+    }
+
+    // MARK: - slider events
+
+    @objc func sliderTouchDown(_ slider:UISlider) {
+        usingSlider = true
+        show()
+    }
+
+    @objc func sliderTouchUp(_ slider:UISlider) {
+        usingSlider = false
+        hideAfterDelay()
+    }
+
+    @objc func sliderChange(_ slider:UISlider) {
+        let movePosition = (height() * CGFloat(slider.value))
+        let offset = readerConfig.isDirection(CGPoint(x: 0, y: movePosition), CGPoint(x: movePosition, y: 0), CGPoint(x: 0, y: movePosition))
+        scrollView()?.setContentOffset(offset, animated: false)
+    }
+
+    // MARK: - show / hide
+
+    func show() {
+        cancelHide()
+
+        visible = true
+
+        if slider.alpha <= 0 {
+            UIView.animate(withDuration: showSpeed, animations: {
+
+                self.slider.alpha = 1
+
+            }, completion: { (Bool) -> Void in
+                self.hideAfterDelay()
+            })
+        } else {
+            slider.alpha = 1
+            if usingSlider == false {
+                hideAfterDelay()
+            }
+        }
+    }
+
+
+    @objc func hide() {
+        visible = false
+        resetScrollDelta()
+        UIView.animate(withDuration: hideSpeed, animations: {
+            self.slider.alpha = 0
+        })
+    }
+
+    func hideAfterDelay() {
+        cancelHide()
+        hideTimer = Timer.scheduledTimer(timeInterval: hideDelay, target: self, selector: #selector(ScrollScrubber.hide), userInfo: nil, repeats: false)
+    }
+
+    func cancelHide() {
+
+        if hideTimer != nil {
+            hideTimer.invalidate()
+            hideTimer = nil
+        }
+
+        if visible == false {
+            slider.layer.removeAllAnimations()
+        }
+
+        visible = true
+    }
+
+    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+
+        if scrollDeltaTimer != nil {
+            scrollDeltaTimer.invalidate()
+            scrollDeltaTimer = nil
+        }
+
+        if scrollStart == nil {
+            scrollStart = scrollView.contentOffset.forDirection(withConfiguration: readerConfig)
+        }
+    }
+
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        guard (readerConfig.scrollDirection == .vertical ||
+            readerConfig.scrollDirection == .defaultVertical ||
+            readerConfig.scrollDirection == .horizontalWithVerticalContent) else {
+                return
+        }
+
+        if visible && usingSlider == false {
+            setSliderVal()
+        }
+
+        if (slider.alpha > 0) {
+            self.show()
+        } else if delegate?.currentPage != nil && scrollStart != nil {
+            scrollDelta = scrollView.contentOffset.forDirection(withConfiguration: readerConfig) - scrollStart
+
+            guard let pageHeight = folioReader.readerCenter?.pageHeight,
+                (scrollDeltaTimer == nil && scrollDelta > (pageHeight * 0.2 ) || (scrollDelta * -1) > (pageHeight * 0.2)) else {
+                    return
+            }
+
+            self.show()
+            self.resetScrollDelta()
+        }
+    }
+
+    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        resetScrollDelta()
+    }
+
+    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
+        scrollDeltaTimer = Timer(timeInterval:0.5, target: self, selector: #selector(ScrollScrubber.resetScrollDelta), userInfo: nil, repeats: false)
+        RunLoop.current.add(scrollDeltaTimer, forMode: RunLoopMode.commonModes)
+    }
+
+    @objc func resetScrollDelta() {
+        if scrollDeltaTimer != nil {
+            scrollDeltaTimer.invalidate()
+            scrollDeltaTimer = nil
+        }
+
+        scrollStart = (scrollView()?.contentOffset.forDirection(withConfiguration: readerConfig) ?? 0)
+        scrollDelta = 0
+    }
+
+    func setSliderVal() {
+        slider.value = Float(scrollTop() / height())
+    }
+
+    // MARK: - utility methods
+
+    fileprivate func scrollView() -> UIScrollView? {
+        return delegate?.currentPage?.webView?.scrollView
+    }
+
+    fileprivate func height() -> CGFloat {
+        guard let currentPage = delegate?.currentPage,
+            let pageHeight = folioReader.readerCenter?.pageHeight,
+            let webView = currentPage.webView else {
+                return 0
+        }
+
+        return webView.scrollView.contentSize.height - pageHeight + 44
+    }
+    
+    fileprivate func scrollTop() -> CGFloat {
+        guard let currentPage = delegate?.currentPage, let webView = currentPage.webView else {
+            return 0
+        }
+        return webView.scrollView.contentOffset.forDirection(withConfiguration: readerConfig)
+    }
+}