Swift 使用平移手势将UIView拖动到UIBezierPath上
我使用UIBezierPath创建了一个图形。它基本上是一段时间内的货币换算图。所以用户可以拖动手指查看路径上的值。当用户拖动手指时,如何获得BezierPath的Y值以调整UIView帧 我使用Swift 使用平移手势将UIView拖动到UIBezierPath上,swift,graph,calayer,uibezierpath,cashapelayer,Swift,Graph,Calayer,Uibezierpath,Cashapelayer,我使用UIBezierPath创建了一个图形。它基本上是一段时间内的货币换算图。所以用户可以拖动手指查看路径上的值。当用户拖动手指时,如何获得BezierPath的Y值以调整UIView帧 我使用apply(info:UnsafeMutableRawPointer?,function:cgpathappierfunction)在CGPath上获得分数,但我认为这些分数不足以获得好的结果 我需要什么 我使用UIBezierPath实现的功能 任何指点都会有帮助的…谢谢 这是我到目前为止
apply(info:UnsafeMutableRawPointer?,function:cgpathappierfunction)
在CGPath上获得分数,但我认为这些分数不足以获得好的结果
我需要什么
我使用UIBezierPath实现的功能
任何指点都会有帮助的…谢谢 这是我到目前为止试过的
类图形视图:UIView{
@IBInspectable var startColor:UIColor=.red
@IBInspectable变量endColor:UIColor=.green
风险值数据:[CGFloat]=[2,3,2.9,2.8,6,5,7,1,3,2,8,8.2,9,5,6,4,10,5,4,2,5,7,12,10,11,10.5,11,13,10]{
迪塞特{
setNeedsDisplay()
}
}
func coordYFor(索引:Int)->CGFloat{
返回bounds.height-bounds.height*数据[索引]/(data.max()??0)
}
重写函数绘图(rect:CGRect){
let path=quadCurvedPath()
var bezierPoints=NSMutableArray()
apply(信息:&bezierPoints,函数:{info,元素在
guard let resultingPoints=info?.assumingMemoryBound(到:NSMutableArray.self)else{
返回
}
设点=element.pointee.points
let type=element.pointee.type
开关类型{
case.moveToPoint:
resultingPoints.pointee.add([NSNumber(值:Float(点[0].x]),NSNumber(值:Float(点[0].y]))
case.addLineToPoint:
resultingPoints.pointee.add([NSNumber(值:Float(点[0].x]),NSNumber(值:Float(点[0].y]))
case.addQuadCurveToPoint:
resultingPoints.pointee.add([NSNumber(值:Float(点[0].x]),NSNumber(值:Float(点[0].y]))
resultingPoints.pointee.add([NSNumber(值:Float(点[1].x]),NSNumber(值:Float(点[1].y]))
case.addCurveToPoint:
resultingPoints.pointee.add([NSNumber(值:Float(点[0].x]),NSNumber(值:Float(点[0].y]))
resultingPoints.pointee.add([NSNumber(值:Float(点[1].x]),NSNumber(值:Float(点[1].y]))
resultingPoints.pointee.add([NSNumber(值:Float(点[2].x]),NSNumber(值:Float(点[2].y]))
案例.关闭子路径:
打破
@未知默认值:
打破
}
})
打印(bezierPoints)
设shapeLayer=CAShapeLayer()
shapeLayer.strokeColor=#colorLiteral(红色:0.3669792414,绿色:0.2084159851,蓝色:0.6959738135,alpha:1)
shapeLayer.lineWidth=3
shapeLayer.lineDashPattern=[4,5]
shapeLayer.path=path.cgPath
shapeLayer.fillColor=UIColor.clear.cgColor
layer.addSublayer(shapeLayer)
guard let clippingPath=path.copy()作为?UIBezierPath else{
返回
}
let height=rect.height
clippingPath.addLine(到:CGPoint)(
x:bounds.width,
y:高度)
clippingPath.addLine(到:CGPoint(x:0,y:height))
clippingPath.close()
clippingPath.addClip()文件
let shape=CAShapeLayer()
shape.frame=bounds
shape.path=clippingPath.cgPath
shape.fillColor=UIColor.blue.cgColor
设梯度=CAGradientLayer()
gradient.frame=边界
gradient.colors=[#colorLiteral(红色:0.9705377221,绿色:0.9600282311,蓝色:0.9863556027,alpha:1),
UIColor.white.cgColor]
//gradient.startPoint=CGPoint(x:1,y:0.5)
//gradient.endPoint=CGPoint(x:1,y:0.5)
梯度位置=[0.5,1.0]
gradient.mask=形状
图层。添加子图层(渐变)
//5-检查剪辑路径-临时代码
//UIColor.green.setFill()
//设rectPath=UIBezierPath(rect:rect)
//rectPath.fill()
//layer.lineDashPattern=[2,3]
//UIColor.blue.setStroke()
//path.lineWidth=1
//path.stroke()
}
func quadCurvedPath()->UIBezierPath{
let path=UIBezierPath()
让步长=bounds.width/CGFloat(data.count-1)
var p1=CGPoint(x:0,y:coordYFor(指数:0))
路径.移动(到:p1)
绘图点(点:p1,颜色:UIColor.red,半径:3)
如果(data.count==2){
addLine(to:CGPoint(x:step,y:coordYFor(index:1)))
返回路径
}
变量oldControlP:CGPoint?
对于我来说,1..CGPoint{
防护装置让p1=点,让中心=其他中心{
归零
}
设newX=2*center.x-p1.x
设diffY=abs(p1.y-center.y)
设newY=center.y+diffY*(p1.yCGPoint{
返回点(x:(p1.x+p2.x)/2,y:(p1.y+p2.y)/2);
}
///查找addCurve的控制点2
///-参数:
///-p1:曲线的第一点
///-p2:我们正在寻找其控制点的曲线第二点
///-下一个:预测下一个点,该点将使用反足控制点进行查找
func控制点FORPOINTS(p1:CGPoint,p2:CGPoint,下一个p3:CGPoint?->CGPoint{
防护罩让p3=p3其他{
归零
}
设LeftMiddpoint=中点(p1:p1,p2:p2)
设右中点=m
class GraphView: UIView {
@IBInspectable var startColor: UIColor = .red
@IBInspectable var endColor: UIColor = .green
var data: [CGFloat] = [2, 3 , 2.9 ,2.8,6,5,7,1,3,2,8,8.2,9,5,6,4, 10,5, 4,2,5,7,12 ,10,11,10.5,11, 13,10] {
didSet {
setNeedsDisplay()
}
}
func coordYFor(index: Int) -> CGFloat {
return bounds.height - bounds.height * data[index] / (data.max() ?? 0)
}
override func draw(_ rect: CGRect) {
let path = quadCurvedPath()
var bezierPoints = NSMutableArray()
path.cgPath.apply(info: &bezierPoints, function: { info, element in
guard let resultingPoints = info?.assumingMemoryBound(to: NSMutableArray.self) else {
return
}
let points = element.pointee.points
let type = element.pointee.type
switch type {
case .moveToPoint:
resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])
case .addLineToPoint:
resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])
case .addQuadCurveToPoint:
resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])
resultingPoints.pointee.add([NSNumber(value: Float(points[1].x)), NSNumber(value: Float(points[1].y))])
case .addCurveToPoint:
resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])
resultingPoints.pointee.add([NSNumber(value: Float(points[1].x)), NSNumber(value: Float(points[1].y))])
resultingPoints.pointee.add([NSNumber(value: Float(points[2].x)), NSNumber(value: Float(points[2].y))])
case .closeSubpath:
break
@unknown default:
break
}
})
print(bezierPoints)
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = #colorLiteral(red: 0.3669792414, green: 0.2084159851, blue: 0.6959738135, alpha: 1)
shapeLayer.lineWidth = 3
shapeLayer.lineDashPattern = [4,5]
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
layer.addSublayer(shapeLayer)
guard let clippingPath = path.copy() as? UIBezierPath else {
return
}
let height = rect.height
clippingPath.addLine(to: CGPoint(
x: bounds.width ,
y: height))
clippingPath.addLine(to: CGPoint(x: 0 , y: height))
clippingPath.close()
clippingPath.addClip()
let shape = CAShapeLayer()
shape.frame = bounds
shape.path = clippingPath.cgPath
shape.fillColor = UIColor.blue.cgColor
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [#colorLiteral(red: 0.9705377221, green: 0.9600282311, blue: 0.9863556027, alpha: 1).cgColor,
UIColor.white.cgColor]
// gradient.startPoint = CGPoint(x: 1, y: 0.5)
// gradient.endPoint = CGPoint(x: 1, y: 0.5)
gradient.locations = [0.5,1.0]
gradient.mask = shape
layer.addSublayer(gradient)
// 5 - Check clipping path - Temporary code
// UIColor.green.setFill()
// let rectPath = UIBezierPath(rect: rect)
// rectPath.fill()
//layer.lineDashPattern = [2,3]
// UIColor.blue.setStroke()
// path.lineWidth = 1
// path.stroke()
}
func quadCurvedPath() -> UIBezierPath {
let path = UIBezierPath()
let step = bounds.width / CGFloat(data.count - 1)
var p1 = CGPoint(x: 0, y: coordYFor(index: 0))
path.move(to: p1)
drawPoint(point: p1, color: UIColor.red, radius: 3)
if (data.count == 2) {
path.addLine(to: CGPoint(x: step, y: coordYFor(index: 1)))
return path
}
var oldControlP: CGPoint?
for i in 1..<data.count {
let p2 = CGPoint(x: step * CGFloat(i), y: coordYFor(index: i))
drawPoint(point: p2, color: UIColor.red, radius: 3)
var p3: CGPoint?
if i < data.count - 1 {
p3 = CGPoint(x: step * CGFloat(i + 1), y: coordYFor(index: i + 1))
}
let newControlP = controlPointForPoints(p1: p1, p2: p2, next: p3)
path.addCurve(to: p2, controlPoint1: oldControlP ?? p1, controlPoint2: newControlP ?? p2)
p1 = p2
oldControlP = antipodalFor(point: newControlP, center: p2)
}
return path;
}
/// located on the opposite side from the center point
func antipodalFor(point: CGPoint?, center: CGPoint?) -> CGPoint? {
guard let p1 = point, let center = center else {
return nil
}
let newX = 2 * center.x - p1.x
let diffY = abs(p1.y - center.y)
let newY = center.y + diffY * (p1.y < center.y ? 1 : -1)
return CGPoint(x: newX, y: newY)
}
/// halfway of two points
func midPointForPoints(p1: CGPoint, p2: CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2);
}
/// Find controlPoint2 for addCurve
/// - Parameters:
/// - p1: first point of curve
/// - p2: second point of curve whose control point we are looking for
/// - next: predicted next point which will use antipodal control point for finded
func controlPointForPoints(p1: CGPoint, p2: CGPoint, next p3: CGPoint?) -> CGPoint? {
guard let p3 = p3 else {
return nil
}
let leftMidPoint = midPointForPoints(p1: p1, p2: p2)
let rightMidPoint = midPointForPoints(p1: p2, p2: p3)
var controlPoint = midPointForPoints(p1: leftMidPoint, p2: antipodalFor(point: rightMidPoint, center: p2)!)
if p1.y.between(a: p2.y, b: controlPoint.y) {
controlPoint.y = p1.y
} else if p2.y.between(a: p1.y, b: controlPoint.y) {
controlPoint.y = p2.y
}
let imaginContol = antipodalFor(point: controlPoint, center: p2)!
if p2.y.between(a: p3.y, b: imaginContol.y) {
controlPoint.y = p2.y
}
if p3.y.between(a: p2.y, b: imaginContol.y) {
let diffY = abs(p2.y - p3.y)
controlPoint.y = p2.y + diffY * (p3.y < p2.y ? 1 : -1)
}
// make lines easier
controlPoint.x += (p2.x - p1.x) * 0.4
return controlPoint
}
func drawPoint(point: CGPoint, color: UIColor, radius: CGFloat) {
let ovalPath = UIBezierPath(ovalIn: CGRect(x: point.x - radius, y: point.y - radius, width: radius * 2, height: radius * 2))
color.setFill()
ovalPath.fill()
}
}
extension CGFloat {
func between(a: CGFloat, b: CGFloat) -> Bool {
return self >= Swift.min(a, b) && self <= Swift.max(a, b)
}
}