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.
Related
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.
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()
}
So in my swift app I'm allowing the user to paint with there finger on touch. I'm using cgcontext to do this. After the user lifts the finger and the touch ends I am dynamically adding a visualeffect view on top of the shape with the same height and width of the drawn shape.
What i want to do next is use the shape as a mask for visualeffect view. The problem right now is if i try to mask the visualeffect view with the shape. the masked view does not show unless the origin point of the shape is (0,0). Here is a link to how i'm currently attempting to implementing this (https://pastebin.com/JaM9kx4G)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = false
if let touch = touches.first as? UITouch {
if (touch.view == sideView){
return
}
tempPath = UIBezierPath()
lastPoint = touch.location(in: view)
tempPath.move(to:lastPoint)
}
}
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
tempPath.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
UIGraphicsBeginImageContext(view.frame.size)
let context = UIGraphicsGetCurrentContext()
otherImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
// 2
context!.move(to: CGPoint(x:fromPoint.x,y:fromPoint.y))
context!.addLine(to: CGPoint(x:toPoint.x, y:toPoint.y))
// 3
context!.setLineCap(CGLineCap.round)
context!.setLineWidth(brushWidth)
context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
context!.setBlendMode(CGBlendMode.normal)
// 4
context!.strokePath()
// 5
otherImageView.image = UIGraphicsGetImageFromCurrentImageContext()
otherImageView.alpha = opacity
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first as? UITouch {
let currentPoint = touch.location(in: view)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
// 7
lastPoint = currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
tempPath.close()
let tempImage = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
UIGraphicsBeginImageContext(tempImage.frame.size)
tempImage.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: CGBlendMode.normal, alpha: 1.0)
otherImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: CGBlendMode.normal, alpha: opacity)
let context = UIGraphicsGetCurrentContext()
let image = UIGraphicsGetImageFromCurrentImageContext()
tempImage.image = image
otherImageView.image = nil
imageView.addSubview(tempImage)
let blur = VisualEffectView()
blur.frame = CGRect(x:tempPath.bounds.origin.x,y:tempPath.bounds.origin.y, width:tempPath.bounds.width, height:tempPath.bounds.height)
blur.blurRadius = 5
blur.layer.mask = tempImage.layer
}
I have a simple view controller where the entire screen is an imageView. The imageView has its content mode set to UIViewContentMode.scaleAspectFill. I can draw on the picture, but as soon as I do the image shrinks horizontally. This messing up the quality of the image and I'm not sure exactly why it's happening.
I looked at this question, but I didn't understand the only answer. I think the issue is with the way I am drawing and that rect is shrinking the image.
class AnnotateViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var lastPoint = CGPoint.zero
var fromPoint = CGPoint()
var toPoint = CGPoint.zero
var brushWidth: CGFloat = 5.0
var opacity: CGFloat = 1.0
#IBAction func startDrawing(_ sender: Any) {
self.isDrawing = !self.isDrawing
if isDrawing {
self.imageView.isUserInteractionEnabled = true
showColorButtons()
}
else {
self.imageView.isUserInteractionEnabled = false
hideColorButtons()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
lastPoint = touch.preciseLocation(in: self.imageView)
}
}
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, false, 0.0)
imageView.image?.draw(in: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height))
let context = UIGraphicsGetCurrentContext()
context?.move(to: fromPoint)
context?.addLine(to: toPoint)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(red: 255, green: 0, blue: 0, alpha: 1.0)
context?.setBlendMode(CGBlendMode.normal)
context?.strokePath()
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
imageView.alpha = opacity
UIGraphicsEndImageContext()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let currentPoint = touch.preciseLocation(in: self.imageView)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
}
}
}
UPDATE - SOLUTION
I finally got back to working on this problem. Looking at this question, I was able to use the accepted answer to solve my problem. Below is my updated code:
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, true, 0.0)
let aspect = imageView.image!.size.width / imageView.image!.size.height
let rect: CGRect
if imageView.frame.width / aspect > imageView.frame.height {
let height = imageView.frame.width / aspect
rect = CGRect(x: 0, y: (imageView.frame.height - height) / 2,
width: imageView.frame.width, height: height)
} else {
let width = imageView.frame.height * aspect
rect = CGRect(x: (imageView.frame.width - width) / 2, y: 0,
width: width, height: imageView.frame.height)
}
imageView.image?.draw(in: rect)
let context = UIGraphicsGetCurrentContext()
context?.move(to: fromPoint)
context?.addLine(to: toPoint)
context?.setLineCap(CGLineCap.round)
context?.setLineWidth(brushWidth)
context?.setStrokeColor(red: self.red, green: self.green, blue: self.blue, alpha: 1.0)
context?.setBlendMode(CGBlendMode.normal)
context?.strokePath()
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
annotatedImage = imageView.image
imageView.alpha = opacity
UIGraphicsEndImageContext()
}
This line:
imageView.image?.draw(in: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height))
Draws the image at the aspect ratio of the screen, but your image is probably not at the same aspect ratio (the ratio of width to height).
I think this might be useful: UIImage Aspect Fit when using drawInRect?
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()
}
}