Draw lines in swift 3.0 - ios

I used the following code to no avail in Swift 3.0 - it gives me a blank screen on the simulator - I don't know what's happening.
import UIKit
class DrawExample: UIView {
override func draw( _ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context!.setLineWidth(3.0)
context!.setStrokeColor(UIColor.purple.cgColor)
//make and invisible path first then we fill it in
context!.move(to: CGPoint(x: 50, y: 60))
context!.addLine(to: CGPoint(x: 250, y:320))
context!.strokePath()
}

Update your class with below:
import UIKit
class DrawExample: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw( _ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context!.setLineWidth(3.0)
context!.setStrokeColor(UIColor.purple.cgColor)
//make and invisible path first then we fill it in
context!.move(to: CGPoint(x: 50, y: 60))
context!.addLine(to: CGPoint(x: 250, y:320))
context!.strokePath()
}
}
Now make an instance on the view controller and add it to view. Check the below code for it.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let draw = DrawExample(frame: self.view.bounds)
view.addSubview(draw)
}
}

Drawing in Swift 4.1
let lastPoint = CGPoint.zero
let red: CGFloat = 0.0
let green: CGFloat = 0.0
let blue: CGFloat = 0.0
let brushWidth: CGFloat = 10.0
let opacity: CGFloat = 1.0
var isSwiping:Bool!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
isSwiping = false
if let touch = touches.first{
lastPoint = touch.location(in: imgViewDraw)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
isSwiping = true;
if let touch = touches.first{
let currentPoint = touch.location(in: imgViewDraw)
UIGraphicsBeginImageContext(self.imgViewDraw.frame.size)
self.imgViewDraw.image?.draw(in: CGRect(x:0, y:0,width:self.imgViewDraw.frame.size.width, height:self.imgViewDraw.frame.size.height))
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: currentPoint.x, y: currentPoint.y))
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
UIGraphicsGetCurrentContext()?.setLineWidth(self.brushWidth)
UIGraphicsGetCurrentContext()?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
UIGraphicsGetCurrentContext()?.strokePath()
self.imgViewDraw.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
lastPoint = currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!isSwiping) {
// This is a single touch, draw a point
UIGraphicsBeginImageContext(self.imgViewDraw.frame.size)
self.imgViewDraw.image?.draw(in: CGRect(x:0, y:0,width:self.imgViewDraw.frame.size.width, height:self.imgViewDraw.frame.size.height))
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
UIGraphicsGetCurrentContext()?.setLineWidth(self.brushWidth)
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
UIGraphicsGetCurrentContext()?.strokePath()
self.imgViewDraw.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}

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)
}
}

How to compare touch-drawn path with a given bezier path?

I'm developing simple kids alphabets drawing app in iOS. I have created bezier path of a letter with dashed line. If kids draw over the bezier path, I have to detect whether they drawing correctly or not. I'm allowing them to draw only over that path. I don't know to detect whether they are drawing correctly or not and whether they completed it correctly.
// Created layer for path
let font = UIFont.systemFont(ofSize: 350)
var unichars = [UniChar](selectedText.utf16)
var glyphs = [CGGlyph](repeating: 0, count: unichars.count)
let gotGlyphs = CTFontGetGlyphsForCharacters(font, &unichars, &glyphs, unichars.count)
if gotGlyphs {
let cgpath = CTFontCreatePathForGlyph(font, glyphs[0], nil)!
var inverse = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -cgpath.boundingBox.height - 1)
let path = UIBezierPath(cgPath: cgpath.copy(using: &inverse)!)
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
shapeLayer.lineDashPattern = [7,3]
mainImageView.layer.addSublayer(shapeLayer)
}
//Allowing user to draw only over letter path
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
guard let point = touch?.location(in: self.mainImageView) else {
return
}
if let path = shapeLayer.path, path.contains(point) {
lastPoint = point
}
}
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
// 1
UIGraphicsBeginImageContext(drawPadView.frame.size)
let context = UIGraphicsGetCurrentContext()
tempImgView.image?.draw(in: CGRect(x: 0, y: 0, width: drawPadView.frame.size.width, height: drawPadView.frame.size.height))
// 2
context?.move(to: toPoint)
context?.addLine(to: toPoint)
// 3
context?.setLineCap(.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(UIColor(displayP3Red: red, green: green, blue: blue, alpha: 1.0).cgColor)
context?.setBlendMode(.normal)
// 4
context?.strokePath()
// 5
tempImgView.image = UIGraphicsGetImageFromCurrentImageContext()
tempImgView.alpha = opacity
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
let touch = touches.first
guard let point = touch?.location(in: self.mainImageView) else {
return
}
if let path = shapeLayer.path, path.contains(point) {
drawLineFrom(fromPoint: lastPoint, toPoint: point)
lastPoint = point
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
}
// Merge tempImageView into mainImageView
UIGraphicsBeginImageContext(drawPadView.frame.size)
mainImageView.image?.draw(in: CGRect(x: 0, y: 0, width: drawPadView.frame.size.width, height: drawPadView.frame.size.height), blendMode: .normal, alpha: opacity)
tempImgView.image?.draw(in: CGRect(x: 0, y: 0, width: drawPadView.frame.size.width, height: drawPadView.frame.size.height), blendMode: .normal, alpha: opacity)
mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
Please suggest me how to verify user completed drawing and check whether they drawn correctly or not.

iOS: Remove last image in array after touchesEnded has been completed

I am building an iOS drawing application and I am trying to implement a undo button to remove the line drawn by the user. I've created an array of images in a variable:
var images = [UIImage]()
Each time the user swipes and removes the finger from the screen a new image is created in the touchesEnded function. I think I need to remove the last image to get the previous image in an IBAction which will act as a undo button.
Here's a sample of my current code:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
guard touch.view != floaty else { return }
swiped = false
if let touch = touches.first{
postBtn.isEnabled = true
postBtn.setImage(UIImage(named: "done_icon_moved"), for: .normal)
dismissBtn.isEnabled = true
undoBtn.isEnabled = true
undoBtn.setImage(UIImage(named: "undo_icon_moved"), for: .normal)
lastPoint = touch.location(in: self.view)
}
}
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
// 1
UIGraphicsBeginImageContext(view.frame.size)
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
let context = UIGraphicsGetCurrentContext()
// 2
context?.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
context?.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
// 3
context?.setBlendMode(CGBlendMode.normal)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(UIColor(red:red, green:green, blue:blue, alpha: 1.0).cgColor)
// 4
context?.strokePath()
// 5
tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
tempImageView.alpha = opacity
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first {
let currentPoint = touch.location(in: view)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
// draw a single point
drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
}
// Merge tempImageView into mainImageView
UIGraphicsBeginImageContext(mainImageView.frame.size)
mainImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height),blendMode: CGBlendMode.normal, alpha: 1.0)
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: CGBlendMode.normal, alpha: opacity)
mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
tempImageView.image = nil
print("touch ended")
}
Check if image array have objects or not before remove last object.
if images.count != 0 {
images.removeLast()
}

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.

