Skip to content

Commit

Permalink
feat: add SliderZeroPositionType and SliderRulerPositionInfoProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
guoyingtao committed Jun 14, 2023
1 parent 6c76f4a commit bc79bac
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 44 deletions.
4 changes: 3 additions & 1 deletion Example/InchwormExample/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ class ViewController: UIViewController {
var verticalTrailingConstraint: NSLayoutConstraint!

var horizontal = true
let config = Inchworm.Config()
var config = Inchworm.Config()

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .black

config.sliderZeroPositionType = .left

let model1 = ProcessIndicatorModel(limitNumber: 30,
normalIconImage: UIImage(named: "ic_flash_on")!.tinted(with: UIColor.white)!.cgImage!,
Expand Down
6 changes: 6 additions & 0 deletions Inchworm/Source/Inchworm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public func createSlider(config: Config = Config(),
public var slideRulerSpan: CGFloat = 50
public var spaceBetweenIndicatorAndSlideRule: CGFloat = 10
public var forceAlignCenterFeedback = true
public var sliderZeroPositionType: SliderZeroPositionType = .middle

public override init() {

Expand All @@ -54,3 +55,8 @@ public func createSlider(config: Config = Config(),
case horizontal
case vertical
}

public enum SliderZeroPositionType {
case middle
case left
}
5 changes: 1 addition & 4 deletions Inchworm/Source/ProcessIndicatorContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ class ProcessIndicatorContainer: UIView {
backgroundSlideView.showsHorizontalScrollIndicator = false
backgroundSlideView.delegate = self
addSubview(backgroundSlideView)

// iF you want a customized page width, comment this one
// backgroundSlideView.isPagingEnabled = true


setupUIFrames()
}

Expand Down
81 changes: 43 additions & 38 deletions Inchworm/Source/SlideRuler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ fileprivate let scaleBarNumber = 41
fileprivate let majorScaleBarNumber = 5
fileprivate let scaleWidth: CGFloat = 1
fileprivate let pointerWidth: CGFloat = 1
fileprivate let dotWidth: CGFloat = 6

protocol SlideRulerDelegate {
func didGetOffsetRatio(from slideRuler: SlideRuler, offsetRatio: CGFloat)
Expand All @@ -22,8 +21,10 @@ class SlideRuler: UIView {
var forceAlignCenterFeedback = true
let pointer = CALayer()
let centralDot = CAShapeLayer()
let slider = UIScrollView()
let scrollRulerView = UIScrollView()
let dotWidth: CGFloat = 6
var sliderOffsetRatio: CGFloat = 0.5
var positionInfoProvider: SliderRulerPositionInfoProvider

let scaleBarLayer: CAReplicatorLayer = {
var r = CAReplicatorLayer()
Expand All @@ -47,17 +48,20 @@ class SlideRuler: UIView {
}
}

override init(frame: CGRect) {
init(frame: CGRect, positionInfoProvider: SliderRulerPositionInfoProvider) {
self.positionInfoProvider = positionInfoProvider
super.init(frame: frame)
self.positionInfoProvider.slideRuler = self
setupUI()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
fatalError()
}

private func setupUI() {
sliderOffsetRatio = positionInfoProvider.getOffsetRatio()

setupSlider()
makeRuler()
makeCentralDot()
Expand All @@ -67,23 +71,25 @@ class SlideRuler: UIView {
}

@objc func setSliderDelegate() {
slider.delegate = self
scrollRulerView.delegate = self
}

public func setUIFrames() {
slider.frame = bounds
scrollRulerView.frame = bounds

offsetValue = sliderOffsetRatio * slider.frame.width
slider.delegate = nil
slider.contentSize = CGSize(width: frame.width * 2, height: frame.height)
slider.contentOffset = CGPoint(x: offsetValue, y: 0)
offsetValue = sliderOffsetRatio * scrollRulerView.frame.width
scrollRulerView.delegate = nil
scrollRulerView.contentSize = CGSize(width: frame.width * 2, height: frame.height)
scrollRulerView.contentOffset = CGPoint(x: offsetValue, y: 0)

perform(#selector(setSliderDelegate), with: nil, afterDelay: 0.1)
// slider.delegate = self

pointer.frame = CGRect(x: (frame.width / 2 - pointerWidth / 2), y: bounds.origin.y, width: pointerWidth, height: frame.height)

centralDot.frame = CGRect(x: frame.width - dotWidth / 2, y: frame.height * 0.2, width: dotWidth, height: dotWidth)
let centralDotOriginX = positionInfoProvider.getCentralDotOriginX()
centralDot.frame = CGRect(x: centralDotOriginX, y: frame.height * 0.2, width: dotWidth, height: dotWidth)

centralDot.path = UIBezierPath(ovalIn: centralDot.bounds).cgPath

scaleBarLayer.frame = CGRect(x: frame.width / 2, y: 0.6 * frame.height, width: frame.width, height: 0.4 * frame.height)
Expand All @@ -102,11 +108,11 @@ class SlideRuler: UIView {
}

private func setupSlider() {
addSubview(slider)
addSubview(scrollRulerView)

slider.showsHorizontalScrollIndicator = false
slider.showsVerticalScrollIndicator = false
slider.delegate = self
scrollRulerView.showsHorizontalScrollIndicator = false
scrollRulerView.showsVerticalScrollIndicator = false
scrollRulerView.delegate = self
}

private func makePointer() {
Expand All @@ -116,7 +122,7 @@ class SlideRuler: UIView {

private func makeCentralDot() {
centralDot.fillColor = UIColor.white.cgColor
slider.layer.addSublayer(centralDot)
scrollRulerView.layer.addSublayer(centralDot)
}

private func makeRuler() {
Expand All @@ -125,14 +131,14 @@ class SlideRuler: UIView {
let scaleBar = makeBarScaleMark(byColor: UIColor.gray.cgColor)
scaleBarLayer.addSublayer(scaleBar)

slider.layer.addSublayer(scaleBarLayer)
scrollRulerView.layer.addSublayer(scaleBarLayer)

majorScaleBarLayer.sublayers?.forEach { $0.removeFromSuperlayer() }

let majorScaleBar = makeBarScaleMark(byColor: UIColor.white.cgColor)
majorScaleBarLayer.addSublayer(majorScaleBar)

slider.layer.addSublayer(majorScaleBarLayer)
scrollRulerView.layer.addSublayer(majorScaleBarLayer)
}

private func makeBarScaleMark(byColor color: CGColor) -> CALayer {
Expand All @@ -144,9 +150,9 @@ class SlideRuler: UIView {

func handleTempReset() {
let offset = CGPoint(x: offsetValue, y: 0)
slider.delegate = nil
slider.setContentOffset(offset, animated: false)
slider.delegate = self
scrollRulerView.delegate = nil
scrollRulerView.setContentOffset(offset, animated: false)
scrollRulerView.delegate = self

centralDot.isHidden = true
let color = UIColor.gray.cgColor
Expand All @@ -159,19 +165,19 @@ class SlideRuler: UIView {

scaleBarLayer.sublayers?.forEach { $0.backgroundColor = UIColor.gray.cgColor}
majorScaleBarLayer.sublayers?.forEach { $0.backgroundColor = UIColor.white.cgColor}


slider.delegate = nil
let offsetX = CGFloat(progress) * offsetValue + offsetValue
scrollRulerView.delegate = nil

let offsetX = positionInfoProvider.getRulerOffsetX(with: CGFloat(progress))
let offset = CGPoint(x: offsetX, y: 0)
slider.setContentOffset(offset, animated: false)
slider.delegate = self
scrollRulerView.setContentOffset(offset, animated: false)
scrollRulerView.delegate = self

checkCentralDotHiddenStatus()
}

func checkCentralDotHiddenStatus() {
centralDot.isHidden = (slider.contentOffset.x == frame.width / 2)
centralDot.isHidden = (scrollRulerView.contentOffset.x == frame.width / 2)
}
}

Expand All @@ -187,7 +193,12 @@ extension SlideRuler: UIScrollViewDelegate {
let speed = scrollView.panGestureRecognizer.velocity(in: scrollView.superview)

let limit = frame.width / CGFloat((scaleBarNumber - 1) * 2)
if abs(slider.contentOffset.x - frame.width / 2) < limit && abs(speed.x) < 10.0 {

func checkIsCenterPosition() -> Bool {
return positionInfoProvider.checkIsCenterPosition(with: limit)
}

if checkIsCenterPosition() && abs(speed.x) < 10.0 {

if !reset {
reset = true
Expand All @@ -198,7 +209,7 @@ extension SlideRuler: UIScrollViewDelegate {
}

func forceAlignCenter() {
let offset = CGPoint(x: frame.width / 2, y: 0)
let offset = CGPoint(x: positionInfoProvider.getForceAlignCenterX(), y: 0)
scrollView.setContentOffset(offset, animated: false)
delegate?.didGetOffsetRatio(from: self, offsetRatio: 0)
}
Expand All @@ -216,15 +227,9 @@ extension SlideRuler: UIScrollViewDelegate {
reset = false
}

var offsetRatio = (slider.contentOffset.x - offsetValue) / offsetValue

if offsetRatio > 1 { offsetRatio = 1.0 }
if offsetRatio < -1 { offsetRatio = -1.0 }

let offsetRatio = positionInfoProvider.getOffsetRatio()
delegate?.didGetOffsetRatio(from: self, offsetRatio: offsetRatio)

if scrollView.frame.width > 0 {
sliderOffsetRatio = scrollView.contentOffset.x / scrollView.frame.width
}
positionInfoProvider.handleOffsetRatioWhenScrolling(scrollView)
}
}
12 changes: 11 additions & 1 deletion Inchworm/Source/Slider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,17 @@ public class Slider: UIView {
}

func createSlideRuler() {
slideRuler = SlideRuler(frame: CGRect(x: 0, y: baseContainer.frame.height / 2, width: baseContainer.frame.width, height: baseContainer.frame.height - config.slideRulerSpan))
let sliderFrame = CGRect(x: 0, y: baseContainer.frame.height / 2, width: baseContainer.frame.width, height: baseContainer.frame.height - config.slideRulerSpan)

let slideRulerPositionProvider: SliderRulerPositionInfoProvider
switch config.sliderZeroPositionType {
case .middle:
slideRulerPositionProvider = MiddleStyleSliderRulerPositionInfoProvider()
case .left:
slideRulerPositionProvider = LeftStyleSliderRulerPositionInfoProvider()
}

slideRuler = SlideRuler(frame: sliderFrame, positionInfoProvider: slideRulerPositionProvider)
slideRuler.delegate = self
slideRuler.forceAlignCenterFeedback = config.forceAlignCenterFeedback
}
Expand Down
95 changes: 95 additions & 0 deletions Inchworm/Source/SliderRulerPositionInfoProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// SliderRulerPositionInfoProvider.swift
// Inchworm
//
// Created by Yingtao Guo on 6/14/23.
//

import Foundation

protocol SliderRulerPositionInfoProvider {
var slideRuler: SlideRuler! { get set }
func getInitialOffsetRatio() -> CGFloat
func getCentralDotOriginX() -> CGFloat
func getRulerOffsetX(with progress: CGFloat) -> CGFloat
func checkIsCenterPosition(with limit: CGFloat) -> Bool
func getForceAlignCenterX() -> CGFloat
func getOffsetRatio() -> CGFloat
func handleOffsetRatioWhenScrolling(_ scrollView: UIScrollView)
}

extension SliderRulerPositionInfoProvider {
func handleOffsetRatioWhenScrolling(_ scrollView: UIScrollView) {}
}

class LeftStyleSliderRulerPositionInfoProvider: SliderRulerPositionInfoProvider {
var slideRuler: SlideRuler!

func getInitialOffsetRatio() -> CGFloat {
return 0
}

func getCentralDotOriginX() -> CGFloat {
return slideRuler.pointer.frame.origin.x - slideRuler.dotWidth / 2
}

func getRulerOffsetX(with progress: CGFloat) -> CGFloat {
return progress * slideRuler.scrollRulerView.frame.width
}

func checkIsCenterPosition(with limit: CGFloat) -> Bool {
return abs(slideRuler.scrollRulerView.contentOffset.x) < limit
}

func getForceAlignCenterX() -> CGFloat {
return 0
}

func getOffsetRatio() -> CGFloat {
let offsetRatio = slideRuler.scrollRulerView.contentOffset.x / slideRuler.scrollRulerView.bounds.width
return min(1, max(0, offsetRatio))
}
}

class MiddleStyleSliderRulerPositionInfoProvider: SliderRulerPositionInfoProvider {
var slideRuler: SlideRuler!

func getInitialOffsetRatio() -> CGFloat {
return 0.5
}

func getCentralDotOriginX() -> CGFloat {
return slideRuler.frame.width - slideRuler.dotWidth / 2
}

func getRulerOffsetX(with progress: CGFloat) -> CGFloat {
return progress * slideRuler.offsetValue + slideRuler.offsetValue
}

func checkIsCenterPosition(with limit: CGFloat) -> Bool {
return abs(slideRuler.scrollRulerView.contentOffset.x - slideRuler.frame.width / 2) < limit
}

func getForceAlignCenterX() -> CGFloat {
return slideRuler.frame.width / 2
}

func getOffsetRatio() -> CGFloat {
guard slideRuler.offsetValue != 0 else {
return 0
}

var offsetRatio = (slideRuler.scrollRulerView.contentOffset.x - slideRuler.offsetValue) / slideRuler.offsetValue

if offsetRatio > 1 { offsetRatio = 1.0 }
if offsetRatio < -1 { offsetRatio = -1.0 }

return offsetRatio
}

func handleOffsetRatioWhenScrolling(_ scrollView: UIScrollView) {
if scrollView.frame.width > 0 {
slideRuler.sliderOffsetRatio = scrollView.contentOffset.x / scrollView.frame.width
}
}
}

0 comments on commit bc79bac

Please sign in to comment.