2-用线段构成图形坐标转换

Posted 颐和园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2-用线段构成图形坐标转换相关的知识,希望对你有一定的参考价值。

本文通过一个自定义 checkbox 按钮的例子,演示如何使用 Core Graphics 的多线段绘制功能绘制控件的 UI 。

CheckButton

首先创建 CheckButton 类:

// 1
class CheckButton: ShadowButton {
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    // 2
    fileprivate func commonInit() {
        text = nil
    }
    // 3
    override public func selectDraw(_ frame: CGRect) {

        // 4
        super.selectDraw(frame)
        // 5
        let selectedColor = UIColor(red: 0, green: 249/255, blue: 192/255, alpha: 1)
        LinePainter.drawCheckMark(selectedColor, targetFrame: frame, shadow: outerShadow)
    }
	// 6
    override public func unselectDraw(_ frame: CGRect) {

        super.unselectDraw(frame)

        LinePainter.drawCheckMark(.white, targetFrame: frame, shadow: outerShadow)
    }
}
extension CheckButton {

    // 7
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        isSelected = !isSelected
        setNeedsDisplay()
    }
    // 8
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {}
}
 
  1. CheckButton 继承了 ShadowButton,因为它也希望拥有 ShadowButton 的内外阴影、填充背景的渐变色、圆角半径等属性。
  2. commonInit 方法中,我们将 text 属性置空,因为它不需要显示文本。
  3. drawSelect 方法,当选中状态时的绘制函数。
  4. 首先,调用继承过来的 drawSelect 方法。
  5. 然后调用 LinePainter 为我们绘制一个表示选中的绿色勾号。
  6. 覆盖 unselectDraw 方法。绘制未选中状态,跟 selectDraw 方法差不多,仅有的区别是改变了勾号的颜色为白色。
  7. touchBegan 方法,处理按钮的触摸,对于一个 checkbox 控件,让它在 select/unselect 之间切换即可。
  8. touchEnded 方法空实现,因为我们不想要释放触摸后状态复原的效果。

LinePainter

LinePainter 是我们遇到的第 3 个 Painter,它提供了多线段的绘制方法,以及为我们绘制勾号的方法。

class LinePainter {
    private let context: CGContext?

    init() {
        self.context =  UIGraphicsGetCurrentContext()
    }
    // 1.
    func polyLines(_ points: [CGPoint]) -> UIBezierPath {
        let path = UIBezierPath()

        for i in 0..<points.count {
            if i == 0 {
                path.move(to: points[0])
            }else{
                path.addLine(to: points[i])
            }
        }
        return path
    }
    
    // 2
    func drawCheckMark(_ color: UIColor, targetFrame: CGRect, shadow: NSShadow?) {
        if let context = context {

            // 3
  
            let originFrame = CGRect(x: 75, y: 22, width: 96, height: 96)
            let vertex1 = convert(CGPoint(x: 100, y: 78),originFrame: originFrame, newFrame: targetFrame)
            let vertex2 = convert(CGPoint(x: 117, y: 92), originFrame: originFrame,newFrame: targetFrame)
            let vertex3 = convert(CGPoint(x: 151, y: 48), originFrame: originFrame, newFrame: targetFrame)
            
            // 4
            let path = polyLines([vertex1, vertex2, vertex3])
            
            context.saveGState()
            // 5
            if let shadow = shadow {
                context.setShadow(offset: shadow.shadowOffset, blur: shadow.shadowBlurRadius, color: (shadow.shadowColor as! UIColor).cgColor)
            }
            // 6
            color.setStroke()
            // 7
            path.lineWidth = 7
            // 8
            path.lineJoinStyle = .round
            // 9
            path.stroke()
            context.restoreGState()

        }
    }
    
    // 10
    private func convert(_ point: CGPoint, originFrame: CGRect, newFrame: CGRect) -> CGPoint {
        let x:CGFloat = (point.x - originFrame.minX)/originFrame.width*newFrame.width + newFrame.minX
        let y:CGFloat = (point.y - originFrame.minY)/originFrame.height*newFrame.height + newFrame.minY
        return CGPoint(x:x, y: y)
    }
}

  1. polyLines 绘制多线段,参数是一个 CGPoint 数组。所谓多线段其实就是由多个点连接起来的折线而已。所以多线段的绘制本质上就是顶点(端点)的绘制。polyLines 函数返回一个 CGPath。
  2. 通过绘制多线段的方式绘制一个勾号。
  3. 一个勾号由三个顶点构成,我们分别计算了这 3 个顶点。每个顶点的计算都是调用 convert 函数得来的。
  4. 将三个顶点传入 polyLines 函数,从而计算出一个二维图形(勾号)。
  5. 如果有阴影,则在绘制时添加阴影。
  6. 设置绘制路径是颜色。
  7. 设置线宽。
  8. 设置顶点圆角。
  9. 绘制图形。
  10. convert 函数负责计算顶点坐标。设计图上的坐标需要进行一定的转换才能得到控件本身的坐标。以这里的例子来说,第一个点的坐标 75,22 是从设计图上画布上取到的坐标(你可以用 sketch)。它首先需要减去控件左上角在画布中的坐标,然后乘以按钮缩放后的比例(即控件在原图中的大小除以添加到 view 以后的实际大小),再加上控件放入 view 后 frame 的原点坐标,才能最终转换成正确的坐标。

测试

在 viewDidLoad 中:

		 let button = CheckButton(frame: CGRect(x: 100, y: 200, width: 100, height: 100))
        button.gradient = gradient()
        button.outerShadow = outerShadow()
        button.innerShadow = innerShadow()
        button.buttonRadius = 11
        
        button.backgroundColor = .white
        button.layer.cornerRadius = 20
        
        addSubview(button)

以上是关于2-用线段构成图形坐标转换的主要内容,如果未能解决你的问题,请参考以下文章

为啥用点线面建立坐标系

将微小的线段转换为弧线

计算机图形学输出图元_3_画线算法_2_DDA算法

计算机图形学输出图元_3_画线算法_2_DDA算法

将细线段转换为Arc

7-8 对称图形的面积 (25分)