draw freehand shapes in swift - ios

I want to draw some shapes like someone's signature or some other freehand shapes.
I am new to UIBezierPath. I have tried following code but it didn't works as I want.
How could be it possible ?
TIA
let path = UIBezierPath()
path.move(to: from)
path.addLine(to: to)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)

Here is a basic drawing code, you should add UIImageView to paint on it, basically you need to paint a line between the previous touched point and the current point, using line cap .round
import UIKit
class BasicDrawingViewController: UIViewController {
var lastPoint = CGPoint.zero
var paintColor : UIColor = UIColor.black
var lineWidth : CGFloat = 20.0
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//Draw logic
extension BasicDrawingViewController{
func drawBetweenPoints(point1:CGPoint,point2:CGPoint){
UIGraphicsBeginImageContext(self.imageView.bounds.size)
let context = UIGraphicsGetCurrentContext()
self.imageView.image?.draw(in: self.imageView.bounds)
context?.move(to: point1)
context?.addLine(to: point2)
context?.setLineCap(.round)
context?.setStrokeColor(self.paintColor.cgColor)
context?.setLineWidth(lineWidth)
context?.strokePath()
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
debugPrint("Began")
if let touch = touches.first{
let point = touch.location(in: self.imageView)
self.drawBetweenPoints(point1: point, point2: point)
self.lastPoint = point
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
debugPrint("Move")
if let touch = touches.first{
let newPoint = touch.location(in: self.imageView)
self.drawBetweenPoints(point1: self.lastPoint, point2: newPoint)
self.lastPoint = newPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
debugPrint("Ended")
if let touch = touches.first{
let point = touch.location(in: self.imageView)
debugPrint(point)
}
}
}
Storyboard Setup
Result

Hello #Rajinder for this you have to get touch points, so you have to use
following methods.
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?)
{
let touch = event?.allTouches?.first
let touchLocation: CGPoint? = touch?.location(in: self.view)
//---Declare "from" globally as CGPoint
from = CGPoint(x: (touchLocation?.x)!, y: (touchLocation?.y)!)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = event?.allTouches?.first
let touchLocation: CGPoint? = touch?.location(in: self.view)
//--Get New touch point
let to = CGPoint(x: (touchLocation?.x)!, y: (touchLocation?.y)!)
//--Draw line
drawLineFromPoint(from: from, to: to, ofColor: UIColor.red, inView: self.view)
//--Save as older point
from = to
}
func drawLineFromPoint(from : CGPoint, to:CGPoint, ofColor lineColor: UIColor, inView view:UIView)
{
//design the path
let path = UIBezierPath()
path.move(to: from)
path.addLine(to: to)
//design path in layer
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)
}

Related

How to draw a line from one point to the next point in Swift

I would like to know how to draw a line from one point to another where the user touch the first point and the next point he touches, it create a line in between. I know that using UIBezierPath will be useful in here but I am still new to Swift and iOS platform so any guidance will helpful.
I have created a function called drawLineFromPoint but does not work for me.
import UIKit
import GLKit
class ViewController: GLKViewController {
private var context: EAGLContext?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
EAGLcontext()
// Gesture Code
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let position = touch.location(in: view)
var dot = UIView(frame: CGRect(x: position.x, y: position.y, width: 10, height: 10))
dot.backgroundColor = .red
view.addSubview(dot)
// View the x and y coordinates
print(position)
}
}
//Not sure how to do this part ???
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)
}
//Create EAGL Context for the GLKView
private func EAGLcontext() {
context = EAGLContext(api: .openGLES2)
EAGLContext.setCurrent(context)
if let view = self.view as? GLKView, let context = context {
view.context = context
delegate = self
}
}
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
//Set the color
glClearColor(0.85, 0.85, 0.85, 1.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
}
}
extension ViewController: GLKViewControllerDelegate {
func glkViewControllerUpdate(_ controller: GLKViewController) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Based On your code I changed some things and this code Works please check.
class ViewController: UIViewController {
var lastPosition: CGPoint?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Gesture Code
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
let position = touch.location(in: view)
if let lastPosition = self.lastPosition {
self.drawLineFromPoint(start: lastPosition, toPoint: position, ofColor: UIColor.red, inView: self.view)
}
self.lastPosition = position
// View the x and y coordinates
let dot = UIView(frame: CGRect(x: position.x, y: position.y, width: 10, height: 10))
dot.backgroundColor = .red
view.addSubview(dot)
print(position)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, let lastPosition = self.lastPosition{
let position = touch.location(in: view)
self.drawLineFromPoint(start: lastPosition, toPoint: position, ofColor: UIColor.red, inView: self.view)
self.lastPosition = position
let dot = UIView(frame: CGRect(x: position.x, y: position.y, width: 10, height: 10))
dot.backgroundColor = .red
view.addSubview(dot)
}
}
//Not sure how to do this part ???
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.lineWidth = 1.0
view.layer.addSublayer(shapeLayer)
}
}

