当前位置: 首页 > 面试题库 >

在Swift中消除UIBezierPath平滑线中的滞后延迟

皇甫高阳
2023-03-14
问题内容

下面的代码通过覆盖触摸来绘制平滑的曲线,但是存在明显的滞后或延迟。该代码每隔4个接触点使用addCurveToPoint并调用setNeedsDisplay一次,这会导致跳动的外观,因为图形无法跟上手指的移动。为了消除延迟或察觉到的延迟,可以用addQuadCurveToPoint和临时填充触摸点1、2、3(直至触摸点4)addLineToPoint

  1. 在代码中,如何在显示最终弯曲线之前通过使用临时线和QuadCurved线来实际消除滞后现象,从而在代码中实现呢?

  2. 如果将下面的类附加到一个类UIView(例如viewOne或self)上,如何UIView在该类之后将图形副本复制到该类之外的另一个类(例如viewTwo)上touchesEnded

     //  ViewController.swift
    

    import UIKit

    class drawSmoothCurvedLinesWithLagging: UIView {

    let path=UIBezierPath()
    var incrementalImage:UIImage?
    
    var points = [CGPoint?](count: 5, repeatedValue: nil)
    
    var counter:Int?
    
    var strokeColor:UIColor?
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override func drawRect(rect: CGRect) {
        autoreleasepool {
            incrementalImage?.drawInRect(rect)
            strokeColor = UIColor.blueColor()
            strokeColor?.setStroke()
            path.lineWidth = 20
            path.lineCapStyle = CGLineCap.Round
            path.stroke()
        }
    }
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        counter = 0
    
        let touch: AnyObject? = touches.first
        points[0] = touch!.locationInView(self)
    }
    
    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch: AnyObject? = touches.first
        let point = touch!.locationInView(self)
    
        counter = counter! + 1
        points[counter!] = point
    
        if counter == 2{
            //use path.addLineToPoint ?
            //use self.setNeedsDisplay() ?
        }
    
        if counter == 3{
            //use path.addQuadCurveToPoint ?
            //use self.setNeedsDisplay() ?
        }
    
        if counter == 4{
            points[3]! = CGPointMake((points[2]!.x + points[4]!.x)/2.0, (points[2]!.y + points[4]!.y)/2.0)
            path.moveToPoint(points[0]!)
            path.addCurveToPoint(points[3]!, controlPoint1: points[1]!, controlPoint2: points[2]!)
    
            self.setNeedsDisplay()
    
            points[0]! = points[3]!
            points[1]! = points[4]!
            counter = 1
        }
    }
    
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.drawBitmap()
        self.setNeedsDisplay()
        path.removeAllPoints()
        counter = 0
    }
    
    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        self.touchesEnded(touches!, withEvent: event)
    }
    
    func drawBitmap(){
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0.0)
        strokeColor?.setStroke()
        if((incrementalImage) == nil){
            let rectPath:UIBezierPath = UIBezierPath(rect: self.bounds)
            UIColor.whiteColor().setFill()
            rectPath.fill()
        }
    
        incrementalImage?.drawAtPoint(CGPointZero)
        path.stroke()
        incrementalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
    

    }

    class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    }


问题答案:
  1. 是的,每隔几个点添加一条曲线会产生滞后的滞后。因此,是的,您可以通过向添加一条线points[1],向添加一条四边形曲线points[2]和向添加一条三次曲线来减少这种影响points[3]

