绘制 1 像素的线

2019-02-20  本文已影响0人  foolish_hungry

1.效果图

image.png

2. 计算线宽

由于在不同的设备下, 1个点对应的像素数不一样。

线宽

let lineWidth = 1 / UIScreen.main.scale

3. 计算偏移量

如果直接使用上面计算出的线宽, 线条模糊且宽度不止1像素(由于自动裁剪, 其实只显示了一半)。


image.png

这是由于系统的反锯齿(antialiasing)技术造成的。

反锯齿技术:
奇数像素宽度的线在渲染的时候将会表现为柔和的宽度扩展到向上的整数宽度的线,除非你手动的调整线的位置,使线刚好落在一行或列的显示单元内。

也就是说,如果要画一条黑线,线条刚好落在了一列或者一行显示显示单元之内,将会渲染出标准的一个像素的黑线。 但如果线落在了两个行或列的中间时,那么会得到一条“失真”的线,其实是两个像素宽的灰线。


image.png

所以对于偶数像素宽度的线我们就不需要偏移了(否则会失真)。只需要对宽度为奇数像素的线条做个位置偏移,使其刚好落在显示单元中。具体偏移量如下:

//线偏移量
let lineAdjustOffset = 1 / UIScreen.main.scale / 2

4.完整代码

import UIKit
 
class ViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        let frame = CGRect(x: 30, y: 30, width: 180, height: 100)
        let cgView = CGView(frame: frame)
        self.view.addSubview(cgView)
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
 
class CGView:UIView {
     
    //线宽
    let lineWidth = 1 / UIScreen.main.scale
     
    //线偏移量
    let lineAdjustOffset = 1 / UIScreen.main.scale / 2
     
    override init(frame: CGRect) {
        super.init(frame: frame)
        //设置背景色为透明,否则是黑色背景
        self.backgroundColor = UIColor.clear
    }
     
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
     
    override func draw(_ rect: CGRect) {
        super.draw(rect)
         
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else {
            return
        }
         
        //创建一个矩形,它的所有边都内缩固定的偏移量
        let drawingRect = self.bounds.insetBy(dx: lineAdjustOffset, dy: lineAdjustOffset)
         
        //创建并设置路径
        let path = CGMutablePath()
        //外边框
        path.addRect(drawingRect)
        //横向分隔线(中点同样要计算偏移量)
        let midY = CGFloat(Int(self.bounds.midY)) + lineAdjustOffset
        path.move(to: CGPoint(x: drawingRect.minX, y: midY))
        path.addLine(to: CGPoint(x: drawingRect.maxX, y: midY))
        //纵向分隔线(中点同样要计算偏移量)
        let midX = CGFloat(Int(self.bounds.midX)) + lineAdjustOffset
        path.move(to: CGPoint(x: midX, y: drawingRect.minY))
        path.addLine(to: CGPoint(x: midX, y: drawingRect.maxY))
         
        //添加路径到图形上下文
        context.addPath(path)
         
        //设置笔触颜色
        context.setStrokeColor(UIColor.black.cgColor)
        //设置笔触宽度
        context.setLineWidth(lineWidth)
 
        //绘制路径
        context.strokePath()
    }
}

转载航哥

上一篇下一篇

猜你喜欢

热点阅读