Swift 绘图应用程序滞后

Swift Drawing App is Laggy

我目前正在开发一个绘图应用程序。我正在为 iOS 构建它,并且正在使用 Swift 3 来这样做。

这只是一个基本的绘图应用程序,但我正在尝试添加一个额外的功能。我从 UIScrollView 开始,然后向该滚动视图添加了一个图像视图。所有绘图部分都是通过图像视图完成的。当您第一次启动该应用程序时,滚动视图会完全放大。当您切换到 "zoom mode" 时,您可以通过捏合来缩放。问题是,当你第一次打开应用程序时,当你放大绘图时,绘图真的很模糊。为了解决这个问题,我可以使用这样一行代码:

UIGraphicsBeginImageContextWithOptions((self.view.frame.size), false, 7.0)

这会导致绘图在放大时看起来很棒,但会导致应用 运行 非常滞后。令我困惑的是,如果我将上面的代码更改为:

UIGraphicsBeginImageContextWithOptions((self.view.frame.size), false, 0.0)

然后一直缩小,绘图看起来完全一样(当然,我一直缩小)但它不再滞后。我知道这可能不是很清楚,所以这里有一段视频显示了第一种情况下发生的情况:https://youtu.be/E_9FKf1pUTY and in the second: https://youtu.be/OofFTS4Q0OA

所以基本上,我想知道是否有办法将放大的区域视为它自己的视图。在我看来,应用程序似乎正在更新整个图像视图,而不仅仅是在任何给定时间可见的部分。有没有办法只更新绘制的图像视图部分?抱歉,如果这有点令人困惑 post,如果有任何您不明白的地方,请随时提问。为了清楚起见,我将在下面包含所有绘图代码:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    print("Touches began")

    swiped = true

    if let touch = touches.first {
        lastPoint = touch.location(in: scrollView)
        lastPoint.x = lastPoint.x / scrollView.zoomScale
        lastPoint.y = lastPoint.y / scrollView.zoomScale
    }
}

func drawLines(fromPoint:CGPoint,toPoint:CGPoint) {
    print("\(fromPoint.x), \(fromPoint.y)")
    //UIGraphicsBeginImageContext(self.view.frame.size)
    UIGraphicsBeginImageContextWithOptions((scrollView.frame.size), false, 0.0)
    imageView.image?.draw(in: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
    let context = UIGraphicsGetCurrentContext()
    context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
    context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))

    context?.setBlendMode(CGBlendMode.normal)
    context?.setLineCap(CGLineCap.round)
    if erase == true {
        context?.setLineWidth(30)
    }
    if erase == false {
        context?.setLineWidth(CGFloat(sizeVar))
    }
    if color == "black" {
        context?.setStrokeColor(UIColor.black.cgColor)
    }
    if color == "white" {
        context?.setStrokeColor(UIColor.white.cgColor)
    }
    if color == "blue" {
        context?.setStrokeColor(UIColor.blue.cgColor)
    }
    if color == "cyan" {
        context?.setStrokeColor(UIColor.cyan.cgColor)
    }
    if color == "green" {
        context?.setStrokeColor(UIColor.green.cgColor)
    }
    if color == "magenta" {
        context?.setStrokeColor(UIColor.magenta.cgColor)
    }
    if color == "red" {
        context?.setStrokeColor(UIColor.red.cgColor)
    }
    if color == "yellow" {
        context?.setStrokeColor(UIColor.yellow.cgColor)
    }

    context?.strokePath()

    imageView.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    swiped = true

    if let touch = touches.first {
        var currentPoint = touch.location(in: scrollView)
        currentPoint.x = currentPoint.x / scrollView.zoomScale
        currentPoint.y = currentPoint.y / scrollView.zoomScale
        drawLines(fromPoint: lastPoint, toPoint: currentPoint)

        lastPoint = currentPoint
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if !swiped {
        drawLines(fromPoint: lastPoint, toPoint: lastPoint)
    }
}

函数UIGraphicsBeginImageContextWithOptions(_:_:_:)中的scale参数不是质量设置,你不应该在那里放任意值。这是一个比例因子,类似于图像的@1x、@2x 和@3x 设置。它告诉系统在将图像像素映射到屏幕像素时使用的比例因子。在大多数(几乎所有)情况下,您应该使用 0,这意味着 "use the native scale of the screen"(@2x 用于正常视网膜,或 @3x 用于 iPhone 6+ 和 7+。)您永远不应将其设置为任意值像 7 这样的值。这会创建一个像素为正常像素 7 倍的图像,并强制系统每次都缩放它以进行屏幕绘制,这会花费更多时间并且速度更慢。

接下来,为每一行创建一个新图像是一种非常低效的绘图方式。它不断地创建和释放大块内存,然后每次都必须完全重绘屏幕。相反,我会设置一个具有 CAShapeLayer 作为支持层的视图,并更新安装在该层中的路径。