IOS Save image with name - ios

i have an app that takes a picture and merges it with another image. I want to be able to display al the images taken in the app (and only my app) and display them in a gallery view. the problem I am having is in assigning the image a name.
I have tried a number of different ways
First Attempt
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil)
this saved the image but i do not know how i can retrieve it
Second Attempt
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(newImage)
}, completionHandler: { (success, error) -> Void in
if success {
//image saved to photos library.
print("image saved")
}
});
Same Problem as first attempt
Third Attempt
let date :NSDate = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'_'HH:mm:ss"
dateFormatter.timeZone = NSTimeZone(name: "GMT")
let imageName = "\(dateFormatter.stringFromDate(date)).jpg"
//var paths: [AnyObject] = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)
var documentsDirectoryPath = getDocumentsURL().relativePath
documentsDirectoryPath! += imageName
let settingsData: NSData = UIImageJPEGRepresentation(newImage, 1.0)!
settingsData.writeToFile(documentsDirectoryPath!, atomically: true)
This seems to have the most promise but the file does bot save
I have looked at various answers on here but I still cant identify a solution

Add a slash as first character of the imageName string like this:
let imageName = "/\(dateFormatter.stringFromDate(date)).jpg"
otherwise the path would be "..../Documentsmyimagename" but it should be "..../Documents/myimagename"
i.e. without the additional slash the image is saved to the folder above "Documents"
I also recomend not to use the colon character in filenames, on a mac the colon is not allowed and is replaced automatically.
This is the code I tested in a new XCode project and that worked for me (I put a file "myimage.png" in the root folder of the project):
let newImage = UIImage.init(named: "myimage")
let date :NSDate = NSDate()
let dateFormatter = NSDateFormatter()
//dateFormatter.dateFormat = "yyyy-MM-dd'_'HH:mm:ss"
dateFormatter.dateFormat = "yyyy-MM-dd'_'HH_mm_ss"
dateFormatter.timeZone = NSTimeZone(name: "GMT")
let imageName = "/\(dateFormatter.stringFromDate(date)).jpg"
print(imageName)
//var paths: [AnyObject] = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)
//var documentsDirectoryPath = getDocumentsURL().relativePath
var documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
documentsDirectoryPath += imageName
print(documentsDirectoryPath)
let settingsData: NSData = UIImageJPEGRepresentation(newImage!, 1.0)!
settingsData.writeToFile(documentsDirectoryPath, atomically: true)

SWIFT 3 :)
import UIKit
class ShowImageViewController: UIViewController {
#IBOutlet weak var mainImageView: UIImageView!
#IBOutlet weak var tempImageView: UIImageView!
private var lastPoint = CGPoint.zero
private var red: CGFloat = 0.0
private var green: CGFloat = 0.0
private var blue: CGFloat = 0.0
private var brushWidth: CGFloat = 10.0
private var opacity: CGFloat = 1.0
private var swiped = false
override func viewDidLoad() {
super.viewDidLoad()
mainImageView.image = captureImages[selectedImageindex]
tempImageView.frame = getImageRect(for: mainImageView)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = false
if let touch = touches.first {
lastPoint = touch.location(in: tempImageView)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
swiped = true
if let touch = touches.first {
let currentPoint = touch.location(in: tempImageView)
drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
lastPoint = currentPoint
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
}
// Merge tempImageView into mainImageView
UIGraphicsBeginImageContext(mainImageView.frame.size)
mainImageView.image?.draw(in: CGRect(x: 0, y: 0, width: mainImageView.frame.size.width, height: mainImageView.frame.size.height), blendMode: .normal, alpha: 1.0)
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: mainImageView.frame.size.width, height: mainImageView.frame.size.height), blendMode: .normal, alpha: opacity)
mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
tempImageView.image = nil
}
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
UIGraphicsBeginImageContext(mainImageView.frame.size)
let context = UIGraphicsGetCurrentContext()
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: mainImageView.frame.size.width, height: mainImageView.frame.size.height))
context!.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
context!.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
context!.setLineCap(.round)
context!.setLineWidth(brushWidth)
context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
context!.setBlendMode(.normal)
context!.strokePath()
tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
tempImageView.alpha = opacity
UIGraphicsEndImageContext()
}
func getImageRect(for imageView: UIImageView) -> CGRect {
let resVi = (imageView.image?.size.width)! / (imageView.image?.size.height)!
let resPl = imageView.frame.size.width / imageView.frame.size.height
if resPl > resVi {
let imageSize = CGSize(width: CGFloat((imageView.image?.size.width)! * imageView.frame.size.height / (imageView.image?.size.height)!), height: CGFloat(imageView.frame.size.height))
return CGRect(x: CGFloat((imageView.frame.size.width - imageSize.width) / 2), y: CGFloat((imageView.frame.size.height - imageSize.height) / 2), width: CGFloat(imageSize.width), height: CGFloat(imageSize.height))
}
else {
let imageSize = CGSize(width: CGFloat(imageView.frame.size.width), height: CGFloat((imageView.image?.size.height)! * imageView.frame.size.width / (imageView.image?.size.width)!))
return CGRect(x: CGFloat((imageView.frame.size.width - imageSize.width) / 2), y: CGFloat((imageView.frame.size.height - imageSize.height) / 2), width: CGFloat(imageSize.width), height: CGFloat(imageSize.height))
}
}
}