Drawing a rectangle on UIImageView using Core Graphics

I want to let the User to draw a rectangle on UIImageView
I added two variables for first an last touch locations
I added this function:-
func draw(from: CGPoint, to: CGPoint) {
UIGraphicsBeginImageContext(imageView.frame.size)
context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(UIColor(red: 0, green: 0, blue: 0, alpha: 1.0).cgColor)
context?.setLineWidth(5.0)
let currentRect = CGRect(x: from.x,
y: from.y,
width: to.x - from.x,
height: to.y - from.y)
context?.addRect(currentRect)
context?.drawPath(using: .stroke)
context?.strokePath()
imageView.image?.draw(in: self.imageView.frame)
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
I add the method to (touchMoved) it draws many rectangles
I add the method to (touchEnded) it draws one, but it does not appear when the user move the touch
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
firstTouchLocation = touch.location(in: self.view)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
lastTouchLocation = touch.location(in: self.view)
draw(from: firstTouchLocation, to: lastTouchLocation)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
lastTouchLocation = touch.location(in: self.view)
draw(from: firstTouchLocation, to: lastTouchLocation)
}
}
I want to let the user extend the rectangle when touchMoved and draw when the touchEnded.
You are replacing your image with a new image composed of the previous image plus a rectangle drawn over it. Rather than drawing the image from the image view, draw the original image.
Alternatively, you could render the the rectangle as a shape layer and just update that shape layer's path:
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
private let shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
_shapeLayer.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
_shapeLayer.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
_shapeLayer.lineWidth = 3
return _shapeLayer
}()
private var startPoint: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.addSublayer(shapeLayer)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startPoint = touches.first?.location(in: imageView)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }
let point: CGPoint
if let predictedTouch = event?.predictedTouches(for: touch)?.last {
point = predictedTouch.location(in: imageView)
} else {
point = touch.location(in: imageView)
}
updatePath(from: startPoint, to: point)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let startPoint = startPoint, let touch = touches.first else { return }
let point = touch.location(in: imageView)
updatePath(from: startPoint, to: point)
imageView.image = imageView.snapshot(afterScreenUpdates: true)
shapeLayer.path = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
shapeLayer.path = nil
}
private func updatePath(from startPoint: CGPoint, to point: CGPoint) {
let size = CGSize(width: point.x - startPoint.x, height: point.y - startPoint.y)
let rect = CGRect(origin: startPoint, size: size)
shapeLayer.path = UIBezierPath(rect: rect).cgPath
}
}
Where:
extension UIView {
func snapshot(afterScreenUpdates: Bool = false) -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
This is not only simpler, but more efficient, too.
That yields:
By the way, I might suggest using predictive touches in touchesMoved. On a device (not the simulator) that can yield a slightly more responsive UI.

Drawing a line in Swift 3.x with UIBezierPath

I am trying to draw a line on an UIView with an UIBezierpath. I think I am missing something, but wasn't able to find it out.
Here is my code:
// Code for touch recognition
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = false
if let touch = touches.first as? UITouch? {
lastPoint = (touch?.location(in: fullview))!
//print(lastPoint)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first as? UITouch? {
let currentPoint = touch?.location(in: fullview)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint!)
lastPoint = currentPoint!
//print(lastPoint)
//print("touch moved")
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
}
//print("touch ended")
}
//code for drawing
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint){
UIGraphicsBeginImageContext(fullview.frame.size)
let context = UIGraphicsGetCurrentContext()
let aPath = UIBezierPath()
//aPath.move(to: fromPoint)
//aPath.addLine(to: toPoint)
aPath.lineWidth=10.0
aPath.lineJoinStyle = .round
aPath.move(to: CGPoint(x:15,y:15))
aPath.addLine(to: CGPoint(x:80,y:80))
aPath.addClip()
aPath.close()
UIColor.green.set()
aPath.stroke()
//print("drawline")
//print("Frompoint = ",fromPoint)
//print("topoint = ",toPoint)
/* context?.setLineCap(.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
context?.setBlendMode(.normal)
context?.beginPath()
context?.move(to: fromPoint)
context?.addLine(to: toPoint)
context?.closePath()
context?.strokePath()*/
//let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
fullview.setNeedsDisplay()
}
As you can see, I tried it also with the context, but it wasn't working too.
I use this to draw a line:
let doYourPath = UIBezierPath(rect: CGRect(x: xPos, y: yPos, width: yourWidth, height: yourHeight))
let layer = CAShapeLayer()
layer.path = doYourPath.cgPath
layer.strokeColor = UIColor.white.cgColor
layer.fillColor = UIColor.white.cgColor
self.view.layer.addSublayer(layer)
Hope this help you out. This is just one the way to draw a line in swift.
Well, you are drawing into an image context (an offscreen bitmap), not into the view. That is quite likely not what you want? Make sure you have read iOS Drawing Concepts.
Your UIView subclass should probably just track the positions of the touches (like in a var touchedPositions = [CGPoint]()) and call setNeedsDisplay().
Then implement the func draw(_ rect: CGRect) method in your subclass. Within this method, create your path and draw it according to the positions you tracked (w/o creating a new context).

