Doodle Draw App: lines drawn keep overlapping when (alpha < 1) - ios

My Swift doodle app works, but when I change the alpha below '1.0', then I draw a line on the screen, the lines keeps overlapping. I created a custom Line class, drawView subclass of UIView. I've been messing around all day but can't seem to figure it out. See Below.
DrawView: UIView class
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.first as! UITouch
lastPoint = touch.locationInView(self)
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.first as! UITouch
var newPoint = touch.locationInView(self)
//from custom Line class: sets width and opacity as well
lines.append(Line(start: lastPoint, end: newPoint, color: drawColor, brushWidth: brushWidth, opacity: opacity))
lastPoint = newPoint
self.setNeedsDisplay()
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent){
touchesMoved(touches, withEvent: event) // adds point
}
override func drawRect(rect: CGRect) {
var context = UIGraphicsGetCurrentContext()
CGContextBeginPath(context)
CGContextSetLineCap(context, kCGLineCapRound)
for line in lines {
CGContextSetLineWidth(context, line.brushWidth)
CGContextSetAlpha(context, line.opacity)
CGContextSetStrokeColorWithColor(context, line.color.CGColor)
CGContextMoveToPoint(context, line.start.x, line.start.y)
CGContextAddLineToPoint(context, line.end.x, line.end.y)
CGContextStrokePath(context)
}
}
I don't know why it keeps overlapping. I some how need to track a the path then draw over it or somehow see if my CGPoints are the same while next to each other remove it or ignore it.
Is this possible using just one UIView?
Just now I tried using:
var touchesEnd: Bool = false //global variable
Then in touchesEnded:
touchesEnd = true
Finally in TouchesMoved:
if touchesEnd == true {
self.setNeedsDisplay()
}
This is the most recent things i've tried but hasn't worked.

Related

Swift: How to pull out CGPoint number from Override func of touch?

I am using the codes shown below for letting user to touch and draw. During touching it, user always leaves CGPoint or coordinates of his/her first and last touch.
var lastPoint: CGPoint!
var firstPoint: CGPoint!
var swiped: Bool!
var allowTouches = true
override func touchesBegan(touches: Set<UITouch>,
withEvent event: UIEvent?) {
guard allowTouches else {
return
}
swiped = false
if let touch = touches.first {
lastPoint = touch.locationInView(self.imageView)
firstPoint = lastPoint
}
}
override func touchesMoved(touches: Set<UITouch>,
withEvent event: UIEvent?) {
guard allowTouches else {
return
}
swiped = true;
if let touch = touches.first {
let currentPoint = touch.locationInView(imageView)
UIGraphicsBeginImageContext(self.imageView.frame.size)
self.imageView.image?.drawInRect(CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height))
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y)
CGContextSetLineCap(UIGraphicsGetCurrentContext(),CGLineCap.Round)
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0)
CGContextStrokePath(UIGraphicsGetCurrentContext())
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
lastPoint = currentPoint
}
}
As you can see in the code that firstPoint saves the coordinates when user touches the screen and after swiping and then removing the touch, lastPoint saves the coordinates.
My question: how can I get those two coordinates(firstPoint and lastPoint) as return from override func? so that I can use these two values for other calculations outside override func.
use
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
method to calculate the lastPoint of touch when user picks his finger. Since you have declared them as the var and global variables therefore you can use them in any function(method) of the class. Just calculate the difference between the lastPoint and firstPoint and you will get the distance.

Why does my drawing lag after drawing for a long period of time?