Related

Cloud moving animation in Swift? Is it possible?

We are following this tutorial to http://www.invasivecode.com/weblog/core-animation-scroll-layer-cascrolllayer
As per their tutorial animation will stop based on the layer bounds reached. How to do the cloud moving animation by using core animation or in Imageview?
Is it possible?
Gif or Lottie animation not needed here, we excepting through code.
Note that we have tried their code and its working but lack is there as i mentioned above.
How to keep on moving animation like cloud does?
Think twice before devoting this question.
Code:
class ViewController: UIViewController {
var translation: CGFloat = 0.0
public var currentWidth : CGFloat = {
let width = UIScreen.main.bounds.width
return width
}()
public var currentHeight : CGFloat = {
let width = UIScreen.main.bounds.height
return width
}()
lazy var scrollLayer : CAScrollLayer = {
let scrollLayer = CAScrollLayer() // 8
scrollLayer.bounds = CGRect(x: 0.0, y: 0.0, width: currentWidth, height: currentHeight) // 9
scrollLayer.position = CGPoint(x: self.view.bounds.size.width/2, y: self.view.bounds.size.height/2) // 10
scrollLayer.borderColor = UIColor.black.cgColor // 11
// scrollLayer.borderWidth = 5.0 // 12
scrollLayer.scrollMode = CAScrollLayerScrollMode.horizontally // 13
return scrollLayer
}()
lazy var displayLink: CADisplayLink = {
let displayLink = CADisplayLink(target: self, selector: #selector(scrollLayerScroll))
if #available(iOS 15.0, *) {
displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 5.0, maximum: 8.0, __preferred: 6.0)
} else {
displayLink.preferredFramesPerSecond = 5
// Fallback on earlier versions
}
return displayLink
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if let img = UIImage(named: "new") { // 1
let imageSize = img.size
let layer = CALayer() // 2
layer.bounds = CGRect(x: 0.0, y: 0.0, width: currentWidth * 50, height: currentHeight * 5) // 3
// layer.position = CGPoint(x: imageSize.width/2, y: imageSize.height/2) // 4
layer.contents = img.cgImage // 5
view.layer.addSublayer(scrollLayer) // 6
scrollLayer.addSublayer(layer) // 7
}
displayLink.add(to: RunLoop.current, forMode: .common)
}
#objc func scrollLayerScroll() {
let newPoint = CGPoint(x: translation, y: 0.0)
scrollLayer.scroll(newPoint)
translation += 10.0
// if translation > 1600.0 {
//// stopDisplayLink()
// }
}
func stopDisplayLink() {
displayLink.invalidate()
}
}
Here setting width too high for animating the layer extra time
layer.bounds = CGRect(x: 0.0, y: 0.0, width: currentWidth * 50, height: currentHeight * 5) // 3
We ended up with following solution by rob mayoff
Animate infinite scrolling of an image in a seamless loop
Please find the updated code.
class CloudPassingVC: UIViewController {
#IBOutlet weak var imageToAnimate: UIImageView!
var cloudLayer: CALayer? = nil
var cloudLayerAnimation: CABasicAnimation? = nil
override func viewDidLoad() {
super.viewDidLoad()
cloudScroll()
}
func cloudScroll() {
let cloudsImage = UIImage(named: "clouds")
let cloudPattern = UIColor(patternImage: cloudsImage!)
cloudLayer = CALayer()
cloudLayer!.backgroundColor = cloudPattern.cgColor
cloudLayer!.transform = CATransform3DMakeScale(1, -1, 1)
cloudLayer!.anchorPoint = CGPoint(x: 0, y: 1)
let viewSize = imageToAnimate.bounds.size
cloudLayer!.frame = CGRect(x: 0, y: 0, width: (cloudsImage?.size.width ?? 0.0) + viewSize.width, height: viewSize.height)
imageToAnimate.layer.addSublayer(cloudLayer!)
let startPoint = CGPoint.zero
let endPoint = CGPoint(x: -(cloudsImage?.size.width ?? 0.0), y: 0)
cloudLayerAnimation = CABasicAnimation(keyPath: "position")
cloudLayerAnimation!.timingFunction = CAMediaTimingFunction(name: .linear)
cloudLayerAnimation!.fromValue = NSValue(cgPoint: startPoint)
cloudLayerAnimation!.toValue = NSValue(cgPoint: endPoint)
cloudLayerAnimation!.repeatCount = 600
cloudLayerAnimation!.duration = 10.0
applyCloudLayerAnimation()
}
func applyCloudLayerAnimation() {
cloudLayer!.add(cloudLayerAnimation!, forKey: "position")
}
}

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.

