使用Pan Gesture将UIView拖到UIBezierPath上 [英] Drag UIView over UIBezierPath with Pan Gesture

查看:37
本文介绍了使用Pan Gesture将UIView拖到UIBezierPath上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用UIBezierPath创建了一个图形。基本上是一段时间内的货币换算图。因此,用户可以拖动手指以查看路径上的值。当用户拖动手指时,如何获得BezierPath的Y值来调整UIView框架。

I created a graph using UIBezierPath. It's basically currency converter graph over a period of time. So user can drag his finger to see values across the path. How can i get Y value of the BezierPath to adjust my UIView frame when user drag his finger.

我使用 apply(info:UnsafeMutableRawPointer?函数:CGPathApplierFunction)但是我不认为它们足以获得良好的结果

I get points on CGPath using apply(info: UnsafeMutableRawPointer?, function: CGPathApplierFunction) But i don't think so they will be enough to get good results

我需要的

我使用UIBezierPath实现了什么

任何指针都会有所帮助...谢谢

Any pointers would be helpful ...thanks

这是我到目前为止尝试过的

Here is what i tried so far

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)
    }
}


推荐答案

我是如何解决的..我正在写信帮助其他试图达到相同目标的人....我通过以下方式获得了我创建了那个图形并用摇动手势遍历了这些点以获得所需的结果

How i solved it.. i am writing to help others who are trying to achieve same .... i get points through which i created that graph and with pan gesture i traverse those points to get required result

这篇关于使用Pan Gesture将UIView拖到UIBezierPath上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