I'm creating a drawing app. In the beginning my drawing is smooth. When I draw a bunch of circles for a long period of time, my drawing starts to get edgy. Perhaps it's the fact that an array can't handle too many points?
DV.swift:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
lastPoint = touches.first!.locationInView(self)
self.setNeedsDisplay()
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
var newPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end: newPoint))
lastPoint = newPoint
self.setNeedsDisplay()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
var veryFirstPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end:veryFirstPoint))
self.setNeedsDisplay()
}
override func drawRect(rect: CGRect) {
var context = UIGraphicsGetCurrentContext()
CGContextBeginPath(context)
for line in lines {
CGContextMoveToPoint(context,line.start.x , line.start.y)
CGContextAddLineToPoint(context, line.end.x, line.end.y)
}
CGContextSetRGBFillColor(context, 0, 0, 0, 1)
CGContextSetLineWidth(context, 5)
CGContextStrokePath(context)
}
Example tested on my iPad mini 4:
On the left side is the jagged numbers after drawing a bunch of loops. the right side is the first few numbers I drew and they're smooth.
You are right. You are having the CPU redraw many paths every time you add a new "line", that is after every touch. This becomes increasingly processor intensive the more lines you have. One solution is to only redraw the "dirty" parts of your view. You can do this by using setNeedsDisplayInRect(rect:CGRect) instead of setNeedsDisplay(). For example you can add the extension:
extension CGRect{
static func rectWithTwoPoints(p1:CGPoint,p2:CGPoint) -> CGRect
{
return CGRectMake(min(p1.x, p2.x),min(p1.y, p2.y),fabs(p1.x - p2.x),fabs(p1.y - p2.y));
}
}
This will give me a rect enclosing any two points. Now in your touchesMoved: and touchesEnded: we can call setNeedsDisplayInRect: like so:
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let newPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end: newPoint))
lastPoint = newPoint
self.setNeedsDisplayInRect(CGRectInset(CGRect.rectWithTwoPoints((lines.last?.start)!, p2: (lines.last?.end)!),-10.0,-10.0)) //would be better to not force unwrap and use magic numbers, but you get the idea
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let veryFirstPoint = touches.first!.locationInView(self)
lines.append(Line(start: lastPoint, end:veryFirstPoint))
self.setNeedsDisplayInRect(CGRectInset(CGRect.rectWithTwoPoints((lines.last?.start)!, p2: (lines.last?.end)!),-10.0,-10.0))
}
Using CGRectInset to expand our rect a bit because we have a path width that if we only redraw the rect returned by rectWithTwoPoints would take into account.

Swift xcode errors: Could not infer type for 'shape' and enum case not found in error type

I'm trying to create a drawing method inside xcode in order to draw different shapes inside a UIView. The purpose of this app is to attach the drawing to a row that is saved in a table (I will post the table code if needed but I don't think it is necessary for this error).
Screenshot of error:
import UIKit
/*
This program is for Xcode 6.3 and Swift 1.2
*/
class CanvasView: UIView {
enum ShapeType: String
{
case Line = "Line"
case Ellipse = "Ellipse"
case Rectangle = "Rectangle"
case FilledEllipse = "Filled Ellipse"
case FilledRectangle = "Filled Rectangle"
case Scribble = "Scribble"
}
var shape: ShapeType = .Line
var color: UIColor = UIColor.blueColor()
var first :CGPoint = CGPointZero
var last :CGPoint = CGPointZero
var points: [CGPoint] = []
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, color.CGColor)
CGContextSetFillColorWithColor(context, color.CGColor)
let rect = CGRect(x: first.x, y: first.y, width: last.x - first.x, height: last.y - first.y)
switch shape {
case .Line:
CGContextMoveToPoint(context, first.x, first.y)
CGContextAddLineToPoint(context, last.x, last.y)
CGContextStrokePath(context)
case .Ellipse:
CGContextStrokeEllipseInRect(context, rect)
case .Rectangle:
CGContextStrokeRect(context, rect)
case .FilledEllipse:
CGContextFillEllipseInRect(context, rect)
case .FilledRectangle:
CGContextFillRect(context, rect)
case .Scribble:
CGContextMoveToPoint(context, first.x, first.y)
for p in points {
CGContextAddLineToPoint(context, p.x, p.y)
}
CGContextStrokePath(context)
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
first = touch.locationInView(self)
last = first
points.removeAll(keepCapacity: true)
if shape == .Scribble {
points.append(first)
}
setNeedsDisplay()
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
last = touch.locationInView(self)
if shape == .Scribble {
points.append(last)
}
setNeedsDisplay()
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
last = touch.locationInView(self)
if shape == .Scribble {
points.append(last)
}
setNeedsDisplay()
}
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
}
}

Swift - Using CGContext to draw with finger

