1. 程式人生 > 實用技巧 >ios觸控精靈_ios觸控繪圖

ios觸控精靈_ios觸控繪圖

ios觸控精靈

In a recent project I’ve been working on, we needed to provide the user a way of drawing on top of a photo by touching the screen. The user would be able to draw freely over the image, and also they would be able to erase any of their previous drawing simply by touching the erase button and then touching the drawing.

在我最近從事的專案中,我們需要為使用者提供一種通過觸控式螢幕幕在照片上方進行繪圖的方法。 使用者將能夠在影象上自由繪圖,並且他們將能夠僅通過觸控擦除按鈕然後觸控該圖形來擦除其先前的任何圖形。

As it turns out the hard part was not the drawing itself, since a quick internet search threw a fantastic drawing tutorial in the Ray Wenderlich site, the hardest for us was the erasing of a particular drawing.

事實證明,最困難的部分不是圖形本身,因為快速的網際網路搜尋在Ray Wenderlich網站上引發了精彩的圖形教程 ,對我們來說最難的是擦除特定圖形。

UIGraphicsContext (UIGraphicsContext)

The Ray Wenderlich tutorial has three main functions that comprise most of the drawing logic.

Ray Wenderlich教程具有三個主要功能,其中包括大多數繪製邏輯。

Where lastPoint is being used to save the last known

point of the line to draw (in this case the last known point is also the initial point).

使用lastPoint儲存要繪製的線的最後一個已知點的情況(在這種情況下,最後一個已知點也是初始點)。

Then touchesMoved:

然後touchesMoved

As you can see, drawing a line is actually quite simple, you just create a custom UIView and basically paste this code and that view is almost ready to serve as a drawing canvas for your user.

如您所見,繪製線條實際上非常簡單,您只需建立一個自定義UIView並基本上貼上此程式碼,該檢視就幾乎可以用作使用者的繪圖畫布了。

The problem we had with this approach is that UIGraphicsGetImageFromCurrentImageContext returns a UIImage? with size of view.frame.size and all of the generated images would have the same size and position making it almost impossible to distinguish between one and another, further complicating the erasing of an individual drawing.

這種方法的問題在於UIGraphicsGetImageFromCurrentImageContext返回UIImage? 使用view.frame.size大小和所有生成的影象將具有相同的大小和位置,從而幾乎無法區分彼此,從而使單個圖形的擦除變得更加複雜。

Which took us to option number 2:

這使我們進入了選項2:

CAShapeLayer (CAShapeLayer)

Using the same touchesBegan/Moved/Ended functions as before we can create CGPaths, which describe all of the points where the user has moved it's finger, then we add this path to a CAShapeLayer and add this new layer as a sublayer of the "canvas" view this way we get images (layers) for each individual drawing.

使用與之前相同的touchesBegan/Moved/Ended函式,我們可以建立CGPaths ,它描述了使用者將手指移動到的所有點,然後將該路徑新增到CAShapeLayer並將此新層新增為“ canvas”的子層以這種方式檢視,我們可以獲得每個單獨圖形的影象(圖層)。

Now let’s really see how using CGPath compares to using UIGraphicContext .

現在,讓我們真正地看看使用CGPath與使用UIGraphicContext

Show me the code!

給我看看程式碼!

To begin with we created a custom view called DrawingView and replaced the touchesBegan function with this:

首先,我們建立了一個名為DrawingView的自定義DrawingView ,並將touchesBegan函式替換為:

The touchesMoved function remains the same as previous example and the drawLine gets changed to this:

touchesMoved函式與前面的示例相同,並且drawLine更改為以下示例:

New drawLine function using CGPath and CAShapeLayer apis
使用CGPath和CAShapeLayer API的新drawLine函式

Comparing previous implementation (CGContext) with current implementation (CALayer) you can see they are incredibly similar. They both make almost the same function calls, but in different contexts. Instead of context.move(to: fromPoint), we use currentPath.move(to: fromPoint), instead of context.setLineCap(.round) we now use currentLayer.lineCap = .round.

將先前的實現(CGContext)與當前的實現(CALayer)進行比較,您會發現它們非常相似。 它們都進行幾乎相同的函式呼叫,但是在不同的上下文中。 代替context.move(to: fromPoint) ,我們使用currentPath.move(to: fromPoint) ,而不是context.setLineCap(.round)我們現在使用currentLayer.lineCap = .round

