消除在 Swift 中连续绘制 UIBezierPath 期间的滞后延迟

2024-04-19

下面的代码通过覆盖触摸来绘制线条,但是在连续不间断绘制的一段时间内开始出现滞后。手指在屏幕上移动的时间越长,这种滞后就会累积并变得更严重。结果是实际设备上的 CPU 几乎达到极限(CPU 98%+),并且绘图持续时间越长,生成的图像看起来就越不稳定。

此外,当绘制速度超快时,尤其是在圆形中,绘制的路径之间存在差异。path and temporaryPath (or localPath)。尽管它们是在不同时间绘制的,但它们似乎同时出现在屏幕上,这在视觉上分散了两条路径绘制的速度。内部路径(path) 似乎与外部路径 (temporaryPath)在下图中以红色突出显示。

1 - 如何消除连续绘制一段时间内的滞后延迟?

2 - 如何消除所绘制路径的差异?

3 - alpha/不透明度如何path and temporaryPath被改变?

class swiftView: UIView {

var strokeColor = UIColor.blueColor()
var lineWidth: CGFloat = 5
var snapshotImage: UIImage?

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

var counterPoints:Int?

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

override func drawRect(rect: CGRect) {
    autoreleasepool {

    snapshotImage?.drawInRect(rect)

    strokeColor.setStroke()

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

    }
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch: AnyObject? = touches.first
    points = [touch!.locationInView(self)]

    counterPoints = 0
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch: AnyObject? = touches.first
    let point = touch!.locationInView(self)

    points.append(point)
    let pointCount = points.count

    counterPoints = counterPoints! + 1

    if pointCount == 2 {
        temporaryPath = createPathStartingAtPoint(points[0])
        temporaryPath?.addLineToPoint(points[1])
        setNeedsDisplay()
    } else if pointCount == 3 {
        temporaryPath = createPathStartingAtPoint(points[0])
        temporaryPath?.addQuadCurveToPoint(points[2], controlPoint: points[1])
        setNeedsDisplay()
    } else if pointCount == 4 {
        temporaryPath = createPathStartingAtPoint(points[0])
        temporaryPath?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2])
//            setNeedsDisplay()

        if counterPoints! < 50 {
            self.setNeedsDisplay()
        } else {
            temporaryPath = nil
            self.constructIncrementalImage()
            path = nil
            self.setNeedsDisplay()
            counterPoints = 0
        }

    } else if pointCount == 5 {
        points[3] = CGPointMake((points[2].x + points[4].x)/2.0, (points[2].y + points[4].y)/2.0)

        // create a quad bezier up to point 4, too

        if points[4] != points[3] {
            let length = hypot(points[4].x - points[3].x, points[4].y - points[3].y) / 2.0
            let angle = atan2(points[3].y - points[2].y, points[4].x - points[3].x)
            let controlPoint = CGPoint(x: points[3].x + cos(angle) * length, y: points[3].y + sin(angle) * length)

            temporaryPath = createPathStartingAtPoint(points[3])
            temporaryPath?.addQuadCurveToPoint(points[4], controlPoint: controlPoint)
        } else {
            temporaryPath = nil
        }

        if path == nil {
            path = createPathStartingAtPoint(points[0])
        }

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

        self.setNeedsDisplay()

        points = [points[3], points[4]]
    }
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.constructIncrementalImage()
    path = nil
    self.setNeedsDisplay()

    counterPoints = 0
}

override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    self.touchesEnded(touches!, withEvent: event)
}

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

    localPath.moveToPoint(point)

    localPath.lineWidth = lineWidth
    localPath.lineCapStyle = .Round
    localPath.lineJoinStyle = .Round

    return localPath
}

private func constructIncrementalImage() {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0)
    strokeColor.setStroke()
    snapshotImage?.drawAtPoint(CGPointZero)
    path?.stroke()
    temporaryPath?.stroke()
    snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
}

}

