Image gets blurry and zoomed out when erasing - ios

I am using functionality of erasing image and my code is like below. You can see Video HERE. Here current_sticker_img is my Imageview
NOTE: I am also using Pan Gesture for zooming image
//MARK: Eraser touch event
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if btn_eraser.isSelected == true || btn_repaint.isSelected == true{
let touch : UITouch = touches.first!
lastpoint = touch.location(in: current_sticker_img)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if btn_eraser.isSelected == true || btn_repaint.isSelected == true{
let touch = touches.first!
let currentPoint : CGPoint = touch.location(in: current_sticker_img)
let isPointInsideView = current_sticker_img.point(inside: currentPoint, with: event)
if isPointInsideView
{
UIGraphicsBeginImageContext(current_sticker_img.frame.size)
// UIGraphicsBeginImageContextWithOptions(CGSize(width: (current_sticker_img.image?.size.width)!, height: (current_sticker_img.image?.size.height)!), false, (current_sticker_img.image?.scale)!)
current_sticker_img
.image?.draw(in: CGRect(x: 0, y: 0, width: (current_sticker_img.frame.size.width), height: (current_sticker_img.frame.size.height)))
UIGraphicsGetCurrentContext()!.setLineCap(.round)
UIGraphicsGetCurrentContext()?.setLineWidth(Current_slider_value_for_Eraser)
UIGraphicsGetCurrentContext()?.setShadow(offset: CGSize(width: CGFloat(0), height: CGFloat(0)), blur: 0, color: UIColor.clear.cgColor )
UIGraphicsGetCurrentContext()!.setBlendMode(.clear)
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: CGFloat(lastpoint.x), y: CGFloat(lastpoint.y)))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: CGFloat(currentPoint.x), y: CGFloat(currentPoint.y)))
UIGraphicsGetCurrentContext()!.strokePath()
current_sticker_img.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
lastpoint = currentPoint
}
}
}
What I am doing wrong ? Please help me with it.
Thank you

Related

CGContext & CGMutablePath drawing line But it's be reversed

I use CgContext & CGMutablePath to Draw objects but the image that drawed be reversed.
I need Draw Object to CoreMl drawing classification project.
I use Touchs events to draw object
But when I draw to top direction if will be to bottom direction.
I want the line is correct direction
I learn from link
func drawContextLine(from fromPoint: CGPoint, to toPoint:CGPoint) ->CGImage?{
let grayscale = CGColorSpaceCreateDeviceGray()
if contextDraw == nil{
contextDraw = CGContext(
data:nil, width:256, height:256, bitsPerComponent:8, bytesPerRow:0,
space:grayscale, bitmapInfo:CGImageAlphaInfo.none.rawValue)
}
// intermediate_bitmap_context?.setStrokeColor(
// red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
contextDraw?.setStrokeColor(UIColor.white.cgColor)
let path = CGMutablePath()
path.move(to: fromPoint)
path.addLine(to: toPoint)
contextDraw?.setLineWidth(5.0)
contextDraw?.beginPath()
contextDraw?.addPath(path)
contextDraw?.strokePath()
guard let imgCg = contextDraw?.makeImage() else {
return nil
}
imgViewPridict.image = UIImage.init(cgImage: imgCg)
return contextDraw?.makeImage()
}
touch begin
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first{
lastPoint = touch.location(in: self.view)
}
}
touch move
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard touches.first != nil else {
return
}
if let touch = touches.first {
let currentPoint = touch.location(in: self.view)
drawLine(from: lastPoint, to: currentPoint)
drawContextLine(from: lastPoint, to: currentPoint)
lastPoint = currentPoint
}
}
Try the code snippet below.
func drawContextLine(from fromPoint: CGPoint, to toPoint:CGPoint) ->CGImage?{
let grayscale = CGColorSpaceCreateDeviceGray()
if contextDraw == nil{
contextDraw = CGContext(
data:nil, width:256, height:256, bitsPerComponent:8, bytesPerRow:0,
space:grayscale, bitmapInfo:CGImageAlphaInfo.none.rawValue)
}
contextDraw?.setStrokeColor(UIColor.white.cgColor)
contextDraw?.saveGState()
contextDraw?.translateBy(x: 0, y: CGFloat(integerLiteral: contextDraw?.height ?? 0))
contextDraw?.scaleBy(x: 1.0, y: -1.0)
contextDraw?.setLineWidth(5.0)
contextDraw?.move(to: CGPoint(x: 100, y: 100))
contextDraw?.addLine(to: CGPoint(x: 150, y: 150))
contextDraw?.strokePath()
contextDraw?.restoreGState()
guard let imgCg = contextDraw?.makeImage() else {
return nil
}
imgViewPridict.image = UIImage.init(cgImage: imgCg)
return contextDraw?.makeImage()
}
You may need to inverse the content drawn on cgContext by applying AffineTransform to it.

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 on UIImage and save original size

I need to draw lines on UIImage (image load from file system) and save it, but I have a problem. When I trying to save context to file, saved image have size equal imageView size (If I set "tempImageView.frame.size" to UIGraphicsBeginImageContextWithOptions and draw(in:)). If I change "size" in code and set tempImageView.image?.size saved image size is OK, but drawing is not work good (lines draw with offset, and lines view badly)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.swiped = false
if let touch = touches.first {
lastPoint = touch.location(in: self.view)
print(lastPoint)
}
}
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions((tempImageView.image?.size)!, false, scale)
let context = UIGraphicsGetCurrentContext()
tempImageView.image?.draw(in: CGRect(origin: CGPoint.zero, size: (tempImageView.image?.size)!))
//let context = UIGraphicsGetCurrentContext()
print("context size = \(context)")
print("fromPoint = \(fromPoint), toPoint = \(toPoint)")
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(5)
context?.setStrokeColor(UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor)
context?.strokePath()
tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
self.newImage = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()!)
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first {
let currentPoint = touch.location(in: self.view)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
print(lastPoint)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
}
}

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.

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