随时间推移的iOS版UIBezierPath的绘图性能 [英] Drawing performance over time for a UIBezierPath with Swift for iOS

查看:63
本文介绍了随时间推移的iOS版UIBezierPath的绘图性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解Swift中不同的绘制方法以及它们为什么会如此表现。

I'm trying to understand different drawing methods in Swift and why they perform as they do.

下面的代码使用 UIBezierPath ,可从 https://github.com/limhowe/LimSignatureView

The below code draws smooth lines using a UIBezierPath, project available from https://github.com/limhowe/LimSignatureView

最初,当用户开始触摸屏幕时,以下代码的性能具有很高的响应速度。但是,随着时间的流逝,在屏幕上触摸和移动的时间越长,性能就会开始下降,并且所显示的图形也不会跟上进度并且变得不准确(有些地方似乎遗漏了)。只有在触摸屏幕结束并再次触摸屏幕以开始绘制新图形时,性能才会恢复为高度响应。

Initially the performance of the code below is highly responsive when the user begins touching the screen. However, over time, the longer the duration of touching and moving on the screen, the performance begins to lag and the drawing displayed doesn't keep up and isn't accurate (some points appear to be missed). The performance only returns to being highly responsive once touching the screen ends and touching the screen begins again for a new drawing.

我注意到的事情:


  • self.setNeedsDisplay()被注释掉(在 override func touchesMoved 中),触摸屏幕时没有显示绘图,但是一旦松开手指触发 override func touchesEnded ,最终结果是完美的绘图,无论持续多长时间,绘图点都不会滞后或不准确。 (这是理想的结果,并在绘制时显示。)

  • When self.setNeedsDisplay() is commented out (in override func touchesMoved), there is no drawing displayed during touching the screen, but once lifting the finger triggering override func touchesEnded, the final result is a perfect drawing, with no lagging or inaccurate drawing points no matter how long the duration. (This is the result ideally wanted and displayed while drawing.)

beizerPath.removeAllPoints()时被注释掉(在 override func touchesEnded 中),即使用户抬起手指并再次开始触摸屏幕,也会继续出现滞后现象。

When beizerPath.removeAllPoints() is commented out (in override func touchesEnded) lagging continues to occur even after the user lifts their finger and starts touching the screen again.

似乎 beizerPath.removeAllPoints()可能会重置滞后,而 self.setNeedsDisplay()可能会导致时间的推移。

It seems that beizerPath.removeAllPoints() might reset the lagging, while self.setNeedsDisplay() might be causing lagging over time.

beizerPath.lineWidth 从2增加到较大的宽度(例如50或100),图形会稍微滞后。

When increasing beizerPath.lineWidth to from 2 to a larger width (e.g. 50 or 100), the drawing tends to lag slightly.

嗯。我对为什么这一切都是如此感到困惑。

Hmmm. I'm confused as to why this is all is as it is.


  • 滞后到底是怎么回事时间?

  • What exactly is going on here with the lagging over time?

beizerPath.removeAllPoints()有什么关系self.setNeedsDisplay()和滞后?

为什么增加 beizerPath.lineWidth 会导致一些小的滞后?

Why does increasing beizerPath.lineWidth cause some minor lagging?

还有什么可能导致滞后?

What might else be causing lagging?

在绘制时可以删除一些要点以提高性能,如果这样,

Could removing some of the points while drawing improve performance, and if so, how can that be acheived?

需要在下面的代码中进行哪些修改,以确保完美的图形继续加班而不会出现滞后?

What needs to be modified in the below code to ensure that perfect drawing continues overtime with no lagging?

感谢所有开明的反馈和对代码的改进。谢谢。

Appreciate any enlightened feedback and improvements to the code. Thank you.

//  LimSignatureView.swift
//  SwiftSignatureView
//
//  Created by MyAdmin on 3/6/15.
//  Copyright (c) 2015 MyAdmin. All rights reserved.
//

import UIKit

class LimSignatureView: UIView {

    var beizerPath: UIBezierPath = UIBezierPath()
    var incrImage : UIImage?
    var points : [CGPoint] = Array<CGPoint>(count: 5, repeatedValue: CGPointZero)
    var control : Int = 0

