如何在使用CAShapeLayer-Swift绘制的路径上标记点? [英] How to mark points on a path drawn using CAShapeLayer - Swift?

查看:88
本文介绍了如何在使用CAShapeLayer-Swift绘制的路径上标记点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个在 UIView 中使用 CAShapeLayer 绘制的自定义路径。



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

  func drawSemicircle(){

//绘制一个圆的上半部分- > 180度到0度,顺时针
让startAngle = CGFloat(M_PI)
让endAngle = CGFloat(0.0)
让centerPoint = CGPointMake(CGRectGetWidth(frame)/ 2,CGRectGetHeight(frame))

//此处设置的路径
let semirCircleLayer = CAShapeLayer()
let semirCirclePath = UIBezierPath(arcCenter:centerPoint,radius:CGRectGetWidth(frame)/ 2-20.0,startAngle:startAngle ,endAngle:endAngle,顺时针:true)
semirCircleLayer.path = semirCirclePath.CGPath

//图层自定义
semirCircleLayer.fillColor = UIColor.clearColor()。CGColor
semirCircleLayer.strokeColor = UIColor(红色:237.0 / 255.0,绿色:236.0 / 255.0,蓝色:236.0 / 255.0,alpha:1.0).CGColor
semirCircleLayer.lineWidth = 20.0
semirCircleLayer.lineCap = kCALineCapButt
layer.addSublayer(semirCircleLayer)
}

当前路径如下:





我试图实现的是在吧。就像模拟时钟刻度+数字一样,但并不那么复杂。只需在任何进度级别上标记1分即可。所以最终输出是这样的:





可以我在这方面得到了帮助吗?

解决方案

您将要使用另一个 CAShapeLayer 代表毕业行, CATextLayer 代表毕业结束时的文本。



而不是像



这里有很多代码,因为您必须做一些额外的逻辑才能计算 CATextLayer 。您可以使用 UILabel 简化此过程,因为可以使用 sizeToFit 来计算大小,但这可能会使情况复杂化



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

p>

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






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


I have a custom path drawn using CAShapeLayer in a UIView. Its a semicircle, upper half, drawn from left to right.

Following is the code used, writted in a UIView subclass:

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

Current path looks like this:

What I am tryign to implement is to mark some points on the bar .Like a analog clock graduation + diigts, but not so complex. Just mark 1 point at any progress level. So a final output, something like this :

Can I get some help on this?

解决方案

You'll want to use another CAShapeLayer for the graduation line and a CATextLayer for the text at the end of the graduation.

Instead of doing Core Graphics drawing as my answer here does, I recommend you use a 100% layer approach as to best fit in with your existing code.

Something like this should do the trick:

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

Output:

There's quite a lot of code here, as you have to do some extra logic in order to calculate the size of the CATextLayer. You could simplify this by using a UILabel as you can use sizeToFit in order to calculate the size, however this may complicate the layer hierarchy.

I've tried my best to explain each line of code - but if you still have questions, I'll be happy to answer them!

Furthermore, if you re-structure the code, you could easily allow for the graduation angle, as well as the other variables to be changed from outside the class. I've provided an example of this in the full project below.


Full Project: https://github.com/hamishknight/Dial-View

这篇关于如何在使用CAShapeLayer-Swift绘制的路径上标记点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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