Swift - 使用CGContext在屏幕上绘制手写图形

5

我正在尝试制作一个绘图应用。我有一个自定义的UIView:

class DrawView: UIView {

var touch : UITouch!
var lastPoint : CGPoint!
var currentPoint : CGPoint!

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    touch = touches.first as! UITouch
    lastPoint = touch.locationInView(self)
    println(lastPoint)
}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    touch = touches.first as! UITouch
    currentPoint = touch.locationInView(self)

    self.setNeedsDisplay()

    lastPoint = currentPoint
}

override func drawRect(rect: CGRect) {
    var context = UIGraphicsGetCurrentContext()
    CGContextSetLineWidth(context, 5)
    CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
    CGContextSetLineCap(context, kCGLineCapRound)

    CGContextBeginPath(context)

    if lastPoint != nil {
        CGContextMoveToPoint(context, lastPoint.x, lastPoint.y)
        CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y)
    }

    CGContextStrokePath(context)
}

}

当我运行它时,只有一个蓝点跟随我的手指移动,但没有线条?我做错了什么?

因为你只是将lastPoint绘制到currentPoint。当你说“设置需要显示”时,你会“使整个视图无效”,这意味着每次绘制调用都会清空视图并仅绘制该单个线段。 - Ben Zotto
3个回答

4

你好,我对你的代码进行了一些简单的修改并修复了问题,希望它能帮助将来的人(代码已更新至Swift 3):

class DrawView: UIView {

    var touch : UITouch!
    var lineArray : [[CGPoint]] = [[CGPoint]()]
    var index = -1

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        touch = touches.first! as UITouch
        let lastPoint = touch.location(in: self)

        index += 1
        lineArray.append([CGPoint]())
        lineArray[index].append(lastPoint)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        touch = touches.first! as UITouch
        let currentPoint = touch.location(in: self)

        self.setNeedsDisplay()

        lineArray[index].append(currentPoint)
    }

    override func draw(_ rect: CGRect) {

        if(index >= 0){
            let context = UIGraphicsGetCurrentContext()
            context!.setLineWidth(5)
            context!.setStrokeColor((UIColor(red:0.00, green:0.38, blue:0.83, alpha:1.0)).cgColor)
            context!.setLineCap(.round)

            var j = 0
            while( j <= index ){
                context!.beginPath()
                var i = 0
                context?.move(to: lineArray[j][0])
                while(i < lineArray[j].count){
                    context?.addLine(to: lineArray[j][i])
                    i += 1
                }
                context!.strokePath()
                j += 1
            }
        }
    }
}

1
两点:
1. 调用 `self.setNeedsDisplay` 不会立即调用 `drawRect`。它只是设置一个标志,以便在不久的将来调用 `drawRect`。由于你在那之后立即将 `lastPoint` 设置为 `currentPoint`,所以当调用 `drawRect` 时,`lastPoint` 总是等于 `currentPoint`。
2. `drawRect` 每次被调用都会重新绘制整个视图,因此你最多只能看到最近的一条线。如果你解决了问题 1,你会看到一个短线跟随着你的手指而不是一个点。如果你想看到整个轨迹,你需要将点存储在视图的属性数组中,然后在 `drawRect` 中绘制连接所有点的线。

谢谢,现在这个工作了!然而,随着行数的增加,速度变得很慢。有什么办法可以解决这个问题吗? - user2397282
1
你可以设置一个离屏上下文进行绘制,然后在 drawRect 中更新视图。此外,如果你只调用 setNeedsDisplayInRect(rect),则只有视图中的一部分需要重新绘制(即 rect 内的部分)。通过结合这两种方法,每次调用 drawRect 时只需要做一些少量的工作。 - vacawama
看看这个教程。他们正在使用UIImageView:http://www.raywenderlich.com/18840/how-to-make-a-simple-drawing-app-with-uikit - vacawama

0

marcomoreira92和Keuha的版本对我有用,但我不太喜欢使用索引。因此,这里是另一种替代版本,在Swift 4.2中进行了测试:

class DrawView: UIView {

    var lineArray: [[CGPoint]] = [[CGPoint]]()

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let firstPoint = touch.location(in: self)
        lineArray.append([CGPoint]())
        lineArray[lineArray.count - 1].append(firstPoint)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let currentPoint = touch.location(in: self)
        lineArray[lineArray.count - 1].append(currentPoint)
        setNeedsDisplay()
    }

    override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
        context?.setLineWidth(5)
        context?.setStrokeColor(UIColor.black.cgColor)
        context?.setLineCap(.round)

        for line in lineArray {
            guard let firstPoint = line.first else { continue }
            context?.beginPath()
            context?.move(to: firstPoint)
            for point in line.dropFirst() {
                context?.addLine(to: point)
            }
            context?.strokePath()
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接