    var lblSignature : UILabel = UILabel()
    var shapeLayer :   CAShapeLayer?

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect) {
        // Drawing code
        incrImage?.drawInRect(rect)
        beizerPath.stroke()

        // Set initial color for drawing    
        UIColor.redColor().setFill()
        UIColor.redColor().setStroke()
        beizerPath.stroke()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        var lblHeight: CGFloat = 61.0
        self.backgroundColor = UIColor.blackColor()
        beizerPath.lineWidth = 2.0
        lblSignature.frame = CGRectMake(0, self.frame.size.height/2 - lblHeight/2, self.frame.size.width, lblHeight);
        lblSignature.font = UIFont (name: "HelveticaNeue-UltraLight", size: 30)
        lblSignature.text = "Sign Here";
        lblSignature.textColor = UIColor.lightGrayColor()
        lblSignature.textAlignment = NSTextAlignment.Center
        lblSignature.alpha = 0.3;
        self.addSubview(lblSignature)
    }

    // MARK : - TOUCH Implementation

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        if lblSignature.superview != nil {
            lblSignature.removeFromSuperview()
        }

        control = 0;
        var touch = touches.anyObject() as UITouch
        points[0] = touch.locationInView(self)

        var startPoint = points[0];
        var endPoint = CGPointMake(startPoint.x + 1.5, startPoint.y
            + 2);

        beizerPath.moveToPoint(startPoint)
        beizerPath.addLineToPoint(endPoint)
    }

    override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {  
        var touch = touches.anyObject() as UITouch
        var touchPoint = touch.locationInView(self)
        control++;
        points[control] = touchPoint;

        if (control == 4)
        {
            points[3] = CGPointMake((points[2].x + points[4].x)/2.0, (points[2].y + points[4].y)/2.0);
            beizerPath.moveToPoint(points[0])
            beizerPath.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2])

            self.setNeedsDisplay()

            points[0] = points[3];
            points[1] = points[4];
            control = 1;
        }
    }

    override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        self.drawBitmapImage()
        self.setNeedsDisplay()

        beizerPath.removeAllPoints()
        control = 0
    }

    override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
        self.touchesEnded(touches, withEvent: event)
    }

    // MARK : LOGIC

    func drawBitmapImage() {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0);

        if incrImage != nil {
            var rectpath = UIBezierPath(rect: self.bounds)
            UIColor.clearColor().setFill()
            rectpath.fill()
        }
        incrImage?.drawAtPoint(CGPointZero)

        //Set final color for drawing
        UIColor.redColor().setStroke()
        beizerPath.stroke()

        incrImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
}


推荐答案

它变得滞后的原因有两个。

The reason that it becomes laggy is two fold.

首先,您正在通过触摸事件绘制图形。那被认为不是一个好主意。我同意这是获得最清晰的触摸运动轨迹的最佳方法,但是如果您担心性能问题,绝对不是个好主意。

First, you are doing the drawing in the touch events. That is considered not a good idea. I agree that it is the best method to get the cleanest tracing of touch movement, but definitely not a good idea if you are concerned with performance.

第二,您正在绘制每个移动事件的整个路径(从触摸到向上)。因此,即使您在到达第四个分段时都已绘制了前三个分段,您仍需要清除屏幕并再次绘制前三个分段。这与每次触摸事件发生的绘图配对会导致严重的减速。

Second, you are drawing the entire path (from touch to up) every move event. So even though you have drawn the first three segments when you get to the fourth, you clear the screen and draw the first three again. This paired with the drawing happening every touch event causes major slowdowns.

理想情况下,您会将最新的触摸事件缓存到对象中。然后创建一个计时器(可能是60 fps?),并从上一个计时器事件到当前缓存的触摸位置画一条线。这可能会导致线路没有紧随触摸事件而引起问题,但是您可能只需要对此进行试验。

Ideally you would cache the latest touch event into an object. Then create a timer (60 fps maybe?) and draw a line from the last timer event to the current cached touch location. This could cause problems with the line not following the touch event as closely, but you may have to just experiment with that.

然后使用该优化,您应该绘制一个图像上下文,然后在需要时将该上下文绘制到屏幕上。这样,您只需将最新的细分吸引到上下文中,而无需重绘整个路径。

Then with that optimization, you should draw into an image context, then draw that context to the screen when needed. That way you are only drawing the latest segment into the context instead of redrawing the entire path.

这两个方面可以极大地提高您的性能。这无疑会对跟踪触摸事件的清晰度产生不利影响,但是您应该可以在其中找到快乐的媒体。也许您缓存了所有触摸事件,然后在计时器事件上将所有最新点绘制到上下文中,并将该上下文填充到屏幕上。然后,您应该能够保持跟踪的清晰度并提高性能。

Those two things should improve your performance immensely. It will definitely have a detrimental effect to the clarity of tracing touch events, but you should be able to find a happy medium in there somewhere. Maybe you cache all touch events and then on the timer event draw all the latest points into the context and fill that context to the screen. Then you should be able to keep the clarity of tracing and improve the performance.

您还可以考虑绘制到UIImageView屏幕上的UIImage中。这样可以保留您的历史绘制路径,而无需每次重绘都重新绘制,但是我对此没有任何经验。

You could also look into drawing into a UIImage that is inside a UIImageView onscreen. That may preserve your historical drawn path without requiring you to redraw it every pass, but I don't have any experience in that.

这篇关于随时间推移的iOS版UIBezierPath的绘图性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