I'm trying to make a drawing app. I have a single custom UIView:
class DrawView: UIView {
var touch : UITouch!
var lastPoint : CGPoint!
var currentPoint : CGPoint!
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
touch = touches.first as! UITouch
lastPoint = touch.locationInView(self)
println(lastPoint)
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
touch = touches.first as! UITouch
currentPoint = touch.locationInView(self)
self.setNeedsDisplay()
lastPoint = currentPoint
}
override func drawRect(rect: CGRect) {
var context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, 5)
CGContextSetStrokeColorWithColor(context, UIColor.blueColor().CGColor)
CGContextSetLineCap(context, kCGLineCapRound)
CGContextBeginPath(context)
if lastPoint != nil {
CGContextMoveToPoint(context, lastPoint.x, lastPoint.y)
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y)
}
CGContextStrokePath(context)
}
}
When I run it, however, all I get is a blue dot that follows my finger, but no lines?
What am I doing wrong?
Hi i make some simple changes and fixed your code, hope it helps someone in the future (code it's updated for Swift 3) :
class DrawView: UIView {
var touch : UITouch!
var lineArray : [[CGPoint]] = [[CGPoint]()]
var index = -1
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touch = touches.first! as UITouch
let lastPoint = touch.location(in: self)
index += 1
lineArray.append([CGPoint]())
lineArray[index].append(lastPoint)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
touch = touches.first! as UITouch
let currentPoint = touch.location(in: self)
self.setNeedsDisplay()
lineArray[index].append(currentPoint)
}
override func draw(_ rect: CGRect) {
if(index >= 0){
let context = UIGraphicsGetCurrentContext()
context!.setLineWidth(5)
context!.setStrokeColor((UIColor(red:0.00, green:0.38, blue:0.83, alpha:1.0)).cgColor)
context!.setLineCap(.round)
var j = 0
while( j <= index ){
context!.beginPath()
var i = 0
context?.move(to: lineArray[j][0])
while(i < lineArray[j].count){
context?.addLine(to: lineArray[j][i])
i += 1
}
context!.strokePath()
j += 1
}
}
}
}
Two things:
Calling self.setNeedsDisplay doesn't immediately call drawRect. It just sets a flag so that drawRect will be called in the near future. Since you set lastPoint to currentPoint right after that, when drawRect is called lastPoint is always equal to currentPoint.
drawRect redraws the entire view every time it is called, so at most you'd only ever see the most recent line. If you fixed problem 1, you'd have a short line following your finger instead of a dot. If you want to see the whole trail, you'll need to store the points in an array that is a property of your view, and then draw lines to connect all of the points in drawRect.
marcomoreira92 and Keuha's version worked for me, but I don't like to use indices that much. Thus here is an alternative version, which was tested in Swift 4.2:
class DrawView: UIView {
var lineArray: [[CGPoint]] = [[CGPoint]]()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let firstPoint = touch.location(in: self)
lineArray.append([CGPoint]())
lineArray[lineArray.count - 1].append(firstPoint)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let currentPoint = touch.location(in: self)
lineArray[lineArray.count - 1].append(currentPoint)
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context?.setLineWidth(5)
context?.setStrokeColor(UIColor.black.cgColor)
context?.setLineCap(.round)
for line in lineArray {
guard let firstPoint = line.first else { continue }
context?.beginPath()
context?.move(to: firstPoint)
for point in line.dropFirst() {
context?.addLine(to: point)
}
context?.strokePath()
}
}
}

Draw lines with Sprite kit and swift

I'm using Sprite kit with Swift to assay drawing lines with fingers.
Code:
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
touch = touches.anyObject() as UITouch!
firstPoint = touch.locationInNode(self)
}
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
var touch = touches.anyObject() as UITouch!
var positionInScene = touch.locationInNode(self)
lineNode.removeFromParent()
CGPathMoveToPoint(pathToDraw, nil, firstPoint.x, firstPoint.y)
CGPathAddLineToPoint(pathToDraw, nil, positionInScene.x, positionInScene.y)
lineNode.path = pathToDraw
lineNode.lineWidth = 10.0
lineNode.strokeColor = UIColor.redColor()
self.addChild(lineNode)
firstPoint = positionInScene
}
override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
}
But the lines obtained are empty, only the border has color (image).
Any idea?
SKShapeNode has a property called fillColor which is by default set to clearColor. In your code, you only set the strokeColor(outline) to red, but you should also do so to fillColor.
You can do this with lineNode.fillColor = UIColor.redColor()
Good luck!

Resources