Draw horizontal or vertical line using touch in iOS

I am working on a project where I want that if user touch move in horizontal direction then horizontal line should draw and of user touch move in vertical direction then vertical line should draw. Kindly suggest some solution using Swift.
I tried below. But this is drawing free line.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch: AnyObject? = touches.first
let lastPoint = touch!.previousLocation(in: holderView)
path.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch: AnyObject? = touches.first
let currentPoint = touch!.location(in: holderView)
path.addLine(to: CGPoint(x: currentPoint.x, y: currentPoint.y))
//Design path in layer
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.orange.cgColor
shapeLayer.lineWidth = 20.0
holderView.layer.addSublayer(shapeLayer)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
path=UIBezierPath()
}
Try this.
class DrawingView: UIView {
var path = UIBezierPath()
var initialLocation = CGPoint.zero
var finalLocation = CGPoint.zero
var shapeLayer = CAShapeLayer()
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView(){
self.layer.addSublayer(shapeLayer)
self.shapeLayer.lineWidth = 20
self.shapeLayer.strokeColor = UIColor.blue.cgColor
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let location = touches.first?.location(in: self){
initialLocation = location
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
if let location = touches.first?.location(in: self){
let dx = location.x - initialLocation.x
let dy = location.y - initialLocation.y
finalLocation = abs(dx) > abs(dy) ? CGPoint(x: location.x, y: initialLocation.y) : CGPoint(x: initialLocation.x, y: location.y)
path.removeAllPoints()
path.move(to: initialLocation)
path.addLine(to: finalLocation)
shapeLayer.path = path.cgPath
}
}
}

Why when I draw something it doesn't appear where my finger was moving?

The line appears a couple inches away from where I wanted it to be drawn. Im making a drawing app in SpriteKit and I try to draw something on the bottom of my UIImageView but it appears couple inches away from where my finger was. Why does that happen?
class GameScene: SKScene {
var drawImageView: UIImageView! = UIImageView()
var lastPoint = CGPoint.zero
var swiped = false
override func didMove(to view: SKView) {
drawImageView.frame = previewCamera.bounds
previewCamera.addSubview(drawImageView)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = false
if let touch = touches.first {
lastPoint = touch.location(in: self.view)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first {
let currentPoint = touch.location(in: self.view)
draw(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
}
}
func draw(fromPoint:CGPoint,toPoint:CGPoint) {
UIGraphicsBeginImageContext((self.view?.frame.size)!)
drawImageView.image?.draw(in: CGRect(x: 0, y: 0, width: (self.view?.frame.width)!, height: (self.view?.frame.height)!))
let context = UIGraphicsGetCurrentContext()
context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
context?.setBlendMode(CGBlendMode.normal)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(7)
context?.setStrokeColor(UIColor(red: red, green: green, blue: blue, alpha: 1.0).cgColor)
context?.strokePath()
drawImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
draw(fromPoint: lastPoint, toPoint: lastPoint)
}
}
}

Resources