在半圆Swift / UIView上绘制比例

时间:2018-08-14 11:51:36

标签: ios swift uiview drawing scale

想象一下,我有一个从单位圆到0到Pi的完整半圆。左侧有一个小数字,称为min,而右侧有一个大数字,称为max。根据某些因素,应用程序内部都可以互换。 你们中的每个人对如何像我在下图中所做的那样绘制比例有个好主意吗?我想为每个x mod 10 = 0设置更长的行,并在两者之间设置三行。灰色圆圈仅用于指示方向。

所以我从下面的代码开始:

let radius = CGFloat(40)
let dashLong = CGFloat(10)
let dashShort = CGFloat(5)
let middle = CGPoint(x: 50, y: 50)
let leftAngle = CGFloat(Double.pi)
let rightAngle = CGFloat(0)
let min = 45 //random num
let max = 117 //random num



let innerPath = UIBezierPath(arcCenter: middle, radius: radius, startAngle: rightAngle, endAngle: leftAngle, clockwise: true)
let middlePath = UIBezierPath(arcCenter: middle, radius: radius+dashShort, startAngle: rightAngle, endAngle: leftAngle, clockwise: true)
let outerPath = UIBezierPath(arcCenter: middle, radius: radius+dashLong, startAngle: rightAngle, endAngle: leftAngle, clockwise: true)

因此,刻度中有两种类型的虚线的半径和长度。我选择45和117作为比例的极值的随机整数。我不需要绘制的三个路径只是一个方向,在这些位置需要开始和结束虚线。因此,对于50,60,... 110,它从innerPath开始并转到外部,我很确定对于所有圆上的破折号必须成相同的角度。

有没有人有一个非常聪明的主意,如何继续计算破折号并绘制它们而不弄乱代码?

enter image description here

2 个答案:

答案 0 :(得分:0)

我的建议是在CALayer中绘制此半圆,并在不同的CALayer中从半圆的中心绘制线,并对它们都进行遮罩,使其看起来像这样

答案 1 :(得分:0)

这是绘制刻度线的数学方法。

让我们以CGFloat的身份进行所有操作,以将转化次数降至最低:

let radius: CGFloat = 40
let dashLong: CGFloat = 10
let dashShort: CGFloat 5
let middle = CGPoint(x: 50, y: 50)
let leftAngle: CGFloat = .pi
let rightAngle: CGFloat = 0
let min: CGFloat = 45 //random num
let max: CGFloat = 117 //random num

首先,计算您的angle

let value: CGFloat = 50
let angle = (max - value)/(max - min) * .pi

现在计算您的两点:

let p1 = CGPoint(x: middle.x + cos(angle) * radius,
                 y: middle.y - sin(angle) * radius)

// use dashLong for a long tick, and dashShort for a short tick
let radius2 = radius + dashLong

let p2 = CGPoint(x: middle.x + cos(angle) * radius2,
                 y: middle.y - sin(angle) * radius2)

然后在p1p2之间划一条线。

注意:在iOS中,坐标系上下颠倒,而+ Y向下,这就是从sin中减去middle.y计算的原因。


完整示例

enum TickStyle {
    case short
    case long
}

class ScaleView: UIView {
    // ScaleView properties.  If any are changed, redraw the view
    var radius: CGFloat = 40           { didSet { self.setNeedsDisplay() } }
    var dashLong: CGFloat = 10         { didSet { self.setNeedsDisplay() } }
    var dashShort: CGFloat = 5         { didSet { self.setNeedsDisplay() } }
    var middle = CGPoint(x: 50, y: 50) { didSet { self.setNeedsDisplay() } }
    var leftAngle: CGFloat = .pi       { didSet { self.setNeedsDisplay() } }
    var rightAngle: CGFloat = 0        { didSet { self.setNeedsDisplay() } }
    var min: CGFloat = 45              { didSet { self.setNeedsDisplay() } }
    var max: CGFloat = 117             { didSet { self.setNeedsDisplay() } }

    override func draw(_ rect: CGRect) {
        let path = UIBezierPath()

        // draw the arc
        path.move(to: CGPoint(x: middle.x - radius, y: middle.y))
        path.addArc(withCenter: middle, radius: radius, startAngle: leftAngle, endAngle: rightAngle, clockwise: true)

        let startTick = ceil(min / 2.5) * 2.5
        let endTick = floor(max / 2.5) * 2.5

        // add tick marks every 2.5 units
        for value in stride(from: startTick, through: endTick, by: 2.5) {
            let style: TickStyle = value.truncatingRemainder(dividingBy: 10) == 0 ? .long : .short
            addTick(at: value, style: style, to: path)
        }

        // stroke the path
        UIColor.black.setStroke()
        path.stroke()
    }

    // add a tick mark at value with style to path
    func addTick(at value: CGFloat, style: TickStyle, to path: UIBezierPath) {
        let angle = (max - value)/(max - min) * .pi

        let p1 = CGPoint(x: middle.x + cos(angle) * radius,
                         y: middle.y - sin(angle) * radius)

        var radius2 = radius
        if style == .short {
            radius2 += dashShort
        } else if style == .long {
            radius2 += dashLong
        }

        let p2 = CGPoint(x: middle.x + cos(angle) * radius2,
                         y: middle.y - sin(angle) * radius2)

        path.move(to: p1)
        path.addLine(to: p2)
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let view = ScaleView(frame: CGRect(x: 50, y: 50, width: 100, height: 60))
        view.backgroundColor = .yellow
        self.view.addSubview(view)
    }
}

在应用中运行的秤的图片:

picture of scale running in app