Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions FreehandDrawing-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,11 @@
TargetAttributes = {
47919A3D1B0CE08600D7BFE9 = {
CreatedOnToolsVersion = 6.1.1;
LastSwiftMigration = 0820;
};
47919A521B0CE08600D7BFE9 = {
CreatedOnToolsVersion = 6.1.1;
LastSwiftMigration = 0820;
TestTargetID = 47919A3D1B0CE08600D7BFE9;
};
};
Expand Down Expand Up @@ -403,6 +405,7 @@
INFOPLIST_FILE = "FreehandDrawing-iOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -413,6 +416,7 @@
INFOPLIST_FILE = "FreehandDrawing-iOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand All @@ -431,6 +435,7 @@
INFOPLIST_FILE = "FreehandDrawing-iOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FreehandDrawing-iOS.app/FreehandDrawing-iOS";
};
name = Debug;
Expand All @@ -446,6 +451,7 @@
INFOPLIST_FILE = "FreehandDrawing-iOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FreehandDrawing-iOS.app/FreehandDrawing-iOS";
};
name = Release;
Expand Down
12 changes: 6 additions & 6 deletions FreehandDrawing-iOS/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(application: UIApplication) {
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

func applicationDidEnterBackground(application: UIApplication) {
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(application: UIApplication) {
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(application: UIApplication) {
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(application: UIApplication) {
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

Expand Down
11 changes: 5 additions & 6 deletions FreehandDrawing-iOS/CircleDrawCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ struct CircleDrawCommand : DrawCommand {

// MARK: DrawCommand

func execute(canvas: Canvas) {
CGContextSetFillColorWithColor(canvas.context, self.color.CGColor)

CGContextAddArc(canvas.context, self.center.x, self.center.y, self.radius, 0, 2 * CGFloat(M_PI), 1)
CGContextFillPath(canvas.context)
func execute(_ canvas: Canvas) {
canvas.context.setFillColor(self.color.cgColor)
canvas.context.addArc(center: self.center, radius: self.radius, startAngle: 0, endAngle: 2 * CGFloat(M_PI), clockwise: true)
(canvas.context).fillPath()
}
}
}
8 changes: 4 additions & 4 deletions FreehandDrawing-iOS/ComposedCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ struct ComposedCommand : DrawCommand {

// MARK: DrawCommand

func execute(canvas: Canvas) {
func execute(_ canvas: Canvas) {
self.commands.map { $0.execute(canvas) }
}

mutating func addCommand(command: DrawCommand) {
mutating func addCommand(_ command: DrawCommand) {
self.commands.append(command)
}

private var commands: [DrawCommand]
}
fileprivate var commands: [DrawCommand]
}
4 changes: 2 additions & 2 deletions FreehandDrawing-iOS/DrawCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ protocol Canvas {
}

protocol DrawCommand {
func execute(canvas: Canvas)
func execute(_ canvas: Canvas)
}

protocol DrawCommandReceiver {
func executeCommands(commands: [DrawCommand])
func executeCommands(_ commands: [DrawCommand])
}
22 changes: 11 additions & 11 deletions FreehandDrawing-iOS/DrawView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class DrawView : UIView, Canvas, DrawCommandReceiver {

// MARK: Canvas

var context: CGContextRef {
return UIGraphicsGetCurrentContext()
var context: CGContext {
return UIGraphicsGetCurrentContext()!
}

func reset() {
Expand All @@ -39,41 +39,41 @@ class DrawView : UIView, Canvas, DrawCommandReceiver {

// MARK: DrawCommandReceiver

func executeCommands(commands: [DrawCommand]) {
func executeCommands(_ commands: [DrawCommand]) {
autoreleasepool {
self.buffer = drawInContext { context in
commands.map { $0.execute(self) }
}

self.layer.contents = self.buffer?.CGImage ?? nil
self.layer.contents = self.buffer?.cgImage ?? nil
}
}

// MARK: General setup to draw. Reusing a buffer and returning a new one

private func drawInContext(code:(context: CGContextRef) -> Void) -> UIImage {
fileprivate func drawInContext(_ code:(_ context: CGContext) -> Void) -> UIImage {
let size = self.bounds.size

// Initialize a full size image. Opaque because we don't need to draw over anything. Will be more performant.
UIGraphicsBeginImageContextWithOptions(size, true, 0)
let context = UIGraphicsGetCurrentContext()

CGContextSetFillColorWithColor(context, self.backgroundColor?.CGColor ?? UIColor.whiteColor().CGColor)
CGContextFillRect(context, self.bounds)
context?.setFillColor(self.backgroundColor?.cgColor ?? UIColor.white.cgColor)
context?.fill(self.bounds)

// Draw previous buffer first
if let buffer = buffer {
buffer.drawInRect(self.bounds)
buffer.draw(in: self.bounds)
}

// Execute draw code
code(context: context)
code(context!)

// Grab updated buffer and return it
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
return image!
}

private var buffer: UIImage?
fileprivate var buffer: UIImage?
}
2 changes: 1 addition & 1 deletion FreehandDrawing-iOS/DrawViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class DrawViewController: UIViewController {
return self.view as! DrawView
}

private var drawController: FreehandDrawController!
fileprivate var drawController: FreehandDrawController!
@IBOutlet var toolbar: Toolbar!
}

58 changes: 29 additions & 29 deletions FreehandDrawing-iOS/FreehandDrawController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import UIKit

class FreehandDrawController : NSObject {
var color: UIColor = UIColor.blackColor()
var color: UIColor = UIColor.black
var width: CGFloat = 5.0

required init(canvas: protocol<Canvas, DrawCommandReceiver>, view: UIView) {
required init(canvas: Canvas & DrawCommandReceiver, view: UIView) {
self.canvas = canvas
super.init()

Expand All @@ -31,48 +31,48 @@ class FreehandDrawController : NSObject {

// MARK: Gestures

private func setupGestureRecognizersInView(view: UIView) {
fileprivate func setupGestureRecognizersInView(_ view: UIView) {
// Pan gesture recognizer to track lines
let panRecognizer = UIPanGestureRecognizer(target: self, action: "handlePan:")
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(FreehandDrawController.handlePan(_:)))
view.addGestureRecognizer(panRecognizer)

// Tap gesture recognizer to track points
let tapRecognizer = UITapGestureRecognizer(target: self, action: "handleTap:")
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(FreehandDrawController.handleTap(_:)))
view.addGestureRecognizer(tapRecognizer)
}

@objc private func handlePan(sender: UIPanGestureRecognizer) {
let point = sender.locationInView(sender.view)
@objc fileprivate func handlePan(_ sender: UIPanGestureRecognizer) {
let point = sender.location(in: sender.view)
switch sender.state {
case .Began:
case .began:
self.startAtPoint(point)
case .Changed:
self.continueAtPoint(point, velocity: sender.velocityInView(sender.view))
case .Ended:
case .changed:
self.continueAtPoint(point, velocity: sender.velocity(in: sender.view))
case .ended:
self.endAtPoint(point)
case .Failed:
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 {
@objc fileprivate func handleTap(_ sender: UITapGestureRecognizer) {
let point = sender.location(in: sender.view)
if sender.state == .ended {
self.tapAtPoint(point)
}
}

// MARK: Draw commands

private func startAtPoint(point: CGPoint) {
fileprivate func startAtPoint(_ point: CGPoint) {
self.lastPoint = point
self.lineStrokeCommand = ComposedCommand(commands: [])
}

private func continueAtPoint(point: CGPoint, velocity: CGPoint) {
let segmentWidth = modulatedWidth(self.width, velocity, self.lastVelocity, self.lastWidth ?? self.width)
fileprivate func continueAtPoint(_ point: CGPoint, velocity: CGPoint) {
let segmentWidth = modulatedWidth(self.width, velocity: velocity, previousVelocity: self.lastVelocity, previousWidth: self.lastWidth ?? self.width)
let segment = Segment(a: self.lastPoint, b: point, width: segmentWidth)

let lineCommand = LineDrawCommand(current: segment, previous: lastSegment, width: segmentWidth, color: self.color)
Expand All @@ -86,29 +86,29 @@ class FreehandDrawController : NSObject {
self.lastWidth = segmentWidth
}

private func endAtPoint(point: CGPoint) {
fileprivate func endAtPoint(_ point: CGPoint) {
if let lineStrokeCommand = self.lineStrokeCommand {
self.commandQueue.append(lineStrokeCommand)
}

self.lastPoint = CGPointZero
self.lastPoint = CGPoint.zero
self.lastSegment = nil
self.lastVelocity = CGPointZero
self.lastVelocity = CGPoint.zero
self.lastWidth = nil
self.lineStrokeCommand = nil
}

private func tapAtPoint(point: CGPoint) {
fileprivate func tapAtPoint(_ point: CGPoint) {
let circleCommand = CircleDrawCommand(center: point, radius: self.width/2.0, color: self.color)
self.canvas.executeCommands([circleCommand])
self.commandQueue.append(circleCommand)
}

private let canvas: protocol<Canvas, DrawCommandReceiver>
private var lineStrokeCommand: ComposedCommand?
private var commandQueue: Array<DrawCommand> = []
private var lastPoint: CGPoint = CGPointZero
private var lastSegment: Segment?
private var lastVelocity: CGPoint = CGPointZero
private var lastWidth: CGFloat?
fileprivate let canvas: Canvas & DrawCommandReceiver
fileprivate var lineStrokeCommand: ComposedCommand?
fileprivate var commandQueue: Array<DrawCommand> = []
fileprivate var lastPoint: CGPoint = CGPoint.zero
fileprivate var lastSegment: Segment?
fileprivate var lastVelocity: CGPoint = CGPoint.zero
fileprivate var lastWidth: CGFloat?
}
30 changes: 15 additions & 15 deletions FreehandDrawing-iOS/LineDrawCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,35 @@ struct LineDrawCommand : DrawCommand {

// MARK: DrawCommand

func execute(canvas: Canvas) {
func execute(_ canvas: Canvas) {
self.configure(canvas)

if let previous = self.previous {
if self.previous != nil {
self.drawQuadraticCurve(canvas)
} else {
self.drawLine(canvas)
}
}

private func configure(canvas: Canvas) {
CGContextSetStrokeColorWithColor(canvas.context, self.color.CGColor)
CGContextSetLineWidth(canvas.context, self.width)
CGContextSetLineCap(canvas.context, kCGLineCapRound)
fileprivate func configure(_ canvas: Canvas) {
canvas.context.setStrokeColor(self.color.cgColor)
canvas.context.setLineWidth(self.width)
(canvas.context).setLineCap(CGLineCap.round)
}

private func drawLine(canvas: Canvas) {
CGContextMoveToPoint(canvas.context, self.current.a.x, self.current.a.y)
CGContextAddLineToPoint(canvas.context, self.current.b.x, self.current.b.y)
CGContextStrokePath(canvas.context)
fileprivate func drawLine(_ canvas: Canvas) {
canvas.context.move(to: CGPoint(x: self.current.a.x, y: self.current.a.y))
canvas.context.addLine(to: CGPoint(x: self.current.b.x, y: self.current.b.y))
canvas.context.strokePath()
}

private func drawQuadraticCurve(canvas: Canvas) {
fileprivate func drawQuadraticCurve(_ canvas: Canvas) {
if let previousMid = self.previous?.midPoint {
let currentMid = self.current.midPoint

CGContextMoveToPoint(canvas.context, previousMid.x, previousMid.y)
CGContextAddQuadCurveToPoint(canvas.context, current.a.x, current.a.y, currentMid.x, currentMid.y)
CGContextStrokePath(canvas.context)
canvas.context.move(to: CGPoint(x: previousMid.x, y: previousMid.y))
canvas.context.addQuadCurve(to: CGPoint(x:current.a.x, y:current.a.y), control: currentMid)
canvas.context.strokePath()
}
}
}
}
Loading