Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 73 additions & 56 deletions ABVideoRangeSlider/Classes/ABVideoRangeSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import UIKit
@objc public protocol ABVideoRangeSliderDelegate: class {
func didChangeValue(videoRangeSlider: ABVideoRangeSlider, startTime: Float64, endTime: Float64)
func indicatorDidChangePosition(videoRangeSlider: ABVideoRangeSlider, position: Float64)

@objc optional func sliderGesturesBegan()
@objc optional func sliderGesturesEnded()
}
Expand All @@ -22,7 +22,16 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
case start
case end
}


public override var bounds: CGRect {
didSet {
self.setup()
self.updateThumbnails()
}
}

public var hidesTimeViews = true

public weak var delegate: ABVideoRangeSliderDelegate? = nil

var startIndicator = ABStartIndicator()
Expand All @@ -49,14 +58,20 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
let indicatorWidth: CGFloat = 20.0

public var minSpace: Float = 1 // In Seconds
public var maxSpace: Float = 0 // In Seconds

public var maxSpace: Float = 0 { // In Seconds
didSet {
let percentage = maxSpace/Float(self.duration) * 100
self.endPercentage = min(CGFloat(percentage), 100.0)
self.layoutIfNeeded()
}
}

public var isProgressIndicatorSticky: Bool = false
public var isProgressIndicatorDraggable: Bool = true

var isUpdatingThumbnails = false
var isReceivingGesture: Bool = false