你问:

  1. 如何消除连续绘制一段时间的滞后现象?

正如您正确推测的那样,是的,执行快照并重置路径可以通过限制路径的长度来解决此问题。

我知道您已经意识到这一点,但为了其他读者的利益,在 iOS 9 中您也可以使用预测触摸。在这个特定的算法中(其中(a)您只是添加到路径,但是(b)每四个点根据下一个点进行调整,以确保两条三次贝塞尔曲线连接处没有不连续性)有点棘手,但可以做到。

  1. 如何消除所绘制路径的差异?

这是因为快照包含临时路径而导致的。但该临时路径的全部目的是,随着更多点的进入,它将被丢弃。因此,您不应将其包含在手势中创建的快照中。

因此,我建议向快照函数添加一个参数,指示是否temporaryPath应包含或不包含。当调用它的中间手势时,您需要指定includeTemporaryPath as false,但是当在手势结束时调用它时,includeTemporaryPath将会true.

例如:

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

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

    override func drawRect(rect: CGRect) {
        snapshotImage?.drawInRect(rect)

        strokeColor.setStroke()

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

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch: AnyObject? = touches.first
        points = [touch!.locationInView(self)]
        totalPointCount = totalPointCount + 1
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch: AnyObject? = touches.first
        let point = touch!.locationInView(self)

        points.append(point)
        totalPointCount = totalPointCount + 1

        updatePaths()

        if totalPointCount > 50 {
            constructIncrementalImage(includeTemporaryPath: false)
            path = nil
            totalPointCount = 0
        }

        setNeedsDisplay()
    }

    private func updatePaths() {
        // update main path

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

            if path == nil {
                path = createPathStartingAtPoint(points[0])
            }

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

            points.removeFirst(3)
        }

        // build temporary path up to last touch point

        let pointCount = points.count

        if pointCount == 2 {
            temporaryPath = createPathStartingAtPoint(points[0])
            temporaryPath?.addLineToPoint(points[1])
        } else if pointCount == 3 {
            temporaryPath = createPathStartingAtPoint(points[0])
            temporaryPath?.addQuadCurveToPoint(points[2], controlPoint: points[1])
        } else if pointCount == 4 {
            temporaryPath = createPathStartingAtPoint(points[0])
            temporaryPath?.addCurveToPoint(points[3], controlPoint1: points[1], controlPoint2: points[2])
        }
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        constructIncrementalImage()
        path = nil
        temporaryPath = nil
        setNeedsDisplay()
    }

    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        touchesEnded(touches!, withEvent: event)
    }

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

        localPath.moveToPoint(point)

        localPath.lineWidth = lineWidth
        localPath.lineCapStyle = .Round
        localPath.lineJoinStyle = .Round

        return localPath
    }

    private func constructIncrementalImage(includeTemporaryPath includeTemporaryPath: Bool = true) {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
        strokeColor.setStroke()
        snapshotImage?.drawAtPoint(CGPointZero)
        path?.stroke()
        if (includeTemporaryPath) { temporaryPath?.stroke() }
        snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
}

顺便说一句,虽然我是提供路径生成代码的人,但我意识到它可以稍微简化一下。我也修复了一个错误。参见上面的代码。

然后你问:

  1. 如何更改路径和临时路径的 alpha/不透明度?

您只需调整调用的颜色即可setStroke具有适当的阿尔法。例如,如果您希望临时路径位于alpha对于主路径,您可以执行以下操作:

