代码之家  ›  专栏  ›  技术社区  ›  blue

swift:让自定义进度视图跟随用户点击?乌比齐尔

  •  1
  • blue  · 技术社区  · 6 年前

    好的,我在这里实现了一个圆形的自定义uiprogressview:

    class PlaybackLineView: UIView {
        var percentage: Float = 0.0 {
            didSet {
                if percentage > 1 {
                    //print("happned!")
                    percentage = 1.0
                }
    
                DispatchQueue.main.async {
                    self.setNeedsDisplay()
                }
            }
        }
    
        override func draw(_ rect: CGRect) {
            let playbackRed = UIColor(red: 181/255.0, green: 23/255.0, blue: 65/255.0, alpha: 1.0)
            playbackRed.setFill()
            playbackRed.setStroke()
    
            let newWidth = rect.size.width * CGFloat(percentage)
            let fillRect = CGRect(x: 0, y: 0, width: newWidth, height: rect.size.height)
            let fillRectPath = UIBezierPath(rect: fillRect)
            fillRectPath.fill()
        }
    
    @IBInspectable public var drawEndPoint: Bool = true
        @IBInspectable public var drawWhenZero: Bool = false
        @IBInspectable public var strokeWidth: CGFloat = 3.0
    
        let smallCircleRadius: CGFloat = 6.0
        var fullCirclePath: UIBezierPath!
    
    
        override func draw(_ rect: CGRect) {
            if !drawWhenZero && (percentage == 0 || percentage == 1) { return }
    
            UIColor.clear.setFill()
    
            let arcCenter = CGPoint(x: rect.midX, y: rect.midY)
            let radius = (rect.width - smallCircleRadius * 2) / 2.0
    
            let circleGray = UIColor(red: 218/255.0, green: 218/255.0, blue: 218/255.0, alpha: 1.0)
            circleGray.setStroke()
            fullCirclePath = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0.0, endAngle:CGFloat.pi * 2, clockwise: true)
            fullCirclePath.lineWidth = strokeWidth
            fullCirclePath.fill()
            fullCirclePath.stroke()
    
            let circleRed = UIColor(red: 181/255.0, green: 23/255.0, blue: 65/255.0, alpha: 1.0)
            circleRed.setStroke()
            let arcPath = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: 0.0, endAngle:CGFloat.pi * 2 * CGFloat(percentage), clockwise: true)
            arcPath.fill()
            arcPath.stroke()
            arcPath.lineWidth = strokeWidth
    
            if !drawEndPoint { return }
    
            let smallCirclePath = UIBezierPath(arcCenter: arcPath.currentPoint, radius: smallCircleRadius, startAngle: 0.0, endAngle:CGFloat.pi * 2, clockwise: true)
            circleRed.setFill()
            smallCirclePath.fill()
            smallCirclePath.stroke()
    
            self.transform = CGAffineTransform(rotationAngle: (-90).degreesToRadians)
        }
    

    这将按照 percentage . 问题是我需要允许用户通过在uibezierpath上点击/清除来控制这个百分比-

    到目前为止,我得到的结果是水龙头是否在更大的圆圈内:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            if let touch = touches.first {
                let currentPoint = touch.location(in: self)
                // do something with your currentPoint
    
                if(fullCirclePath.contains(currentPoint))
                {
                    print("CONTAINS!: ", currentPoint)
                    //percentage = 0.5
                }
            }
        }
    

    如何从这个cgpoint中得到水龙头沿着路径的大圆圈的百分比?

    我试过的:

    重写func touchesStarted(u touches:set,with event:uievent?){ 如果让触摸=触摸。首先{ 设currentpoint=touch.location(in:self) //使用当前点

        //print(fullCirclePath.cgPath.cen)
        if(fullCirclePath.contains(currentPoint))
        {
    
            touchAngle = atan2((currentPoint.x-arcCenter.x), (currentPoint.y-arcCenter.y))
            percentage = Float(touchAngle * 100)/Float(M_PI * 2)
            print("CONTAINS!: ", currentPoint, "Angle: ", touchAngle.radiansToDegrees, " Percent: ", percentage)
    
            if(AudioPlayerManager.shared.isPlaying())
            {
                AudioPlayerManager.shared.seek(toProgress: percentage)
            }
        }
    }
    

    }

    1 回复  |  直到 6 年前
        1
  •  1
  •   an23lm    6 年前

    抓紧,这涉及到一些数学!

    假设圆的中心是(0,0)。假设你有一个点(x,y)位于圆周上,你可以使用 atan2 or arctan2 function 得到角度(弧度; 弧度是你想要的 UIBezierPath 使用弧度 )点位于圆的中心。所以,有了这个角度,你就知道用户在你的圆周长上触到了什么地方(称之为 touch angle )你可以用这个作为你的 endAngle 小精灵 .

    注: 如果圆的中心不是(0,0),而是(x1,y1),则通过减去接触点(x,y)的差来补偿,即,新点是(x-x1,y-y1)

    Angles in the default coordinate system

    如果你想要一个0 rads的周长的百分比。你可以用公式 percentage = (touch angle*(in rads)* * 100)/2π) .

    关于atan2/arctan2的更多信息

    你可以用公式计算你的触角 touch angle = tan^(-1)(y/x) 阿卡 touch angle = arctan2(y,x) . 在哪里? (x,y) 是用户在圆周上接触的点。

    举例说明 (编辑)

    可以 ,经过一番研究,我想出来了。 拿一个 look at this code 注意 touchesBegan(_ ... 功能。

    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        for touch in touches {
            let point = touch.location(in: self)
            if backCircleBezierPath!.contains(point) {
                print("contains")
                let centeredPoint = CGPoint(x: (point.x - frame.width/2), y: (point.y - frame.height/2))
                print(centeredPoint)
                var rads = atan(centeredPoint.y/centeredPoint.x)
                if centeredPoint.x < 0 {
                    rads += CGFloat.pi
                } else if centeredPoint.x > 0 && centeredPoint.y < 0 {
                    rads += CGFloat.pi * 2
                }
                let perc = (rads - backCircleStartAngle) / abs(backCircleStartAngle - backCircleEndAngle)
                frontCircleShapeLayer?.strokeEnd = perc
            }
        }
    }
    

    我已经用过 CAShapeLayer 而不是拉进来 draw(_ rect... ;我建议你也这样做。你的方法也很好,但有一些变化,我将在最后提到。

    我用了两个弧: 1。占位符弧- backCircleShapeLayer 2。进度指示弧- frontCircleShapeLayer

    当用户点击占位符弧时,我们可以计算角度(以弧度为单位)并从 start Angle touch Angle 如前所述( 使用此方法直接绘制笔划 小精灵 draw(_ ... 方法 ,但我要做的是用这些值来计算分数 perc (介于0和1之间)并将其用作 stroke end 进步的弧线。

    这带来了一些有趣的新知识。价值 atan(y/x) 是特定于域的( Some math behind this, if you're interested )所以 如果您的触摸在第四象限,则添加360_或2_,如果在第二象限或第三象限,则添加180_或2_。 . 在这个添加之后 触角 应该体现出完美的价值。