public enum ABTimeViewPosition{
case top
case bottom
Expand All @@ -83,6 +98,7 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
let startDrag = UIPanGestureRecognizer(target:self,
action: #selector(startDragged(recognizer:)))

startIndicator.removeFromSuperview()
startIndicator = ABStartIndicator(frame: CGRect(x: 0,
y: -topBorderHeight,
width: 20,
Expand All @@ -96,6 +112,7 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
let endDrag = UIPanGestureRecognizer(target:self,
action: #selector(endDragged(recognizer:)))

endIndicator.removeFromSuperview()
endIndicator = ABEndIndicator(frame: CGRect(x: 0,
y: -topBorderHeight,
width: indicatorWidth,
Expand All @@ -106,29 +123,26 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {


// Setup Top and bottom line

topLine.removeFromSuperview()
topLine = ABBorder(frame: CGRect(x: 0,
y: -topBorderHeight,
width: indicatorWidth,
height: topBorderHeight))
self.addSubview(topLine)

bottomLine.removeFromSuperview()
bottomLine = ABBorder(frame: CGRect(x: 0,
y: self.frame.size.height,
width: indicatorWidth,
height: bottomBorderHeight))
self.addSubview(bottomLine)

self.addObserver(self,
forKeyPath: "bounds",
options: NSKeyValueObservingOptions(rawValue: 0),
context: nil)

// Setup Progress Indicator

let progressDrag = UIPanGestureRecognizer(target:self,
action: #selector(progressDragged(recognizer:)))

progressIndicator.removeFromSuperview()
progressIndicator = ABProgressIndicator(frame: CGRect(x: 0,
y: -topBorderHeight,
width: 10,
Expand All @@ -148,17 +162,22 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {

// Setup time labels

startTimeView.removeFromSuperview()
startTimeView = ABTimeView(size: CGSize(width: 60, height: 30), position: 1)
startTimeView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
startTimeView.isHidden = hidesTimeViews
self.addSubview(startTimeView)

endTimeView.removeFromSuperview()
endTimeView = ABTimeView(size: CGSize(width: 60, height: 30), position: 1)
endTimeView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
endTimeView.isHidden = hidesTimeViews
self.addSubview(endTimeView)
}

public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds"{
self.setup()
self.updateThumbnails()
}
}
Expand All @@ -180,7 +199,7 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
public func updateProgressIndicator(seconds: Float64){
if !isReceivingGesture {
let endSeconds = secondsFromValue(value: self.endPercentage)

if seconds >= endSeconds {
self.resetProgressPosition()
} else {
Expand Down Expand Up @@ -224,7 +243,9 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
self.duration = ABVideoHelper.videoDuration(videoURL: videoURL)
self.videoURL = videoURL
self.superview?.layoutSubviews()
self.updateThumbnails()
if self.bounds.height != 0 {
self.updateThumbnails()
}
}

public func updateThumbnails(){
Expand Down Expand Up @@ -263,7 +284,7 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
currentIndicator: self.startIndicator
)
}

@objc private func endDragged(recognizer: UIPanGestureRecognizer){
self.processHandleDrag(
recognizer: recognizer,
Expand All @@ -279,17 +300,17 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
currentPositionPercentage: CGFloat,
currentIndicator: UIView
) {

self.updateGestureStatus(recognizer: recognizer)

let translation = recognizer.translation(in: self)

var position: CGFloat = positionFromValue(value: currentPositionPercentage) // self.startPercentage or self.endPercentage

position = position + translation.x

if position < 0 { position = 0 }

if position > self.frame.size.width {
position = self.frame.size.width
}
Expand All @@ -308,57 +329,57 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
}
}
}

recognizer.setTranslation(CGPoint.zero, in: self)

currentIndicator.center = CGPoint(x: position , y: currentIndicator.center.y)

var percentage = currentIndicator.center.x * 100 / self.frame.width

let startSeconds = secondsFromValue(value: self.startPercentage)
let endSeconds = secondsFromValue(value: self.endPercentage)

self.delegate?.didChangeValue(videoRangeSlider: self, startTime: startSeconds, endTime: endSeconds)

var progressPosition: CGFloat = 0.0

if drag == .start {
self.startPercentage = percentage
} else {
self.endPercentage = percentage
}

if drag == .start {
progressPosition = positionFromValue(value: self.startPercentage)

} else {
if recognizer.state != .ended {
progressPosition = positionFromValue(value: self.endPercentage)
} else {
progressPosition = positionFromValue(value: self.startPercentage)
}
}

progressIndicator.center = CGPoint(x: progressPosition , y: progressIndicator.center.y)
let progressPercentage = progressIndicator.center.x * 100 / self.frame.width

if self.progressPercentage != progressPercentage {
let progressSeconds = secondsFromValue(value: progressPercentage)
self.delegate?.indicatorDidChangePosition(videoRangeSlider: self, position: progressSeconds)
}

self.progressPercentage = progressPercentage

layoutSubviews()
}

func progressDragged(recognizer: UIPanGestureRecognizer){
if !isProgressIndicatorDraggable {
return
}

updateGestureStatus(recognizer: recognizer)

let translation = recognizer.translation(in: self)

let positionLimitStart = positionFromValue(value: self.startPercentage)
Expand Down Expand Up @@ -392,7 +413,7 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {

func viewDragged(recognizer: UIPanGestureRecognizer){
updateGestureStatus(recognizer: recognizer)

let translation = recognizer.translation(in: self)

var progressPosition = positionFromValue(value: self.progressPercentage)
Expand Down Expand Up @@ -441,13 +462,13 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {

layoutSubviews()
}

// MARK: - Drag Functions Helpers
private func positionFromValue(value: CGFloat) -> CGFloat{
let position = value * self.frame.size.width / 100
return position
}

private func getPositionLimits(with drag: DragHandleChoice) -> (min: CGFloat, max: CGFloat) {
if drag == .start {
return (
Expand All @@ -461,7 +482,7 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
)
}
}

private func checkEdgeCasesForPosition(with position: CGFloat, and positionLimit: CGFloat, and drag: DragHandleChoice) -> CGFloat {
if drag == .start {
if Float(self.duration) < self.minSpace {
Expand All @@ -480,36 +501,36 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
}
}
}

return position
}

private func secondsFromValue(value: CGFloat) -> Float64{
return duration * Float64((value / 100))
}

private func valueFromSeconds(seconds: Float) -> CGFloat{
return CGFloat(seconds * 100) / CGFloat(duration)
}

private func updateGestureStatus(recognizer: UIGestureRecognizer) {
if recognizer.state == .began {

self.isReceivingGesture = true
self.delegate?.sliderGesturesBegan?()

} else if recognizer.state == .ended {

self.isReceivingGesture = false
self.delegate?.sliderGesturesEnded?()
}
}

private func resetProgressPosition() {
self.progressPercentage = self.startPercentage
let progressPosition = positionFromValue(value: self.progressPercentage)
progressIndicator.center = CGPoint(x: progressPosition , y: progressIndicator.center.y)

let startSeconds = secondsFromValue(value: self.progressPercentage)
self.delegate?.indicatorDidChangePosition(videoRangeSlider: self, position: startSeconds)
}
Expand Down Expand Up @@ -558,21 +579,17 @@ public class ABVideoRangeSlider: UIView, UIGestureRecognizerDelegate {
height: self.frame.size.height + topLine.frame.size.height + bottomLine.frame.size.height)
return extendedBounds.contains(point)
}


private func secondsToFormattedString(totalSeconds: Float64) -> String{
let hours:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 86400) / 3600)
let minutes:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 3600) / 60)
let seconds:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 60))

if hours > 0 {
return String(format: "%i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}

deinit {
removeObserver(self, forKeyPath: "bounds")
}
}