override func drawRect(rect: CGRect) {
    snapshotImage?.drawInRect(rect)

    strokeColor.setStroke()
    path?.stroke()

    strokeColor.colorWithAlphaComponent(0.5).setStroke()
    temporaryPath?.stroke()
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

消除在 Swift 中连续绘制 UIBezierPath 期间的滞后延迟 的相关文章

  • 在React-native中,如何更改NavigatorIOS的样式

    在react native中 如何更改NavigatorIOS的样式 例如背景颜色 谢谢你 var speedNews React createClass render function return
  • NativeScript:禁用 iOS WebView 缩放控件的方法?

    我正在尝试找出一种方法来防止用户通过捏合手势和双击来放大和缩小 iOS WebView tns ios 3 4 1 本质上禁用所有缩放 就像苹果之前使用的 html 元标记一样让用户决定是否要使用 iOS 10 及更高版本进行缩放 我找到了
  • CF 类型的带有 __attribute__((NSObject)) 的强 @property 不会保留

    更新 自 Xcode 4 6 起 此问题已得到修复 现在 这项技术再次按预期发挥作用 但是 在代码中使用之前 请务必阅读 Rob Napier 出色答案顶部的注释 原帖 ARC Xcode 4 3 1 iOS 5 1 我有一个 CF 类型
  • 用于 Flutter 原生广告的 Objective-C 的 Swift 等效项

    我想为我的 Flutter 项目实现原生广告 它使用 Swift 而不是 Objective C https developers google com admob flutter native https developers googl
  • 使用 Core Data Swift 对 TableView 和行进行分区

    我在 sqlite 中有两个表 代码由 XCode Generation 生成 class Event NSManagedObject NSManaged var startDate NSDate NSManaged var details
  • iOS:启动图像多语言

    我有一个多语言应用程序 我的问题是启动图像 根据设备的语言使用启动图像的方法是什么 有什么东西在info plist file 解决方案是像项目中的任何其他资源一样本地化 Default png 从 Xcode 的项目列表中选择 Defau
  • 制作一个固定大小的 UIView,如 UISwitch(使用 IBDesignable)

    当我使用时本指南 http iphonedev tv blog 2014 12 15 create an ibdesignable uiview subclass with code from an xib file in xcode 6要
  • 使用 Swift 解析框架

    有人尝试过将 Parse Framework 与 swift 一起使用吗 只要添加桥接文件 您就可以使用 swift 和 Objective C 代码 这是我的查询 从 Parse 返回的 对象 数组正确地包含了我的所有数据 但该方法在将
  • UIButton 未出现在 iPhone 5S 上

    总的来说 我对 iOS 开发和开发还很陌生 我一直在开发时间 记录保存应用程序 但遇到了一个奇怪的问题 在我的一个视图控制器上 我有一个 UITableView 每个单元格都是一个按钮 可通往不同的视图控制器 在第一个单元格上 用户应该能够
  • SwiftUI TabbedView 仅显示第一个选项卡的内容

    我正在尝试建立一个TabbedView使用以下简单代码 TabbedView Text Hello world tabItemLabel Text Hello Text Foo bar tabItemLabel Text Foo 运行时 两
  • iOS 8 UITableView 分隔符插入 0 不起作用

    我有一个应用程序 其中UITableView的分隔符插入设置为自定义值 右0 Left 0 这完美地适用于iOS 7 x 但是在iOS 8 0我看到分隔符插入设置为默认值15在右侧 即使在 xib 文件中它设置为0 它仍然显示不正确 我该如
  • 将深层链接传递到 iOS 模拟器?

    我想找到一种更简单的方法来在 iOS 模拟器中调用深层链接 在 Android 上 您可以使用 ADB 通过控制台将链接传输到模拟器中 是否有类似的方法或解决方法来打开最新 iOS 模拟器的深层链接 您可以在终端中输入以下内容 xcrun
  • 在 Objective-C iPad 开发中发布

    我正在尝试发出 POST 请求 但我似乎无法弄清楚出了什么问题 我从服务器收到响应 但我的电子邮件 密码对似乎没有正确发送 读取 由服务器 它告诉我不存在这样的帐户 这是我的代码 它包含在一个函数中 当用户按下我创建的 登录 按钮时调用该函
  • 如何在 iOS 中通过 3G 连接创建无线热点

    如果我愿意 我将如何创建一个像这样的应用程序MyWi http intelliborn com mywi html 即 WiFi 网络共享应用程序 实现该功能需要哪些步骤 我需要使用哪些框架 库 我们的目标不是尝试将此应用程序放入应用程序商
  • AWS MobileHub:重命名 Android / iOS 示例项目

    我是 AWS Mobilehub 的新手 我喜欢它允许我使用 AWS 配置选项创建项目 但是 当我尝试构建应用程序 ios swift android 时 它总是使用我的示例项目作为项目名称 在 AWS 项目的大多数配置设置中 例如使用 c
  • 在 xCode 6 中发现意外的 Mach-O 标头代码:1918975009

    导致错误的原因是什么 Found an unexpected Mach O header code 1918975009 in xCode 6 我将我的项目存档并作为ad hoc构建并且它构建 链接 存档很好但是当我在xCode组织者和选择
  • 如何解决创建 SwiftData #Predicate 的编译器错误?

    我一直在尝试很多方法来解决这个问题 我正在尝试使用谓词获取 SwiftData 记录 但我尝试的一切都会导致两个错误 初始化程序 init 要求 Item 符合 Encodable 初始化程序 init 要求 Item 符合 Decodab
  • 在模拟器上卸载应用程序后,NSUserDefaults 未清除

    这听起来可能很菜鸟 我想检查用户是否第二次进入我的应用程序 以便保留我正在使用的运行计数NSUserDefaults 我已经在我的中实现了以下代码rootViewController s viewDidLoad method NSUserD
  • 在 Swift 中使用 Obj-C 完成块

    在 Objective C 中 我有一个完成块类定义为 File h typedef void MYCompletionBlock BOOL success NSDictionary result NSError error 然后 在 Sw
  • 将对象映射到 TableView 部分的 Swift 二维数组

    我想不出更好的方法来做到这一点 我将学生对象的所有属性映射到二维数组中 所以我的电视有几个部分 我也不能使用静态表视图 如果是这样 这个问题就不会存在 所以我在 TVC 中的代码 let currentUser PFUser current

随机推荐

  • 在 `git rebase --update-refs` 之后自动推送

    Git 2 38 引入了 update refs标记为 rebase 命令 如果您有一个分支链 它将更新链中的所有分支 当我将大型 PR 分解为更容易审查的 PR 时 我发现这非常有用 然而 之后git rebase update refs
  • 将组名称获取到星号中相应的 pri 端口

    我正在使用 sagoma 8 端口卡 我的chan dahdi conf配置端口是 autogenerated by usr sbin wancfg dahdi do not hand edit autogenrated on 2015 0
  • Excel 单元格在双击之前不会计算

    我有一个包含三个求和函数的 Excel 工作表 该工作表是一个模板 因此除了 A 列中的一些文本描述外 工作表的其余部分是空白的 我使用 ETL 流程 Talend Open Studio 打开工作表 输入一些数据 附加几个其他工作表 并将
  • 如何从连接到同一网络的另一台计算机打开 create-react-app?

    我在用创建反应应用程序并托管在其默认端口本地主机 3000并希望从同一网络上的另一台设备访问它 我得到了我的主机IP的IP 使用ifconfig 192 168 0 5并尝试打开192 168 0 5 3000但这没有用 有什么办法可以实现
  • Laravel - 如何获取特定用户的委托角色

    我正在用 Laravel 做一个小工作并使用齐扎科信托公司 以管理员身份登录时我想查看全部Roles特定用户的 我搜索了一段时间但没有找到任何线索 我怎样才能使用Entrust或者我应该使用 SQL 查询吗 在您的 User 类中添加 pu
  • Windows 服务器上的 PTP 同步(与 Linux 相比) - 可以保证什么精度

    我想知道大家是否知道准确度如何PTP http en wikipedia org wiki Precision Time Protocol在 Windows Server 2008 上可以保证同步 我知道这个线程 Windows 中进程的最
  • 访问 Google Chrome 的缓存

    是否可以从扩展程序中访问 Google Chrome 的缓存 我想编写一个扩展 当无法访问在线页面时 例如互联网连接问题 加载页面的缓存版本 Updated 我知道我可以编写一个可通过扩展访问的 NPAPI 插件来完成此任务 但我不想编写一
  • Firestore 客户和发票,修订后的要求

    这个问题是此处发布的问题的后续问题 Firestore 客户和发票 如何建模 https stackoverflow com questions 50867267 firestore clients and invoices how to
  • C++ 错误:没有调用“print_size”的匹配函数

    我有这个代码 include
  • 为什么函数 printk() 不使用逗号来分隔参数?

    一个例子printk call printk KERN INFO Log message n 也许这个问题更多地是关于C的 因为我之前从未见过C中的函数可以不用逗号分隔参数 这是如何运作的 编译器如何处理这些信息 由于日志级别是一个整数 而
  • 在没有正确原型的情况下调用 printf 是否会引发未定义的行为?

    这个看起来无辜的程序是否会调用未定义的行为 int main void printf d n 1 return 0 是的 调用printf 没有适当的原型 来自标准头
  • python:单行笛卡尔积for循环

    你知道你能做到吗 gt gt gt x y for x in xrange 2 for y in xrange 5 0 0 0 1 0 2 0 3 0 4 1 0 1 1 1 2 1 3 1 4 很整洁 是否有 for 循环版本或者只能对列
  • Flash AS3 - 将多个对象拖放到一个目标?

    标题或多或少是不言自明的 我一直在学习许多不同的教程 而且说实话 我对 AS3 不太擅长 上图显示了我的目标 无论如何 我在我看到的大多数在线教程中注意到 拖放教程要么基于一个对象到一个目标 要么基于多个对象到多个目标 所以我想知道是否有人
  • 如何让php函数每5秒循环一次

    我正在尝试创建一个每秒更新一次的 php 函数 使用 php 本身 没有其他语言 只是纯 PHP 代码 function exp do something 我希望它每秒返回一个值 就像每秒更新一样 对于应用程序服务器 不是 Web 服务器
  • 让 WordPress 使用页面而不是类别

    我做了大量的研究并发现了一些像这样的解决方案 https wordpress stackexchange com questions 106042 force wordpress to show pages instead of categ
  • VS 2008 Addon 暂时禁用/删除所有 catch 块

    是否有任何插件可以暂时禁用所有 catch 块 我正在维护一个应用程序 我需要找出它到底在哪里抛出异常 有人已经完成了错误处理 所有层都完成了 这使我的工作变得艰难 我不知道如何禁用 catch 块 但您想要实现的目标可以通过异常对话框中的
  • 如何设置微调器下拉列表的最大长度?

    我有一个微调器 当前打开时会遮挡微调器下方的一些文本 我需要通过 java 代码或 XML 来限制微调器的最大下拉长度 这样它就不会模糊此文本 The current design is the left example while the
  • 获取错误消息

    我们使用以下代码在验证文档时添加新的错误消息 function addFacesMessage message component try if typeof component string component getComponent
  • Firebase Push() 与 Angularfire $save()

    angularfire save 与 firebase push 相比如何 我知道 Push 在存储数据时会生成一个唯一的密钥 但我无法使用 AngularFire 重新创建该行为 有没有办法或者我应该使用 push 如果是这样 在什么情况
  • 消除在 Swift 中连续绘制 UIBezierPath 期间的滞后延迟

    下面的代码通过覆盖触摸来绘制线条 但是在连续不间断绘制的一段时间内开始出现滞后 手指在屏幕上移动的时间越长 这种滞后就会累积并变得更严重 结果是实际设备上的 CPU 几乎达到极限 CPU 98 并且绘图持续时间越长 生成的图像看起来就越不稳