正如您所说,请确保将其添加到单独的路径。因此,在Swift 3/4中:

    class SmoothCurvedLinesView: UIView {
    var strokeColor = UIColor.blue
    var lineWidth: CGFloat = 20
    var snapshotImage: UIImage?

    private var path: UIBezierPath?
    private var temporaryPath: UIBezierPath?
    private var points = [CGPoint]()

    override func draw(_ rect: CGRect) {
        snapshotImage?.draw(in: rect)

        strokeColor.setStroke()

        path?.stroke()
        temporaryPath?.stroke()
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            points = [touch.location(in: self)]
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let point = touch.location(in: self)

        points.append(point)

        updatePaths()

        setNeedsDisplay()
    }

    private func updatePaths() {
        // update main path

        while points.count > 4 {
            points[3] = CGPoint(x: (points[2].x + points[4].x)/2.0, y: (points[2].y + points[4].y)/2.0)

            if path == nil {
                path = createPathStarting(at: points[0])
            }

            path?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])

            points.removeFirst(3)

            temporaryPath = nil
        }

        // build temporary path up to last touch point

        if points.count == 2 {
            temporaryPath = createPathStarting(at: points[0])
            temporaryPath?.addLine(to: points[1])
        } else if points.count == 3 {
            temporaryPath = createPathStarting(at: points[0])
            temporaryPath?.addQuadCurve(to: points[2], controlPoint: points[1])
        } else if points.count == 4 {
            temporaryPath = createPathStarting(at: points[0])
            temporaryPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])
        }
    }

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

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

    private func finishPath() {
        constructIncrementalImage()
        path = nil
        setNeedsDisplay()
    }

    private func createPathStarting(at point: CGPoint) -> UIBezierPath {
        let localPath = UIBezierPath()

        localPath.move(to: point)

        localPath.lineWidth = lineWidth
        localPath.lineCapStyle = .round
        localPath.lineJoinStyle = .round

        return localPath
    }

    private func constructIncrementalImage() {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
        strokeColor.setStroke()
        snapshotImage?.draw(at: .zero)
        path?.stroke()
        temporaryPath?.stroke()
        snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
}

您甚至可以将此与iOS9预测性触控结合使用如我在其他答案中所述,这可以进一步减少延迟。

  1. 要获取生成的图像并将其用于其他地方,您只需抓住incrementalImage(我在snapshotImage上面将其重命名为),然后将其放入另一个视图的图像视图中即可。


 类似资料:
  • 问题内容: 在这里四处寻找了一段时间,但似乎找不到有效的解决方案。 我试图在Swift中禁用滑动以返回到上一个视图手势。 我尝试了多种解决方案,包括: 和 是否有执行此操作的新方法或其他可行的方法? 问题答案: 您可以禁用它,但是不建议这样做,因为大多数iOS用户通过滑动来回退,而通过按后退按钮可以减少回退。如果要禁用它,则使用a 代替推送转移(不是很大的转移)更为合理。如果您真的想摆脱滑动返回功

  • 问题内容: 我在x轴上有3个数据点,在y轴上有3个数据点: 我想要一条从(1,0)开始,到(3,5)到最高点,然后在(5,0)结束的曲线 我想我需要使用插值,但是不确定如何使用。如果我像这样从scipy使用样条线: 问题答案: 问题是由于没有多余的自变量是阶数为3的。这意味着您没有足够的点/等式来获得样条曲线(这本身就表示对状态不佳的矩阵的警告)。您需要应用较低阶的样条,例如三次样条,其阶数为2:

  • 问题内容: 我在延迟游戏中电脑的移动时遇到问题。 我找到了一些解决方案,但在我的情况下它们不起作用,例如 我试图将此与功能一起使用,但也没有任何效果。 还有其他可能性吗? 问题答案: 迅捷3 使用GCD: 或使用计时器: 迅捷2 使用GCD: 或使用计时器:

  • 问题内容: 在我用Swift编写的SpriteKit iOS游戏中,播放非常短的声音(约0.5秒)会产生打ic(如滞后)。在其他问题中,我读到我应该发出声音,就像以前那样。 我什至使用变量()在播放之前检查声音是否准备就绪。每当播放完()时,我也会重新准备声音。以下是代码的相关部分: 我不知道我在哪里犯了错误。我感觉我已经尝试了所有方法(包括但不限于:仅准备一次,在播放后立即准备,不使用变量,而只

  • 我想实现一个功能,使用Swift 3.0将整个表单元格扫走。比如如何在iOS10的邮件应用程序中滑动删除。 重要的是要注意,我不是指滑动以显示删除按钮,然后处理删除。我的意思是用户可以在屏幕上从右向左滑动来删除。(还要注意,tableView是静态的,而不是动态的) 我必须为此实现UISWipleTestureRecognitor吗?

  • 问题内容: 我想暂时暂停我的应用。换句话说,我希望我的应用执行代码,但是在某个时候暂停4秒钟,然后继续执行其余的代码。我怎样才能做到这一点? 我正在使用Swift。 问题答案: 如果要从UI线程调用该方法,则可以考虑使用或调度计时器,而不是进行睡眠(这会锁定您的程序)。 但是,如果您确实需要延迟当前线程: 这使用UNIX中的功能。