【问题标题】:How to mark points on a path drawn using CAShapeLayer - Swift?如何在使用 CAShapeLayer - Swift 绘制的路径上标记点?
【发布时间】:2016-06-24 21:35:23
【问题描述】:

我在 UIView 中使用 CAShapeLayer 绘制了一条自定义路径。它是一个半圆形,上半部分,从左到右绘制。

以下是使用的代码,写在UIView 子类中:

func drawSemicircle() {

        // drawing an upper half of a circle -> 180 degree to 0 degree, clockwise
        let startAngle = CGFloat(M_PI)
        let endAngle = CGFloat(0.0)
        let centerPoint = CGPointMake(CGRectGetWidth(frame)/2 , CGRectGetHeight(frame))

        // path set here
        let semirCircleLayer = CAShapeLayer()
        let semirCirclePath = UIBezierPath(arcCenter:centerPoint, radius: CGRectGetWidth(frame)/2 - 20.0 , startAngle:startAngle, endAngle:endAngle, clockwise: true)
        semirCircleLayer.path = semirCirclePath.CGPath

        // layer customisation
        semirCircleLayer.fillColor = UIColor.clearColor().CGColor
        semirCircleLayer.strokeColor = UIColor(red: 237.0/255.0, green: 236.0/255.0, blue: 236.0/255.0, alpha: 1.0).CGColor
        semirCircleLayer.lineWidth = 20.0
        semirCircleLayer.lineCap = kCALineCapButt
        layer.addSublayer(semirCircleLayer)
    }

当前路径如下所示:

我尝试实现的是在条上标记一些点。就像模拟时钟刻度+ diigts,但不是那么复杂。只需在任何进度级别标记 1 分。所以最终的输出是这样的:

我能得到一些帮助吗?

【问题讨论】:

  • 由于问题被标记为core-graphicsmy answer here 可能是一个有用的起点。如果我有时间,我会尝试对您的问题给出更具体的答案。
  • @originaluser2 感谢您的检查!是的,我已经浏览了您的帖子,但是不知何故,在我的情况下无法实现。我正在尝试保持当前的实施并为其添加毕业。

标签: ios swift uiview core-graphics cashapelayer


【解决方案1】:

您需要使用另一个 CAShapeLayer 来表示毕业线,并使用 CATextLayer 表示毕业结束时的文字。

与其像my answer here 那样进行核心图形绘制,我建议您使用 100% 分层方法以最适合您现有的代码。

这样的事情应该可以解决问题:

