使用UberSignature在iOS上进行工程签名渲染

0
使用UberSignature在iOS上进行工程签名渲染

自2009年成立以来,优步的使命就是让交通更可靠,包括送食物、送小猫、送冰淇淋等等。的超级API这使得开发人员可以很容易地构建应用程序来协调各种东西的移动,但每种技术都有不同的需求。

例如,当我们推出产品时UberRUSH2015年,它的快速增长要求我们快速添加新的功能,因为有不同交付需求的企业加入了平台;许多企业需要的一个关键安全特性是通过签名的交付证明。

为了满足这一要求,我们需要一种方法来收集签名,以确认UberRUSH上的交付。我们认为最有效的方法是向Uber Partner iOS应用.通过在应用程序中包含这一功能,快递合作伙伴可以直接在他们的设备上快速、轻松地从收件人那里收集签名。

今天,我们开源UberSignature,这是一项允许用户在UberRUSH应用程序上绘制和存储签名的新功能。本文概述了我们如何利用现有算法(如上图所示)设计UberSignature(如上图所示),并根据我们的交付合作伙伴的需求对解决方案进行了一些修改。我们还解释了我们如何使Uber Partner应用程序的触摸屏签名看起来更自然。

为iOS Research构建UberSignature雷竞技是骗人的

开发UberSignature的主要挑战之一是确保签名呈现代码在所有类型的移动设备上都能执行,有些已经有五年多的历史了然而,仍有许多配送合作伙伴在使用它。该功能还需要清晰的架构,这样任何Uber工程师都可以维护它,避免崩溃。这一点尤为重要,因为该功能是面向消费者的;交付伙伴在交付事务期间重新启动合作伙伴应用程序会影响他们的效率,还会给收件人带来问题。

iOS没有任何内置类可以直接在智能手机屏幕上通过触摸绘图,我们想要一个解决方案,可以让用户“签名”一个自然、流畅的签名。幸运的是,这在iOS世界中并不是一个新问题。有许多资源可以在网上找到解决这个问题的方法。通过我们的研究,我们雷竞技是骗人的发现了2013年3月的一篇文章,”iOS SDK:高级手绘技术,一种先进的绘图算法,它产生的结果非常适合我们的需求。

这个解画出两条平行线bezy路径它们彼此之间的距离不同,由两端的直线连接,形成一个光滑的曲线,厚度不断变化。其他的解改变了每段线的厚度特性;这是有问题的因为厚度的变化是突然的,让签名看起来不自然。

我们的代码基于并行曲线方法,但扩展了实现并加入了一些新特性:

  • 每次触摸都会更新签名:而不是等待每四个接触点画一个完整的be齐尔曲线,我们想要一个能在每次触摸时更新签名的解决方案。因为我们画不出be当齐尔曲线上的控制点少于4个时,我们决定用我们已有的点画出我们所能画出的:一个点,两条线,三条四轴曲线(只有一个控制点的曲线)。通过这种方法,签名在每次触摸时改变形状,在记录下每个触摸点后自我修正。这使得实现更加复杂,但绘图感觉响应更迅速,签名线结束在触摸结束的地方,使签名看起来更流畅和自然。
  • 新签名权值计算:我们改变了计算签名权值的方式。我们发现,让签名看起来更薄,更接近纸上钢笔的样子,而不是随着用户手指移动速度的加快而变厚。
  • 画圆点的能力:我们添加了在点击视图时创建圆点的功能,这对于允许用户准确地绘制他们的签名非常重要。
  • 将实现分解为多个类:WE构建了一个视图控制器,它可以处理调整大小,同时仍然提供重置、检索当前签名和使用以前的签名图像初始化的功能。在底层,我们将实现分解为五个类,职责更少。这使得代码更容易理解、调试和维护。

架构设计

为了在应用程序中启用这些功能并促进更自然的签名,我们创建了一个具有五个不同类的签名算法,如下所示:

图1:UberSignature算法由五个类组成。

在接下来的章节中,我们将概述这些类如何适应UberSignature体系结构:

UBezierPath + UBWeightedPoint

呈现的签名显示与每个点相关的不同权重。所以我们提出了一个类型定义结构体要表示这个,叫做UBWeightedPoint:


CGPoint点;
CGFloat重量;
}
UBWeightedPoint;

然后我们做了一个分类UIBezierPath这就从加权点创建了路径。它是用创造不同的方法建造的UIBezierPaths最多四个加权点,如下所示。这是我们扩展校正技术所需要的,我们在每次触摸时更新签名。

