Created
October 30, 2019 14:46
-
-
Save DonMag/a2154e70a3c67193a7b19bee41c8fe95 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
import UIKit | |
class CurvedSliderViewController: UIViewController { | |
var bezier: QuadBezier! | |
let pathLayer: CAShapeLayer = { | |
let layer = CAShapeLayer() | |
layer.lineWidth = 5 | |
layer.strokeColor = UIColor.blue.cgColor | |
layer.fillColor = UIColor.clear.cgColor | |
return layer | |
}() | |
// DonMag - layer to mask curve past the "thumb" | |
let maskLayer: CALayer = { | |
let layer = CALayer() | |
layer.backgroundColor = UIColor.black.cgColor | |
return layer | |
}() | |
var animation: CAKeyframeAnimation! | |
var shape: CircleView = { | |
let shape = CircleView() | |
shape.backgroundColor = .red | |
shape.frame = CGRect(origin: .zero, size: CGSize(width: 50, height: 50)) | |
return shape | |
}() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
view.layer.addSublayer(pathLayer) | |
// DonMag - set the mask for the pathLayer | |
pathLayer.mask = maskLayer | |
view.addSubview(shape) | |
} | |
override func viewDidLayoutSubviews() { | |
super.viewDidLayoutSubviews() | |
bezier = buildCurvedPath() | |
pathLayer.path = bezier.path.cgPath | |
shape.center = bezier.point(at: 0.5) | |
// DonMag - update the mask | |
updateMask(at: shape.center) | |
} | |
func buildCurvedPath() -> QuadBezier { | |
let bounds = view.bounds | |
let point1 = CGPoint(x: bounds.minX, y: bounds.midY) | |
let point2 = CGPoint(x: bounds.maxX, y: bounds.midY) | |
let controlPoint = CGPoint(x: bounds.midX, y: bounds.midY + 100) | |
let path = QuadBezier(point1: point1, point2: point2, controlPoint: controlPoint) | |
return path | |
} | |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { | |
guard let touch = touches.first else { return } | |
updatePosition(for: touch) | |
} | |
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { | |
guard let touch = touches.first else { return } | |
if let touch = event?.predictedTouches(for: touch)?.last { | |
updatePosition(for: touch) | |
} else { | |
updatePosition(for: touch) | |
} | |
} | |
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { | |
guard let touch = touches.first else { return } | |
updatePosition(for: touch) | |
} | |
func updatePosition(for touch: UITouch) { | |
let location = touch.location(in: view) | |
let t = (location.x - view.bounds.minX) / view.bounds.width | |
shape.center = bezier.point(at: t) | |
// DonMag - update mask frame when "thumb" moves | |
updateMask(at: shape.center) | |
} | |
func updateMask(at point: CGPoint) -> Void { | |
// DonMag - | |
// layer frame changes have default animations, which causes | |
// the mask to "lag behind" the position update | |
// so, disable layer animation | |
var f = view.bounds | |
f.size.width = point.x | |
CATransaction.begin() | |
CATransaction.setDisableActions(true) | |
maskLayer.frame = f | |
CATransaction.commit() | |
} | |
} | |
struct QuadBezier { | |
var point1: CGPoint | |
var point2: CGPoint | |
var controlPoint: CGPoint | |
var path: UIBezierPath { | |
let path = UIBezierPath() | |
path.move(to: point1) | |
path.addQuadCurve(to: point2, controlPoint: controlPoint) | |
return path | |
} | |
func point(at t: CGFloat) -> CGPoint { | |
let t1 = 1 - t | |
return CGPoint( | |
x: t1 * t1 * point1.x + 2 * t * t1 * controlPoint.x + t * t * point2.x, | |
y: t1 * t1 * point1.y + 2 * t * t1 * controlPoint.y + t * t * point2.y | |
) | |
} | |
} | |
@IBDesignable | |
public class CircleView: UIView { | |
override public func layoutSubviews() { | |
super.layoutSubviews() | |
layer.cornerRadius = min(bounds.width, bounds.height) / 2 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment