I’m utilizing SwiftyDraw library to attract a path for my app. Drawing the trail is working effectively however there’s a downside if person begin drawing path and transfer the finger backwards and forwards in the identical place. Nonetheless its drawing the trail and it will get accomplished. Right here I’ve my complete code.
lazy var targetView: UIView = {
let sTargetView = UIView(body: CGRect(x: 0,
y: 0,
width: self.view.body.width,
top: self.view.body.top/1.3))
sTargetView.middle = CGPoint(x: UIScreen.principal.bounds.dimension.width/2,
y: UIScreen.principal.bounds.dimension.top/2)
sTargetView.backgroundColor = .clear
sTargetView.isUserInteractionEnabled = true
canvasView.isUserInteractionEnabled = true
canvasView.backgroundColor = .clear
sTargetView.addSubview(canvasView)
canvasView.brush = .thick
canvasView.brush.shade = self.assistiveTouchSwitch.isOn ? Shade(.clear) : Shade.init(self.accentColor)
canvasView.body = sTargetView.bounds
canvasView.delegate = self
return sTargetView
}()
var swiped = false
var lastPoint = CGPoint.zero
var accentColor = UIColor.purple//UIColor(hex: "00acc1")
let canvasView = SwiftyDrawView()
var isTutorialAnimationInProgresss = false
var strokeAnimationCounter = 0
var currentPathToTrace : CGPath!
var defaultTransform = CGAffineTransform()
var assistiveDrawLayersArray = [CAShapeLayer()]
var pathToHitTestAgainst : CGPath!
let dashLayer = CAShapeLayer()
let tutorialLayer = CALayer()
var strokePathsArray = [MyBezierPath]()
var strokeIndex = 0 {
didSet {
self.pathToHitTestAgainst = self.strokePathsArray[strokeIndex].cgPath.copy(strokingWithWidth: 10, lineCap: .spherical, lineJoin: .spherical, miterLimit: 0, remodel: defaultTransform)
}
}
@IBOutlet weak var assistiveTouchSwitch: UISwitch!
override func viewDidload() {
self.view.addSubview(targetView)
let p1 = MyBezierPath(svgPath: "M 55 20 V 90")
let p2 = MyBezierPath(svgPath: "M 55 47 C 10 18 11 90 55 63")
let p3 = MyBezierPath(svgPath: "M 55 47 C 93 25 94 62 71 73")
let p4 = MyBezierPath(svgPath: "M 35 20 H 80")
strokePathsArray.append(p1)
strokePathsArray.append(p2)
strokePathsArray.append(p3)
strokePathsArray.append(p4)
let combinedPath = CGMutablePath()
combinedPath.addPath(p1.cgPath)
combinedPath.addPath(p2.cgPath)
combinedPath.addPath(p3.cgPath)
combinedPath.addPath(p4.cgPath)
defaultTransform = CGAffineTransform(scaleX: self.targetView.body.width/109, y: self.targetView.body.width/109)
let shapeLayer = CAShapeLayer()
shapeLayer.backgroundColor = UIColor.cyan.cgColor
shapeLayer.remodel = CATransform3DMakeAffineTransform(defaultTransform)
// The Bezier path that we made must be transformed to
// a CGPath earlier than it may be used on a layer.
shapeLayer.path = combinedPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 10
shapeLayer.lineCap = .spherical
shapeLayer.strokeColor = UIColor.white.cgColor
self.targetView.layer.addSublayer(shapeLayer)
self.pathToHitTestAgainst = self.strokePathsArray[0].cgPath.copy(strokingWithWidth: 10, lineCap: .spherical, lineJoin: .spherical, miterLimit: 0, remodel: defaultTransform)
animatePath()
setUpLayersForAssistiveMode(shade: accentColor)
self.targetView.layer.addSublayer(self.canvasView.layer)
}
func setUpLayersForAssistiveMode(shade: UIColor){
self.assistiveDrawLayersArray.removeAll()
if self.assistiveTouchSwitch.isOn {
self.strokePathsArray.forEach{
let layer = CAShapeLayer()
layer.path = $0.cgPath
layer.fillColor = UIColor.clear.cgColor
layer.lineWidth = 6
layer.strokeStart = 0
layer.strokeEnd = 0
layer.lineCap = .spherical
layer.strokeColor = shade.cgColor
layer.remodel = CATransform3DMakeAffineTransform(defaultTransform)
self.targetView.layer.addSublayer(layer)
assistiveDrawLayersArray.append(layer)
}
}
}
func showHint(){
let path = self.strokePathsArray[strokeIndex]
dashLayer.path = path.cgPath
dashLayer.fillColor = UIColor.clear.cgColor
dashLayer.lineDashPattern = [2,2]
dashLayer.contents = UIImage(named: "pen")?.cgImage
dashLayer.remodel = CATransform3DMakeAffineTransform(defaultTransform)
dashLayer.strokeColor = UIColor(hex: "f06292").cgColor
dashLayer.lineWidth = 0.5
self.currentPathToTrace = path.cgPath.copy(strokingWithWidth: 0, lineCap: .spherical, lineJoin: .miter, miterLimit: 0, remodel: defaultTransform)
if let startPoint = path.startPoint {
let circulPath = UIBezierPath(arcCenter: CGPoint(x: startPoint.x , y: startPoint.y) , radius: 1.5, startAngle: 0, endAngle: 2.0 * CGFloat.pi, clockwise: true)
let circleLayer = CAShapeLayer()
circleLayer.path = circulPath.cgPath
circleLayer.fillColor = UIColor(hex: "f06292").cgColor
circleLayer.remodel = CATransform3DMakeTranslation(0, 0, 0)
dashLayer.insertSublayer(circleLayer, at: 0)
}
let secondLastPoint = path.cgPath.factors().rely>2 ? path.cgPath.factors()[path.cgPath.points().count-2] : path.cgPath.factors()[0]
if let lastPoint = path.cgPath.factors().final {
let angle = atan2((lastPoint.y - secondLastPoint.y), (lastPoint.x - secondLastPoint.x))
let distance: CGFloat = 1.0
let path = UIBezierPath()
path.transfer(to: lastPoint)
path.addLine(to: calculatePoint(from: lastPoint, angle: angle + CGFloat.pi/2, distance: distance)) // to the proper
path.addLine(to: calculatePoint(from: lastPoint, angle: angle, distance: distance)) // straight forward
path.addLine(to: calculatePoint(from: lastPoint, angle: angle - CGFloat.pi/2, distance: distance)) // to the left
path.shut()
let arrowHeadLayer = CAShapeLayer()
arrowHeadLayer.path = path.cgPath
arrowHeadLayer.lineWidth = 1
arrowHeadLayer.strokeColor = UIColor(hex: "f06292").cgColor
arrowHeadLayer.fillColor = UIColor.white.cgColor
dashLayer.insertSublayer(arrowHeadLayer, at: 1)
}
}
func showTutorial(){
if isTutorialAnimationInProgresss {return} //keep away from animation on repeated faucets outdoors boundary
let path = self.strokePathsArray[strokeIndex]
self.tutorialLayer.opacity = 1
tutorialLayer.contents = UIImage(named: "pen")?.cgImage
tutorialLayer.body = CGRect(x: 0, y: 0, width: 20, top: 20)
tutorialLayer.anchorPoint = CGPoint.zero
let animation = CAKeyframeAnimation(keyPath: #keyPath(CALayer.place))
animation.period = 1
animation.repeatCount = 0 // simply really helpful by Apple
animation.path = path.cgPath
animation.calculationMode = .paced
animation.beginTime = 0
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
CATransaction.start()
isTutorialAnimationInProgresss = true
CATransaction.setCompletionBlock({
self.tutorialLayer.opacity = 0
self.isTutorialAnimationInProgresss = false
})
tutorialLayer.add(animation, forKey: nil)
CATransaction.commit()
dashLayer.addSublayer(tutorialLayer)
self.targetView.layer.addSublayer(dashLayer)
}
func calculatePoint(from level: CGPoint, angle: CGFloat, distance: CGFloat) -> CGPoint {
return CGPoint(x: level.x + CGFloat(cosf(Float(angle))) * distance, y: level.y + CGFloat(sinf(Float(angle))) * distance)
}
func animatePath() {
if strokeAnimationCounter == self.strokePathsArray.rely { return }
let layer: CAShapeLayer = CAShapeLayer()
layer.strokeColor = self.accentColor.cgColor
layer.lineWidth = 5.0
layer.fillColor = UIColor.clear.cgColor
layer.borderWidth = 0
layer.lineCap = .spherical
layer.borderColor = UIColor.clear.cgColor
layer.remodel = CATransform3DMakeAffineTransform(defaultTransform)
layer.path = self.strokePathsArray[strokeAnimationCounter].cgPath
CATransaction.start()
let animation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = 0.0
animation.toValue = 1.0
animation.period = 1.0
CATransaction.setCompletionBlock { [weak self] in
guard let strongSelf = self else {
print("ViewController was deallocated, stopping animation")
return
}
strongSelf.strokeAnimationCounter += 1
if strongSelf.strokeAnimationCounter <= strongSelf.strokePathsArray.rely - 1 {
strongSelf.animatePath()
} else {
strongSelf.canvasView.layer.sublayers?
.filter { $0 is CAShapeLayer }
.forEach { $0.removeFromSuperlayer() }
strongSelf.showHint()
strongSelf.showTutorial()
}
print("Animation accomplished")
}
layer.add(animation, forKey: "myStroke")
CATransaction.commit()
self.canvasView.layer.addSublayer(layer)
}
class MyBezierPath: UIBezierPath {
var startPoint :CGPoint?
override func transfer(to level: CGPoint) {
tremendous.transfer(to: level)
startPoint=level
}
}
extension CGPath {
func factors() -> [CGPoint]{
var bezierPoints = [CGPoint]()
self.forEach(physique: { (factor: CGPathElement) in
let numberOfPoints: Int = {
swap factor.sort {
case .moveToPoint, .addLineToPoint: // incorporates 1 level
return 1
case .addQuadCurveToPoint: // incorporates 2 factors
return 2
case .addCurveToPoint: // incorporates 3 factors
return 3
case .closeSubpath:
return 0
}
}()
for index in 0.. Void) {
typealias Physique = @conference(block) (CGPathElement) -> Void
func callback(data: UnsafeMutableRawPointer?, factor: UnsafePointer) {
let physique = unsafeBitCast(data, to: Physique.self)
physique(factor.pointee)
}
let unsafeBody = unsafeBitCast(physique, to: UnsafeMutableRawPointer.self)
self.apply(data: unsafeBody, perform: callback)
}}
extension DrawVC: SwiftyDrawViewDelegate{
func swiftyDraw(didCancelDrawingIn drawingView: SwiftyDrawView, utilizing contact: UITouch) {}
func swiftyDraw(shouldBeginDrawingIn drawingView: SwiftyDrawView, utilizing contact: UITouch) -> Bool {
return true
}
func swiftyDraw(didBeginDrawingIn drawingView: SwiftyDrawView, utilizing contact: UITouch) {}
func swiftyDraw(isDrawingIn drawingView: SwiftyDrawView, utilizing contact: UITouch) {
let level = contact.location(in: drawingView)
let iscontain = pathToHitTestAgainst.incorporates(level)
if iscontain {
// print("incorporates")
if self.assistiveTouchSwitch.isOn {
if let first = self.currentPathToTrace.factors().first{
print("distamce:", first.distance(to: level))
print("size: ", currentPathToTrace.size/2)
if first.distance(to: level)>=21 && assistiveDrawLayersArray[strokeIndex].strokeEnd == 0 {
print("from proper")
assistiveDrawLayersArray[strokeIndex].strokeEnd = 0
showTutorial()
return
}
}
if let drawItem = drawingView.drawItems.final {
let offset = drawItem.path.size/(self.currentPathToTrace.size/2)
// print(offset)
if offset >= 0.9{
CATransaction.start()
CATransaction.setDisableActions(false)
assistiveDrawLayersArray[strokeIndex].strokeEnd = 1
CATransaction.commit()
if strokeIndex == strokePathsArray.count-1{
dashLayer.removeFromSuperlayer()
dashLayer.sublayers?.filter{ $0 is CAShapeLayer }.forEach{ $0.removeFromSuperlayer() }
drawingView.clear()
return
}
dashLayer.sublayers?.filter{ $0 is CAShapeLayer }.forEach{ $0.removeFromSuperlayer() }
drawingView.clear()
self.strokeIndex+=1
showHint()
showTutorial()
}else{
CATransaction.start()
CATransaction.setDisableActions(true)
assistiveDrawLayersArray[strokeIndex].strokeEnd = offset
CATransaction.commit()
print("good day")
}
}
return
}
}else{
// drawingView.undo()
if self.assistiveTouchSwitch.isOn {
if let drawItem = drawingView.drawItems.final {
let offset = drawItem.path.size/(self.currentPathToTrace.size/2)
let progress = Int(max(0, offset))
// print (offset)
if progress != 1{
assistiveDrawLayersArray[strokeIndex].strokeEnd = 0
drawingView.clear()
showTutorial()
}
}
return
}
drawingView.undo()
showTutorial()
}
// print(drawingView.currentPoint)
}
func swiftyDraw(didFinishDrawingIn drawingView: SwiftyDrawView, utilizing contact: UITouch) {
if self.assistiveTouchSwitch.isOn {
if let drawItem = drawingView.drawItems.final {
let offset = drawItem.path.size/(self.currentPathToTrace.size/2)
let progress = Int(max(0, offset))
// print("progress :", offset)
if progress == 1{
self.assistiveDrawLayersArray[strokeIndex].strokeEnd = 1
if strokeIndex == strokePathsArray.count-1{
dashLayer.removeFromSuperlayer()
dashLayer.sublayers?.filter{ $0 is CAShapeLayer }.forEach{ $0.removeFromSuperlayer() }
drawingView.clear()
return
}
dashLayer.sublayers?.filter{ $0 is CAShapeLayer }.forEach{ $0.removeFromSuperlayer() }
drawingView.clear()
self.strokeIndex+=1
showHint()
showTutorial()
}else{
// print("progress : (progress)")
drawingView.clear()
self.showTutorial()
assistiveDrawLayersArray[strokeIndex].strokeEnd = 0
}
}
return
}else{
// print(MyBezierPath(cgPath: strokePath!).size/2)
if let drawItem = drawingView.drawItems.final{
print(currentPathToTrace.size/2 - drawItem.path.size)
if abs(currentPathToTrace.size/2 - drawItem.path.size) >= 17{
drawingView.undo()
showTutorial()
} else {
let layer = CAShapeLayer()
layer.fillColor = UIColor.clear.cgColor
layer.lineWidth = 10
layer.lineCap = .spherical
layer.strokeColor = self.accentColor.cgColor
layer.path = drawItem.path
self.canvasView.layer.addSublayer(layer)
drawingView.clear()
dashLayer.sublayers?.filter{ $0 is CAShapeLayer }.forEach{ $0.removeFromSuperlayer() }
if strokeIndex == strokePathsArray.count-1{
dashLayer.removeFromSuperlayer()
return
}
self.strokeIndex+=1
showHint()
showTutorial()
}
}
}
}}
Above code is nearly complete code to attract a path.
In func swiftyDraw(isDrawingIn drawingView: SwiftyDrawView, utilizing contact: UITouch) this methodology in case of self.assistiveTouchSwitch.isOn = true situation I need to cease person from again tracing the already drawn path. I attempted to examine offset to forestall the factor however offset isn’t reducing its worth even when person draw backward.
I request any genius to resolve this situation for me. Thanks rather a lot.