1
  1. I make a path using UIBezierPath then from this path I create a SKShapeNode.
  2. I want from the user to draw a same path by his touches on screen to match my original path.

this is the code:

import SpriteKit import GameplayKit

class GameScene: SKScene {

var orginalPath: UIBezierPath!

var orgialShape: SKShapeNode!
var userTouchs = [CGPoint]()
var drawingLine: SKShapeNode!


override func didMove(to view: SKView) {

    orginalPath = UIBezierPath()
    orginalPath.move(to: CGPoint(x: 170, y: 230))
    orginalPath.addLine(to: CGPoint(x: 370, y: 230))
    orginalPath.addQuadCurve(to: CGPoint(x: 480, y: 55), controlPoint: CGPoint(x: 525, y: 150))


    orgialShape = SKShapeNode(path: orginalPath.cgPath)
    orgialShape.strokeColor = UIColor.white
    orgialShape.lineWidth = 30
    orgialShape.lineCap = .round
    orgialShape.lineJoin = .round
    orgialShape.zPosition = 0
    orgialShape.name = "orginal"
    addChild(orgialShape)

}

func createLine() {
    if userTouchs.count > 2 {
        let line = SKShapeNode(points: &userTouchs, count: userTouchs.count)
        line.strokeColor = UIColor.red
        line.lineWidth = 10
        line.lineCap = .round
        line.zPosition = 10
        line.isAntialiased = true
        line.name = "newLine"
        addChild(line)

    }
}


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        let location = touch.location(in: self)
        let objects = nodes(at: location)
        for object in objects {
            if object.name == "orginal" {
                    userTouchs.append(location)
            }
        }
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        let location = touch.location(in: self)
        let objects = nodes(at: location)
        for object in objects {
            if object.name == "orginal" {
                    print(location)
                    userTouchs.append(location)
                    createLine()
            }
        }
    }
}

this code should make the user draw "only" on my path like I show in the picture:

enter image description here

but the problem is when I write the code I found the that I can draw a line out of the path, actually its look like a square not just a path like I show in the pictures:

enter image description here enter image description here enter image description here

So please did anyone can explain to me the right way to make the user only draw over my path and if he touch out the path he can't draw any thing.

Ammar Ahmad
  • 534
  • 5
  • 24

1 Answers1

0

You could define a path where the user is allowed to draw in, e.g. see the green path here:

clip path

Then clip to this path:

self.currentBezierPath.addClip()

See more details for this approach in my answer here: How to draw inside the black edges in iOS SDK with OpenGL ES?

Ensure that the user follows the path in one direction

You could divide the area e.g. into small rectangles and store them in order into an array, see sketch here:

divide path into areas

Then make sure that the user starts by touching the rectangles (shown in red in the illustration) only in ascending order and that the user does not leave the outer boundary (shown in green in the illustration).

To check whether a point lies within a path, the following can be used:

    let inside = bezierPath.contains(point)

Would that solve your use case?

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
  • Thank you for your answer I saw your work and its work fine if you have a picture to fill it with color, but in my case my app should learn children how two write letters so I need the user follow letter path with sequence and with your solution some time letter lines intersect so the user is just fill the letter with color and not follow the right path to learn how to write it correctly, if you have another answer Im will be happy to show me. – Ammar Ahmad Nov 24 '18 at 17:28
  • @AmmarAhmad Ah, ok, I see, I've updated the answer. It should now ensure that the user follows the letter path in the right direction and does not leave the entire path at all. – Stephan Schlecht Nov 25 '18 at 09:37
  • Actually its very interesting approach to use it, and I think it will solve my problem, But can you show me how to divide the path into a rectangles just with simple code (like divide a simple line) so I can understand how you do that, and thank you again for your help. – Ammar Ahmad Nov 25 '18 at 18:15
  • Here is a clever solution: https://stackoverflow.com/a/26131682/2331445. It's in Objective-C. If you scroll down and take a look at drawRect of ArrowView.m, you can see the Rob_forEachPointAtInterval method in action: for each interval it returns a point and a vector. For example, instead of drawing an arrow, you would create an array of rectangular paths. – Stephan Schlecht Nov 25 '18 at 19:09