Swift: Draw on image without changing its size (Aspect fill)

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?

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.

CGBlendMode.clear isn't working for me

I'm trying make an eraser function in a project. I can draw the lines ok, but there is no action when I invoke CGBlendMode.clear.
Can anyone see what I'm doing wrong or missing. I'm posting a code snippet below, but if another piece is required, please let me know.
fileprivate func drawLine(_ stroke: Stroke) -> Void {
print(#function)
let properties = stroke.settings
let array = stroke.points
if array?.count == 1 {
let pointStr = array?[0]
let point = CGPointFromString(pointStr!)
self.drawLineFrom(point, toPoint: point, properties: properties!)
}
for i in 0 ..< (array?.count)! - 1 {
let pointStr0 = array?[i]
let pointStr1 = array?[i+1]
let point0 = CGPointFromString(pointStr0!)
let point1 = CGPointFromString(pointStr1!)
self.drawLineFrom(point0, toPoint: point1, properties: properties!)
}
self.mergeViews()
}
fileprivate func drawLineFrom(_ fromPoint: CGPoint, toPoint: CGPoint, properties: StrokeSettings) -> Void {
print(#function)
UIGraphicsBeginImageContext(self.frame.size)
let context = UIGraphicsGetCurrentContext()
context!.move(to: CGPoint(x: fromPoint.x, y: fromPoint.y))
context!.addLine(to: CGPoint(x: toPoint.x, y: toPoint.y))
context!.setLineCap(CGLineCap.round)
context!.setLineWidth(properties.width)
print(#function, "drawEraseSwitch: ", drawEraseSwitch)
if drawEraseSwitch == 0 {
context!.setBlendMode(CGBlendMode.normal)
context!.setStrokeColor(red: properties.color.red, green: properties.color.green, blue: properties.color.blue, alpha: 1.0)
} else {
context!.setStrokeColor(red: properties.color.red, green: properties.color.green, blue: properties.color.blue, alpha: 1.0)
context!.setBlendMode(CGBlendMode.clear)
}
context!.strokePath()
self.backgroundImageView.image?.draw(in: self.backgroundImageView.frame)
let image = UIGraphicsGetImageFromCurrentImageContext()
self.backgroundImageView.image = image
self.backgroundImageView.alpha = properties.color.alpha
UIGraphicsEndImageContext()
}
fileprivate func mergeViews() {
print(#function)
UIGraphicsBeginImageContext(self.mainImageView.frame.size)
let image = UIGraphicsGetImageFromCurrentImageContext()
self.mainImageView.image = image
UIGraphicsEndImageContext()
//self.backgroundImageView.image = nil
}

Resources