Last active
June 28, 2022 12:07
-
-
Save robertmryan/67484c74297cede3926a3aed2fceedb9 to your computer and use it in GitHub Desktop.
This file contains 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
class ViewController: 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 | |
}() | |
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) | |
view.addSubview(shape) | |
} | |
override func viewDidLayoutSubviews() { | |
super.viewDidLayoutSubviews() | |
bezier = buildCurvedPath() | |
pathLayer.path = bezier.path.cgPath | |
shape.center = bezier.point(at: 0.5) | |
} | |
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) | |
} | |
} | |
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