图2:根据收集到的点的数量,有四种类型的线/曲线。

这个类别整齐地包装了绘制签名béziers所需的所有逻辑。这里的绘图和计算技术类似于我们最初的灵感,从状态和视图代码中抽象出来。

UBSignatureBezierProvider

该对象响应方法调用addPointToSignatureBezier:并生成签名,将其作为组合的临时(不断变化的)路径和最终路径提供。bézier会变长,直到收集到4个点,足以形成完整的bézier曲线。如图3所示,最终完成的路径总是返回最终完成的线,而临时路径返回仍在绘制的线的部分。

添加新接触点的方法使用标准线性方程计算,Y = mx + c,它根据该点与前一个接触点的距离来查找该点的相关权值。在这个方程中,x是点与点之间的距离减去最大距离,c(常数)决定了直线的最小权值,m(梯度)控制了权值随长度变化而减小的速率,y是返回的权值。随着距离的增加,这些变量之间的关系导致权重变小,直到最大距离。

添加点时,提供程序使用更新的临时bézier调用它的委托UIBezierPath用新计算的加权点分类方法,如下图所示:

图3:UBSignatureBezierProvider类创建临时的和最终的béziers通过委托回调公开它们。

一旦添加了下一个bézier的接触点,当前bézier的最后一个点将成为前一个点和下一个点的平均值,这是一种平滑它们之间连接的策略。此时,当前的bézier将不会更改,因此将使用这个最终完成的路径通知委托。临时的bézier再次启动,使用最终完成的bézier的最后一个点作为第一个点,使用新的点作为第二个点。这个过程无限重复,直到重置提供程序,开始新的一行。

UBSignatureDrawingModel

UBSignatureDrawingModel类的实例的类UBSignatureBezierProvider要生成签名,存储它并将其作为两个对象公开用户界面图像和a(暂时的)UIBezierPath

使用单个bézier来表示整个签名不能很好地扩展;它必须在每次更新时绘制,每次都有更多的点,因此每当签名发生变化时,在屏幕上呈现签名所需的时间就会增加。我们使用图像来表示签名,每次完成签名时都在图像上绘制提供者的béziers。尽管这个图像比一个小的bézier画的时间稍长一些,但与其他方法相比,它是一个常数时间操作,而且缩放很好(而且很快)。

我们的模型还存储和公开提供者的临时路径。我们不能将它绘制到图像中,因为在下一次更新时它将改变形状,我们不能从图像中删除之前的路径。相反,我们将分别存储和显示临时路径。这可以显示为另一个图像,但是渲染另一个全尺寸图像的性能不如渲染一个永远不会超过4个点的小bézier路径。

由于签名由图像表示,因此它与视图的尺寸相匹配。如果模型所支持的视图的大小发生了变化,则可以要求模型调整图像的大小。在调整大小之前,它会将临时的bézier绘制到图像中,因为缩放bézier路径和计算缩放权重是棘手的,并且不会与调整后的图像很好地对齐。

UBSignatureDrawingModelAsync

UBSignatureDrawingModelAsync是模型的包装器,允许它被异步使用,这样它就不会阻塞应用程序的主线程。这一点很重要,因为该模型的计算成本很高;在主线程上运行它将减少我们捕捉触摸的频率,显著影响签名的准确性。

为了避免使实现复杂化,底层模型是同步设计的,不使用后台线程。然后,异步包装器在NSOperationQueue而主线程为同步方法。model属性是原子的,以避免多个线程同时访问模型,从而将异步代码的复杂性抽象到单个类中。这也意味着我们仍然可以直接使用同步模型。在调试的情况下,这使得分步执行代码变得容易得多。

UBSignatureDrawingViewController

视图控制器,命名UBSignatureDrawingViewController,封装我们的签名特性。它在内部使用异步模型,覆盖UIResponder触摸事件添加点,然后显示签名图像通过UIImageView临时路径bézier使用CALayer.它还处理调整大小事件,并在完成时公开获取签名的图像方法。

下一个步骤

随着智能手机对商业越来越重要,UberSignature提供了一种流线型和印刷更准确的方式,只使用触摸在iOS上绘制签名。我们希望开发者可以在他们自己的应用中使用UberSignature。

Dom Chapman是Uber Everything团队的高级软件工程师Uber的纽约办公室

评论