Permalink
Browse files

Refactors View into two parts: Canvas and tracing the points and lines

  • Loading branch information...
DarthMike committed Jun 3, 2015
1 parent 44272d3 commit bc1c896ce5800caea00d986bbb4aeef0f49e310b
@@ -19,6 +19,7 @@
47D780891B1F1A9900076BD6 /* DrawCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D780881B1F1A9900076BD6 /* DrawCommands.swift */; };
47D7808C1B1F1D0100076BD6 /* LineDrawCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D7808B1B1F1D0100076BD6 /* LineDrawCommand.swift */; };
47D7808F1B1F1D1400076BD6 /* CircleDrawCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D7808E1B1F1D1400076BD6 /* CircleDrawCommand.swift */; };
47D780931B1F238300076BD6 /* FreehandDrawController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D780921B1F238300076BD6 /* FreehandDrawController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -48,6 +49,7 @@
47D780881B1F1A9900076BD6 /* DrawCommands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawCommands.swift; sourceTree = "<group>"; };
47D7808B1B1F1D0100076BD6 /* LineDrawCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineDrawCommand.swift; sourceTree = "<group>"; };
47D7808E1B1F1D1400076BD6 /* CircleDrawCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleDrawCommand.swift; sourceTree = "<group>"; };
47D780921B1F238300076BD6 /* FreehandDrawController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FreehandDrawController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -72,9 +74,10 @@
isa = PBXGroup;
children = (
47919A431B0CE08600D7BFE9 /* AppDelegate.swift */,
47919A451B0CE08600D7BFE9 /* DrawViewController.swift */,
47D7808A1B1F1CE700076BD6 /* DrawCommands */,
4712795C1B0CE98400A49B7E /* DrawView.swift */,
47919A451B0CE08600D7BFE9 /* DrawViewController.swift */,
47D780921B1F238300076BD6 /* FreehandDrawController.swift */,
47702F9E1B0E448800417ACF /* Toolbar.swift */,
47702FA01B0E452100417ACF /* Toolbar.xib */,
);
@@ -253,6 +256,7 @@
47D780891B1F1A9900076BD6 /* DrawCommands.swift in Sources */,
47D7808C1B1F1D0100076BD6 /* LineDrawCommand.swift in Sources */,
4712795D1B0CE98400A49B7E /* DrawView.swift in Sources */,
47D780931B1F238300076BD6 /* FreehandDrawController.swift in Sources */,
47702F9F1B0E448800417ACF /* Toolbar.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -31,3 +31,7 @@ protocol Canvas {
protocol DrawCommand {
func execute(canvas: Canvas)
}
protocol DrawCommandReceiver {
func executeCommand(command: DrawCommand)
}
@@ -24,40 +24,24 @@ THE SOFTWARE.
import UIKit
class DrawView : UIView {
override func awakeFromNib() {
super.awakeFromNib()
self.setupGestureRecognizers()
}
class DrawView : UIView, Canvas, DrawCommandReceiver {
// MARK: Drawing a path
// MARK: Canvas
private func drawLine(a: CGPoint, b: CGPoint, buffer: UIImage?) -> UIImage {
let image = drawInContext { context in
// Draw the line
self.drawColor.setStroke()
CGContextSetLineWidth(context, self.drawWidth)
CGContextSetLineCap(context, kCGLineCapRound)
CGContextMoveToPoint(context, a.x, a.y)
CGContextAddLineToPoint(context, b.x, b.y)
CGContextStrokePath(context)
}
return image
var context: CGContextRef {
return UIGraphicsGetCurrentContext()
}
// MARK: Drawing a point
// MARK: DrawCommandReceiver
private func drawPoint(at: CGPoint, buffer: UIImage?) -> UIImage {
let image = drawInContext { context in
// Draw the point
self.drawColor.setFill()
let circle = UIBezierPath(arcCenter: at, radius: self.drawWidth / 2.0, startAngle: 0, endAngle: 2 * CGFloat(M_PI), clockwise: true)
circle.fill()
func executeCommand(command: DrawCommand) {
autoreleasepool {
self.buffer = drawInContext { context in
command.execute(self)
}
self.layer.contents = self.buffer?.CGImage ?? nil
}
return image
}
// MARK: General setup to draw. Reusing a buffer and returning a new one
@@ -86,74 +70,5 @@ class DrawView : UIView {
return image
}
// MARK: Gestures
private func setupGestureRecognizers() {
// Pan gesture recognizer to track lines
let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
self.addGestureRecognizer(panRecognizer)
// Tap gesture recognizer to track points
let tapRecognizer = UITapGestureRecognizer(target: self, action: "handleTap:")
self.addGestureRecognizer(tapRecognizer)
}
@objc private func handlePan(sender: UIPanGestureRecognizer) {
let point = sender.locationInView(self)
switch sender.state {
case .Began:
self.startAtPoint(point)
case .Changed:
self.continueAtPoint(point)
case .Ended:
self.endAtPoint(point)
case .Failed:
self.endAtPoint(point)
default:
assert(false, "State not handled")
}
}
@objc private func handleTap(sender: UITapGestureRecognizer) {
let point = sender.locationInView(self)
if sender.state == .Ended {
self.tapAtPoint(point)
}
}
// MARK: Tracing a line
private func startAtPoint(point: CGPoint) {
self.lastPoint = point
}
private func continueAtPoint(point: CGPoint) {
autoreleasepool {
// Draw the current stroke in an accumulated bitmap
// Then replace layer contents with the updated image
self.buffer = self.drawLine(self.lastPoint, b: point, buffer: self.buffer)
self.layer.contents = self.buffer?.CGImage ?? nil
self.lastPoint = point
}
}
private func endAtPoint(point: CGPoint) {
self.lastPoint = CGPointZero
}
// MARK: Tracking a point
private func tapAtPoint(point: CGPoint) {
autoreleasepool {
self.buffer = self.drawPoint(point, buffer: self.buffer)
self.layer.contents = self.buffer?.CGImage ?? nil
}
}
var drawColor: UIColor = UIColor.blackColor()
var drawWidth: CGFloat = 10.0
private var lastPoint: CGPoint = CGPointZero
private var buffer: UIImage?
}
@@ -26,17 +26,20 @@ import UIKit
class DrawViewController: UIViewController {
override func viewDidLoad() {
self.drawController = FreehandDrawController(canvas: self.drawView, view: self.drawView)
self.drawController.width = 10.0
self.toolbar.colorChangeHandler = { color in
self.drawView.drawColor = color
self.drawController.color = color
}
}
var drawView: DrawView {
return self.view as! DrawView
}
private var drawController: FreehandDrawController!
@IBOutlet var toolbar: Toolbar!
}
@@ -0,0 +1,85 @@
//
// FreehandDrawController.swift
// FreehandDrawing-iOS
//
// Created by Miguel Angel Quinones on 03/06/2015.
// Copyright (c) 2015 badoo. All rights reserved.
//
import UIKit
class FreehandDrawController : NSObject {
var color: UIColor = UIColor.blackColor()
var width: CGFloat = 5.0
required init(canvas: protocol<Canvas, DrawCommandReceiver>, view: UIView) {
self.canvas = canvas
super.init()
self.setupGestureRecognizersInView(view)
}
// MARK: Gestures
private func setupGestureRecognizersInView(view: UIView) {
// Pan gesture recognizer to track lines
let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
view.addGestureRecognizer(panRecognizer)
// Tap gesture recognizer to track points
let tapRecognizer = UITapGestureRecognizer(target: self, action: "handleTap:")
view.addGestureRecognizer(tapRecognizer)
}
@objc private func handlePan(sender: UIPanGestureRecognizer) {
let point = sender.locationInView(sender.view)
switch sender.state {
case .Began:
self.startAtPoint(point)
case .Changed:
self.continueAtPoint(point)
case .Ended:
self.endAtPoint(point)
case .Failed:
self.endAtPoint(point)
default:
assert(false, "State not handled")
}
}
@objc private func handleTap(sender: UITapGestureRecognizer) {
let point = sender.locationInView(sender.view)
if sender.state == .Ended {
self.tapAtPoint(point)
}
}
// MARK: Draw commands
private func startAtPoint(point: CGPoint) {
self.lastPoint = point
}
private func continueAtPoint(point: CGPoint) {
let lineCommand = LineDrawCommand(a: self.lastPoint, b: point, width: self.width, color: self.color)
self.canvas.executeCommand(lineCommand)
self.commandQueue.append(lineCommand)
self.lastPoint = point
}
private func endAtPoint(point: CGPoint) {
self.lastPoint = CGPointZero
}
private func tapAtPoint(point: CGPoint) {
let circleCommand = CircleDrawCommand(center: point, radius: self.width/2.0, color: self.color)
self.canvas.executeCommand(circleCommand)
self.commandQueue.append(circleCommand)
}
private let canvas: protocol<Canvas, DrawCommandReceiver>
private var commandQueue: Array<DrawCommand> = []
private var lastPoint: CGPoint = CGPointZero
}

0 comments on commit bc1c896

Please sign in to comment.