Drawn lines with touch location shrinking and fading into background Swift 3.0

I am experiencing a weird bug on Swift 3.0 whereby I am successfully drawing into a UIImageView that is located inside a UIView, however as I draw the previously drawn line begins to fade and shrink into the top of UIImageView and becomes extremely blurry as shown in the attached images.
Beginning of Drawing, Image becoming blurry.
I would appreciate any help/guidance that could be provided.
class DrawView: UIView {
var drawArea: UIImageView!
var clearDrawArea: UIButton!
var lastTouch = CGPoint.zero
init(){
super.init(frame: CGRect(x: UIScreen.main.bounds.width*0.135, y:UIScreen.main.bounds.height*0.05, width: UIScreen.main.bounds.width*0.8, height: UIScreen.main.bounds.height*0.9))
drawArea = UIImageView(frame: CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.bounds.width*0.9, height: self.bounds.height*0.86))
drawArea.backgroundColor = UIColor.white
let buttonWidth = self.bounds.width*0.2
let buttonHeight = self.bounds.height*0.1
clearDrawArea = UIButton(frame: CGRect(x: (self.bounds.width*0.61)-(buttonWidth/2), y: self.bounds.height*0.94, width: buttonWidth, height: buttonHeight))
clearDrawArea.setTitle("Clear Draw Area", for: .normal)
clearDrawArea.backgroundColor = UIColor(red: 49/255, green: 200/255, blue: 134/255, alpha: 255)
clearDrawArea.addTarget(self, action: #selector(resetImage), for: .touchUpInside)
self.addSubview(clearDrawArea)
self.addSubview(drawArea)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first{
lastTouch = firstTouch.location(in: drawArea)
}
}
func drawLines(_ from: CGPoint, to: CGPoint){
UIGraphicsBeginImageContext(drawArea.frame.size)
let context = UIGraphicsGetCurrentContext()
context?.move(to: CGPoint(x: from.x, y: from.y))
context?.addLine(to: CGPoint(x: to.x, y: to.y))
context?.setLineCap(.round)
context?.setLineWidth(3)
context?.setStrokeColor(UIColor.black.cgColor)
context?.strokePath()
drawArea.image?.draw(in: drawArea.bounds)
drawArea.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first{
let touchLocation = firstTouch.location(in: drawArea)
drawLines(lastTouch, to: touchLocation)
lastTouch = touchLocation
}
}
func resetImage(){
drawArea.image = nil
}
}
Changed your code a bit try the following..
class DrawView: UIView {
var drawArea: UIImageView!
var clearDrawArea: UIButton!
var lastTouch = CGPoint.zero
let pathAnimation: CABasicAnimation = CABasicAnimation(keyPath:"animation")
let tempPathLayer: CAShapeLayer = CAShapeLayer()
var lineLayer:CAShapeLayer=CAShapeLayer()
init(){
super.init(frame: CGRect(x: UIScreen.main.bounds.width*0.135, y:UIScreen.main.bounds.height*0.05, width: UIScreen.main.bounds.width*0.8, height: UIScreen.main.bounds.height*0.9))
drawArea = UIImageView(frame: CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.bounds.width*0.9, height: self.bounds.height*0.86))
drawArea.backgroundColor = UIColor.white
let buttonWidth = self.bounds.width*0.2
let buttonHeight = self.bounds.height*0.1
clearDrawArea = UIButton(frame: CGRect(x: (self.bounds.width*0.61)-(buttonWidth/2), y: self.bounds.height*0.94, width: buttonWidth, height: buttonHeight))
clearDrawArea.setTitle("Clear Draw Area", for: .normal)
clearDrawArea.backgroundColor = UIColor(red: 49/255, green: 200/255, blue: 134/255, alpha: 255)
clearDrawArea.addTarget(self, action: #selector(resetImage), for: .touchUpInside)
self.addSubview(clearDrawArea)
self.addSubview(drawArea)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first{
lastTouch = firstTouch.location(in: drawArea)
}
}
func drawLines(_ from: CGPoint, to: CGPoint){
UIGraphicsBeginImageContext(drawArea.frame.size)
//let context = UIGraphicsGetCurrentContext()
let tempPath: UIBezierPath = UIBezierPath()
var lineLayer:CAShapeLayer=CAShapeLayer()
tempPath.move(to: from)
tempPath.addLine(to: to)
let strokeColor = UIColor.red
UIColor.blue.setFill()
strokeColor.setStroke()
let tempPathLayer: CAShapeLayer = CAShapeLayer()
tempPathLayer.frame = self.bounds
tempPathLayer.position=self.layer.position
tempPathLayer.strokeColor = UIColor.green.cgColor
tempPathLayer.fillColor = UIColor.red.cgColor
tempPathLayer.lineWidth = 1.0
tempPathLayer.path = tempPath.cgPath
lineLayer=tempPathLayer
pathAnimation.beginTime=CACurrentMediaTime()
pathAnimation.duration = 2
pathAnimation.fromValue = NSNumber(value: 0.0)
pathAnimation.toValue = NSNumber(value:1.0)
lineLayer.add(pathAnimation, forKey: "animation")
self.layer.addSublayer(lineLayer)
drawArea.image?.draw(in: drawArea.bounds)
drawArea.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first{
let touchLocation = firstTouch.location(in: drawArea)
drawLines(lastTouch, to: touchLocation)
lastTouch = touchLocation
}
}
func resetImage(){
drawArea.image = nil
}
}
I have finally got it to work properly. I took advantage of your idea to use bezier paths instead and modified it to limit the drawing scope to the UIView.
class DrawView: UIView {
var drawArea: UIImageView!
var clearDrawArea: UIButton!
var lastTouch = CGPoint.zero
var context: CGContext!
var path: UIBezierPath!
init(){
context = UIGraphicsGetCurrentContext()
path = UIBezierPath()
super.init(frame: CGRect(x: UIScreen.main.bounds.width*0.135, y:UIScreen.main.bounds.height*0.05, width: UIScreen.main.bounds.width*0.8, height: UIScreen.main.bounds.height*0.9))
drawArea = UIImageView(frame: CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.bounds.width*0.9, height: self.bounds.height*0.86))
drawArea.backgroundColor = UIColor.white
let buttonWidth = self.bounds.width*0.2
let buttonHeight = self.bounds.height*0.1
clearDrawArea = UIButton(frame: CGRect(x: (self.bounds.width*0.61)-(buttonWidth/2), y: self.bounds.height*0.94, width: buttonWidth, height: buttonHeight))
clearDrawArea.setTitle("Clear Draw Area", for: .normal)
clearDrawArea.backgroundColor = UIColor(red: 49/255, green: 200/255, blue: 134/255, alpha: 255)
clearDrawArea.addTarget(self, action: #selector(resetImage), for: .touchUpInside)
self.addSubview(clearDrawArea)
self.addSubview(drawArea)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first{
lastTouch = firstTouch.location(in: drawArea)
}
}
func drawLines(_ from: CGPoint, to: CGPoint){
UIGraphicsBeginImageContext(drawArea.frame.size)
path.move(to: CGPoint(x: from.x, y: from.y))
path.addLine(to: CGPoint(x: to.x, y: to.y))
UIColor.black.setStroke()
path.lineWidth = 3
path.stroke()
context?.saveGState()
context?.addPath(path.cgPath)
context?.closePath()
context?.restoreGState()
drawArea.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
let touchLocation = firstTouch.location(in: drawArea)
drawLines(lastTouch, to: touchLocation)
lastTouch = touchLocation
}
}
func resetImage(){
path.removeAllPoints()
drawArea.image = nil
}
}
I also had this issue. I fixed it by altering the constraints so that the view was pinned to the view controller. I originally had an aspect ratio, and it was causing this problem in the 6/7 size, but not the 5/SE size or the PLUS sizes. this may be why upon testing the code that another answer submitted, it worked for them but not for you. if the uiView and the drawingarea are not aligned and sizing correctly, it will distort your drawing/cause 'striations' big lines that seem to come from no where/only draw on certain areas/you will touch in one place and it will draw somewhere else.
I recommend testing all screen sizes to ensure your functionality works on all of them and looks correct with the constraint settings you have.

Resources