如何在iOS設(shè)備上捕捉用戶簽名
Square Engineering Blog 上有一篇很棒的文章,為 Android 帶來了更流暢的簽名,但我沒有找到任何與 iOS 相關(guān)的介紹。所以,在 iOS 設(shè)備上捕捉用戶簽名的***方式是什么呢?
雖然我沒有找到關(guān)于簽名捕捉的文章,但 App Store 上已有一些應(yīng)用很好地實現(xiàn)了簽名捕捉。我的目標(biāo)用戶體驗是達(dá)到 iPad 應(yīng)用 Paper by 53 那樣的效果(它是一個繪圖應(yīng)用,有著漂亮且***響應(yīng)性的筆刷)。
本文所有的代碼都可以在 Github 倉庫 SignatureDemo 里找到。
點點相連
最簡單的方式是捕捉用戶的觸摸點并用直線將它們連接在一起。
在 UIView 子類的初始化方法中,創(chuàng)建一個 path ,以及用于捕捉觸摸事件的手勢識別器。
- // 創(chuàng)建一個 path 以連接各點
- path = [UIBezierPath bezierPath];
- // 捕捉觸摸
- UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
- pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
- [self addGestureRecognizer:pan];
捕捉 pan 手勢的觸摸點,并通過連線將其放入 bézier path 里:
- - (void)pan:(UIPanGestureRecognizer *)pan {
- CGPoint currentPoint = [pan locationInView:self];
- if (pan.state == UIGestureRecognizerStateBegan) {
- [path moveToPoint:currentPoint];
- } else if (pan.state == UIGestureRecognizerStateChanged)
- [path addLineToPoint:currentPoint];
- [self setNeedsDisplay];
- }
畫出 path:
- - (void)drawRect:(CGRect)rect
- {
- [[UIColor blackColor] setStroke];
- [path stroke];
- }
上圖是用這種技術(shù)畫出的字母 “J” ,它也揭示了一些問題:在較慢的速度下, iOS 可以捕捉到足夠多的觸摸點,因此看起來較平滑,但稍快的移動就會導(dǎo)致觸摸點之間較大的間距,簽名看起來就是一些直線段相連。
2012 年的 Apple 開發(fā)者大會有一個 session 叫做 創(chuàng)建高級手勢識別器,它用了一點數(shù)學(xué)來解決此問題。
二次貝塞爾曲線
作為用線段連接各觸摸點的代替,我們改用二次貝塞爾曲線來連接這些點,使用前述的 WWDC session 上討論的技術(shù)(定位到 42 分 15 秒)。用二次貝塞爾曲線來連接觸摸點,使用觸摸點作為控制點以及中間點作為起點和終點。
添加二次曲線到之前的代碼里要求存儲前一個觸摸點,所以要添加一個實例變量:
- CGPoint previousPoint;
并創(chuàng)建一個函數(shù)來計算兩點的中間點:
- static CGPoint midpoint(CGPoint p0, CGPoint p1) {
- return (CGPoint) {
- (p0.x + p1.x) / 2.0,
- (p0.y + p1.y) / 2.0
- };
- }
再更新 pan 手勢處理器,用二次曲線替換直線:
- - (void)pan:(UIPanGestureRecognizer *)pan {
- CGPoint currentPoint = [pan locationInView:self];
- CGPoint midPoint = midpoint(previousPoint, currentPoint);
- if (pan.state == UIGestureRecognizerStateBegan) {
- [path moveToPoint:currentPoint];
- } else if (pan.state == UIGestureRecognizerStateChanged) {
- [path addQuadCurveToPoint:midPoint controlPoint:previousPoint];
- }
- previousPoint = currentPoint;
- [self setNeedsDisplay];
- }
- grefbd6a2d7175616472617469632e706e67
并沒有改寫太多代碼,但我們已經(jīng)看到了巨大的不同。觸摸點不再可見,但對于捕捉簽名來說,它依然有些乏味。每個曲線都是同樣的寬度,這不符合實際用鋼筆簽名的感覺。
可變的線寬
基于觸摸速度,寬度可變,這樣就能創(chuàng)建出更加自然的筆畫。 UIPanGestureRecognizer
已經(jīng)包含有一個叫做 velocityInView:
的方法,它用一個 CGPoint
返回當(dāng)前觸摸的速度。
為了渲染出一個可變寬度的筆畫,我切換到 OpenGL ES 和一個能轉(zhuǎn)換筆畫為三角形(具體說來,是三角帶(triangle strip))的叫做鑲嵌(tesselation)的技術(shù)(OpenGL 支持畫線,但 iOS 不支持平滑過渡的可變寬度)。沿曲線的二次分點同樣需要被計算,但這超出了本文討論的范疇。還請看看 Github 上的源代碼了解細(xì)節(jié)吧。
給定兩個點,計算出一個垂直的向量,它的大小設(shè)置為當(dāng)前厚度的一半?;?GL_TRIANGLE_STRIP 的自然特性,只需要兩個點就能用兩個三角形去創(chuàng)建下一個矩形。
下面是使用二次貝塞爾曲線,并根據(jù)速度調(diào)節(jié)筆畫粗細(xì)而創(chuàng)造出的一個具有視覺吸引力且非常自然的簽名。
原文出處: JASON HARWIG
譯文出處: nixzhu(@nixzhu)