ios - 在半圆上绘制比例尺 Swift/UIView

标签 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,从内部路径开始并转到外部路径,我很确定所有圆圈上的破折号必须处于相同的角度。

有谁有一个非常聪明的想法如何继续计算破折号并绘制它们而不弄乱代码?

enter image description here

最佳答案

这是绘制刻度线的数学公式。

让我们以 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

首先,计算您的角度

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 向下,这就是为什么从 middle.y 中减去 sin 计算的原因。

<小时/>

完整示例

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

关于ios - 在半圆上绘制比例尺 Swift/UIView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51840763/

相关文章:

javascript - iOS Phonegap 使用手势滑动检测

swift - CollectionViewCell 在 reloadData 函数后加载 nil 值

iphone - UIView 和 XIB 连接。正在显示吗?

ios - 如何在 iOS 中添加相对于其他 UI 元素的 UILabel

ios - 隐藏导航栏和内容之间的分隔线

ios - NSLocalizedString 的宏不起作用

c# - MonoTouch.Dialog:具有值的 StringElement,但在 UI 中隐藏值?

objective-c - UITableViews 在 Interface Builder 中使用 "Static Cells"选项布局是否可以通过代码修改?

IOS - 生成本地镜像的 MD5 时内存耗尽

swift - AppDelegates 函数 "supportedInterfaceOrientationsFor"不会在 iPad 上被调用