Finally the touchesEnded function gets changed to this, since we need to render the added sublayers:

最後, touchesEnded函式更改為此,因為我們需要渲染新增的子層:

At this point the user can draw by moving it’s finger on the screen, each drawing is individual and it’s linked to a CAShapeLayer.

此時,使用者可以通過在螢幕上移動手指來進行繪製,每個繪製都是獨立的,並且已連結到CAShapeLayer

Now how do we erase specific drawings? This is the issue we wanted to solve in the beggining, so let's get to it!

現在我們如何刪除特定的圖紙? 這是我們在開始時要解決的問題,讓我們開始吧!

擦除 (Erasing)

First we will create a function that will help us find the layer that contains the touch point:

首先,我們將建立一個函式來幫助我們找到包含接觸點的圖層:

Now if you have some worked previously with CAShapeLayers you may have used the hitTest function to check if the layer contains a CGPoint.

現在,如果您以前曾與CAShapeLayers一起工作過, CAShapeLayers可能已經使用hitTest函式檢查該圖層是否包含CGPoint

In our specific case this doesn’t work because for the hitTest function requires that the layer have a frame and we are not assigning one to it, because if the user draws a long diagonal line through the screen the frame that contains the whole drawing would be a really big rectangle which would detect touches even if the finger is far away from the actual drawn line.

在我們的特定情況下,這是行不通的,因為hitTest函式要求layer具有frame並且我們不會為其分配frame ,因為如果使用者在螢幕上繪製一條較長的對角線,則包含整個圖形的框架會是一個非常大的矩形,即使手指遠離實際繪製的線也可以檢測到觸控。

Image for post
The red line is path drawn by the user, the blue line would be the frame of the layer containing the path
紅線是使用者繪製的路徑,藍線將是包含該路徑的圖層的框架

Where the red line is the line drawed by the user, and the blue rectangle is the frame.

其中紅線是使用者繪製的線,藍色矩形是框架。

Another thing that pops up in the previous code is that we are creating an outline of the shapeLayer.path and then calling the contains(point) function on it, why not call the contains function directly on the path, why do we need to create an outline?

先前程式碼中彈出的另一件事是,我們正在建立shapeLayer.path輪廓 ,然後在其上呼叫contains(point)函式,為什麼不直接在路徑上呼叫contains函式,為什麼我們需要建立一份大綱?

Well it turns out that the contains(point) function only works on closed paths, so if the user draws a straight line that funtion would always return false.

事實證明, contains(point)函式僅適用於封閉路徑,因此,如果使用者繪製直線,函式將始終返回false

The outline is something like this:

大綱是這樣的:

Image for post

As you can see the outline is a closed shape, so it gives us the required precision we want when deleting shapes by tapping.

如您所見,輪廓 封閉形狀,因此它為我們提供了在點選刪除形狀時所需的精度。

Great explanation of this here.

這裡對此有很好的解釋。

Ok let’s carry on, the findLayer function returns the touched layer, so now we need to delete it:

好的,讓我們繼續, findLayer函式返回被觸控的layer ,所以現在我們需要刪除它:

With these two new functions in place, we need a way to switch between drawing and erasing, so we add a control property and use it to limit drawing capabilities in touchesBegan:

有了這兩個新功能後,我們需要一種在繪製和擦除之間切換的方法,因此我們添加了一個控制元件屬性,並使用它來限制touchesBegan繪製功能:

and touchesMoved:

touchesMoved

In touchesEnded we call the findLayer if isDrawing == false

touchesEnded ,如果isDrawing == false則呼叫findLayer

The result is this:

結果是這樣的:

Image for post

It looks kind of nice! Well this are the basics for drawing in iOS, the code can be found here.

看起來不錯! 好吧,這是在iOS中進行繪圖的基礎,可以在此處找到程式碼。

This is a very simple example of how drawing and erasing works, this code can be further exapanded and refactored to give the user more drawing options like drawing a square or a circle, or showing the angle between two lines. These options will be covered on a following article.

這是繪製和擦除工作方式的非常簡單的示例,可以進一步擴充套件和重構此程式碼,以便為使用者提供更多繪製選項,例如繪製正方形或圓形或顯示兩條線之間的角度。 這些選項將在下一篇文章中介紹。

I hope you liked it! Thanks for reading!

我希望你喜歡它! 謝謝閱讀!

翻譯自: https://levelup.gitconnected.com/ios-touch-drawing-4d5b715d2d35

ios觸控精靈