func drawSemicircle() {

    // drawing an upper half of a circle -> 180 degree to 0 degree, clockwise
    let startAngle = CGFloat(M_PI)
    let endAngle = CGFloat(0.0)
    let centerPoint = CGPoint(x: CGRectGetWidth(frame)*0.5 , y: CGRectGetHeight(frame))
    let radius = frame.size.width*0.5 - 80.0 // radius of your arc

    // path set here
    let semiCircleLayer = CAShapeLayer()
    semiCircleLayer.frame = bounds // requried for layer calculations
    let semiCirclePath = UIBezierPath(arcCenter:centerPoint, radius:radius, startAngle:startAngle, endAngle:endAngle, clockwise: true)
    semiCircleLayer.path = semiCirclePath.CGPath

    // layer customisation
    semiCircleLayer.fillColor = UIColor.clearColor().CGColor
    semiCircleLayer.strokeColor = UIColor(red: 237.0/255.0, green: 236.0/255.0, blue: 236.0/255.0, alpha: 1.0).CGColor
    semiCircleLayer.lineWidth = 20.0
    semiCircleLayer.lineCap = kCALineCapButt
    layer.addSublayer(semiCircleLayer)


    // draw graduation (cue the wall of code!)

    let graduationLayer = CAShapeLayer() // the graduation layer that'll display the graduation
    graduationLayer.frame = semiCircleLayer.bounds

    let graduationWidth = CGFloat(4.0) // the width of the graduation
    let graduationLength = CGFloat(50.0) // the length of the graduation
    let graduationColor = UIColor.redColor() // the color of both the graduation line and text

    let startGradRad = radius-semiCircleLayer.lineWidth*0.5 // the starting radius of the graduation
    let endGradRad = startGradRad+graduationLength // the ending radius of the graduation

    let graduationAngle = CGFloat(M_PI*0.79) // 21% along the arc from the left (0 degrees coresponds to the right hand side of the circle, with the positive angle direction going anti-clocwise (much like a unit circle in maths), so we define 79% along the arc, from the right hand side)

    // the starting point of the graduation line. the angles are negative as the arc is effectively drawn upside-down in the UIKit coordinate system.
    let startGradPoint = CGPoint(x: cos(-graduationAngle)*startGradRad+centerPoint.x, y: sin(-graduationAngle)*startGradRad+centerPoint.y)
    let endGradPoint = CGPoint(x: cos(-graduationAngle)*endGradRad+centerPoint.x, y: sin(-graduationAngle)*endGradRad+centerPoint.y)

    // the path for the graduation line
    let graduationPath = UIBezierPath()
    graduationPath.moveToPoint(startGradPoint) // start point
    graduationPath.addLineToPoint(endGradPoint) // end point

    graduationLayer.path = graduationPath.CGPath // add path to the graduation shape layer

    // configure stroking options
    graduationLayer.fillColor = UIColor.clearColor().CGColor
    graduationLayer.strokeColor = graduationColor.CGColor
    graduationLayer.lineWidth = graduationWidth

    // add to semi-circle layer
    semiCircleLayer.addSublayer(graduationLayer)

    // the font of the text to render at the end of the graduation
    let textFont = UIFont.systemFontOfSize(30)

    // the text to render at the end of the graduation - do you custom value logic here
    let str : NSString = "value"

    // default paragraph style
    let paragraphStyle = NSParagraphStyle()

    // the text attributes dictionary. used to obtain a size of the drawn text in order to calculate its frame
    let textAttributes = [NSParagraphStyleAttributeName:paragraphStyle, NSFontAttributeName:textFont]

    // size of the rendered text
    let textSize = str.sizeWithAttributes(textAttributes)

    let xOffset = abs(cos(graduationAngle))*textSize.width*0.5 // the x-offset of the text from the end of the graduation line
    let yOffset = abs(sin(graduationAngle))*textSize.height*0.5 // the y-offset of the text from the end of the graduation line

    /// the padding between the graduation line and the text
    let graduationTextPadding = CGFloat(5.0)

    // bit of pythagorus to determine how far away the center of the text lies from the end of the graduation line. multiplying the values together is cheaper than using pow. the text padding is added onto it.
    let textOffset = sqrt(xOffset*xOffset+yOffset*yOffset)+graduationTextPadding

    // the center of the text to render
    let textCenter = CGPoint(x: cos(-graduationAngle)*textOffset+endGradPoint.x, y: sin(-graduationAngle)*textOffset+endGradPoint.y)

    // the frame of the text to render
    let textRect = CGRect(x: textCenter.x-textSize.width*0.5, y: textCenter.y-textSize.height*0.5, width: textSize.width, height: textSize.height)

    let textLayer = CATextLayer()
    textLayer.contentsScale = UIScreen.mainScreen().scale // to ensure the text is rendered at the screen scale
    textLayer.frame = textRect
    textLayer.string = str
    textLayer.font = textFont
    textLayer.fontSize = textFont.pointSize // required as CATextLayer ignores the font size of the font you pass
    textLayer.foregroundColor = graduationColor.CGColor // color of text
    graduationLayer.addSublayer(textLayer)
}

输出:

这里有很多代码,因为您必须执行一些额外的逻辑才能计算CATextLayer 的大小。您可以通过使用UILabel 来简化此操作,因为您可以使用sizeToFit 来计算大小,但这可能会使层层次结构复杂化。

我已尽力解释每一行代码 - 但如果您仍有问题,我很乐意为您解答!

此外,如果您重新构造代码,您可以轻松地允许从类外部更改毕业角度以及其他变量。我在下面的完整项目中提供了一个示例。


完整项目:https://github.com/hamishknight/Dial-View

【讨论】:

  • 这对@MicRO 有帮助吗?
  • @Harnish 我已经使用您的代码实现了上述形状。我还有一个用动画更新值标记的要求。我正在从后端接收值。请帮忙,因为我挠了很长时间。我在这里发布了另一个问题:stackoverflow.com/questions/54362085/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多