這裡有一篇很棒的文章寫如何在Android上獲取流暢的簽名:Smoother Signatures,但是我沒有找到一篇是寫在iOS上如何實現。那麼,究竟怎麼做才能在iOS設備上獲取用戶的簽名呢?
雖然我沒有找到任何關於獲取簽名的文章,但是在App store上已經有了實現得很好的app。 Paper by 53 是一個畫畫的iPad應用程序,它擁有漂亮並且靈敏的畫筆,這也是我所要追求的用戶體驗。
代碼: SignatureDemo 本文最後的下載
連點成線
最簡單得辦法是,依次獲取觸摸點並且用直線把它們連起來。
在UIView子類的初始化方法中創建path和用於捕獲觸摸事件的gesture recongnizer .
// Create a path to connect lines
path = [UIBezierPath bezierPath];
// Capture touches
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
[self addGestureRecognizer:pan];
將捕獲到的pan事件location數據依次加入到貝塞爾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];
}
畫出軌跡
- (void)drawRect:(CGRect)rect
{
[[UIColor blackColor] setStroke];
[path stroke];
}
用這種方法畫個字母J就暴露出一些問題了。
當簽名速度較慢時,iOS可以捕獲到足夠的touch位置信息,讓連接起來的直線看起來不那麼明顯。但是當手指移動速度很快時就有麻煩了。
在2012蘋果開發者大會中介紹的 Building Advanced Gesture Recognizers 提到,可以用數學來解決這個問題。
二次貝塞爾曲線
我們需要用二次貝塞爾曲線去連接那些觸摸點,而並非用直線,可以參考上面給出的蘋果開發者大會視頻(大約在42:15處)。連接二次貝塞爾曲線時,應把觸摸點當作控制點,而取中點為對應的起點和終點。
添加二次貝塞爾曲線到之前的代碼中,需要用到上一次的touch信息,所以我們增加一個實例變量來存儲它。
CGPoint previousPoint;
寫一個計算2點中點的方法
static CGPoint midpoint(CGPoint p0, CGPoint p1) {
return (CGPoint) {
(p0.x + p1.x) / 2.0,
(p0.y + p1.y) / 2.0
};
}
更新手勢處理,用二次貝塞爾曲線替換掉之前的直接連接處理
- (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];
}
沒有寫很多代碼,我們就看到了很大的改觀。稜角不見了,但是作為簽名似乎有點乏味。每一處曲線都是等寬的,和用一只真正的鋼筆簽出來的簽名效果相違背。
可變的筆刷寬度
筆刷的寬度應該基於簽名的速度而變化,這樣的簽名看起來才自然。UIPanGestureRecognizer 有一個 velocityInView 方法可以返回當前觸摸點的速度。
為了畫出變化的寬度,我改用OpenGL ES 曲面細分將筆刷轉換成三角序列(OpenGL支持畫線,但是iOS不支持繪制平滑的可變寬度的線條)。二次貝塞爾曲線點需要重新計算,但是這超出了這篇文章的討論范疇,具體可以查看代碼,見本文最後鏈接。
用相鄰的2個二次貝塞爾曲線點來說明一下,可以計算得到此兩點差值表示的向量的垂直向量,並且設定其長度為當前厚度值的1/2(譯者注:下圖大括號部分包含2份1/2厚度值長度,故恰好為當前厚度),采用GL_TRIANGLE_STRIP的方式繪制三角序列,因此需要2個二次貝塞爾曲線點來確定一個含有2個三角形的矩形段。
(譯者注:原理細節原文一筆帶過,建議讀下代碼,了解下OpenGL曲面細分,有助於更好的理解)
這個例子,就是用二次貝塞爾曲線和速度控制筆刷厚度的方法畫出來的簽名,自然多了吧。
在iOS上繪制自然的簽名相關附件下載:
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /2014年資料/3月/4日/在iOS上繪制自然的簽名
下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm