Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get calligraphy line brush effect in swift using GraphicContext

I want to add calligraphy brush effect just like shown in below Image.for drawing I am using SwiftyDrawView.

enter image description here


Following is code snippets from SwiftyDraw

/// Overriding draw(rect:) to stroke paths

  override open func draw(_ rect: CGRect) {
        super.draw(rect)
        guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }

        for line in lines {
            context.setLineCap(.round)
            context.setLineJoin(.round)
            context.setLineWidth(line.brush.width)
            // set blend mode so an eraser actually erases stuff
            context.setBlendMode(line.brush.blendMode)
            context.setAlpha(line.brush.opacity)
            context.setStrokeColor(line.brush.color.cgColor)
            context.addPath(line.path)
            context.strokePath()
        }
    }

// TouchBegan

  override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard isEnabled, let touch = touches.first else { return }
        if #available(iOS 9.1, *) {
            guard allowedTouchTypes.flatMap({ $0.uiTouchTypes }).contains(touch.type) else { return }
        }
        guard delegate?.swiftyDraw(shouldBeginDrawingIn: self, using: touch) ?? true else { return }
        delegate?.swiftyDraw(didBeginDrawingIn: self, using: touch)

        setTouchPoints(touch, view: self)
        let newLine = Line(path: CGMutablePath(),
                           brush: Brush(color: brush.color, width: brush.width, opacity: brush.opacity, blendMode: brush.blendMode))
        newLine.path.addPath(createNewPath())
        lines.append(newLine)
        drawingHistory = lines // adding a new line should also update history
    }

and touchesMoved

 override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard isEnabled, let touch = touches.first else { return }
        if #available(iOS 9.1, *) {
            guard allowedTouchTypes.flatMap({ $0.uiTouchTypes }).contains(touch.type) else { return }
        }
        delegate?.swiftyDraw(isDrawingIn: self, using: touch)

        updateTouchPoints(for: touch, in: self)
        let newPath = createNewPath()
        if let currentPath = lines.last {
            currentPath.path.addPath(newPath)
        }
    }

Touch ended

override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard isEnabled, let touch = touches.first else { return }
        delegate?.swiftyDraw(didFinishDrawingIn: self, using: touch)
    }

and Touch cancel

override open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard isEnabled, let touch = touches.first else { return }
        delegate?.swiftyDraw(didCancelDrawingIn: self, using: touch)
    }

// and set TouchPint

private func setTouchPoints(_ touch: UITouch,view: UIView) {
        previousPoint = touch.previousLocation(in: view)
        previousPreviousPoint = touch.previousLocation(in: view)
        currentPoint = touch.location(in: view)
    }

// and updateTouchPoints

private func updateTouchPoints(for touch: UITouch,in view: UIView) {
        previousPreviousPoint = previousPoint
        previousPoint = touch.previousLocation(in: view)
        currentPoint = touch.location(in: view)
    }

and createNewPath

private func createNewPath() -> CGMutablePath {
        let midPoints = getMidPoints()
        let subPath = createSubPath(midPoints.0, mid2: midPoints.1)
        let newPath = addSubPathToPath(subPath)
        return newPath
    }

and calculateMidPoint

private func calculateMidPoint(_ p1 : CGPoint, p2 : CGPoint) -> CGPoint {
        return CGPoint(x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5);
    }

and getMidPoints

private func getMidPoints() -> (CGPoint,  CGPoint) {
        let mid1 : CGPoint = calculateMidPoint(previousPoint, p2: previousPreviousPoint)
        let mid2 : CGPoint = calculateMidPoint(currentPoint, p2: previousPoint)
        return (mid1, mid2)
    }

and createSubPath

private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
        let subpath : CGMutablePath = CGMutablePath()
        subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
        subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
        return subpath
    }

and addSubPathToPath

 private func addSubPathToPath(_ subpath: CGMutablePath) -> CGMutablePath {
        let bounds : CGRect = subpath.boundingBox
        let drawBox : CGRect = bounds.insetBy(dx: -2.0 * brush.width, dy: -2.0 * brush.width)
        self.setNeedsDisplay(drawBox)
        return subpath
    }
like image 509
Bhavesh.iosDev Avatar asked Jul 13 '19 11:07

Bhavesh.iosDev


1 Answers

I Found simple Solution :

 private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
        let subpath : CGMutablePath = CGMutablePath()
        subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
        subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
        for i   in 1 ..< 6 {
            subpath.addLines(between: [CGPoint(x: mid1.x + CGFloat(i), y: mid1.y + CGFloat(i)),CGPoint(x: mid2.x+CGFloat(i), y: mid2.y + CGFloat(i))])
         }
        return subpath
    }
like image 151
Bhavesh.iosDev Avatar answered Nov 19 '22 03:11

Bhavesh.iosDev