问题
I'm trying to make a simple drawing app (OSX Mac app) and I'm trying to figure out how the user can draw a line by two mouse clicks, for example, the first mouse click (mouseDown then mouseUP) would mark the origin point of the line, and the second mouse click (mouseDown then mouseUP) would mark the end point of the line. Before the user makes the second click of the end point, I'd like the line (before anchoring the end point) to be shown live, kind of like in photoshop. Both Objective-C and Swift are fine.
So far I've got...
var newLinear = NSBezierPath()
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
var lastPoint = theEvent.locationInWindow
lastPoint.x -= frame.origin.x
lastPoint.y -= frame.origin.y
newLinear.moveToPoint(lastPoint)
}
override func mouseUp(theEvent: NSEvent) {
var newPoint = theEvent.locationInWindow
newPoint.x -= frame.origin.x
newPoint.y -= frame.origin.y
newLinear.lineToPoint(newPoint)
needsDisplay = true
}
Cheers!
回答1:
enum
s with associated values are great for this as your application scales up and possibly adds other tools and states.
enum State {
case normal
case drawingLine(from: CGPoint, to: CGPoint)
}
var state = State.normal
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
var lastPoint = theEvent.locationInWindow
lastPoint.x -= frame.origin.x
lastPoint.y -= frame.origin.y
state = .drawingLine(from: lastPoint, to: lastPoint)
}
override func mouseUp(theEvent: NSEvent) {
if case .drawingLine(let firstPoint, _) = state {
var newPoint = theEvent.locationInWindow
newPoint.x -= frame.origin.x
newPoint.y -= frame.origin.y
//finalize line from `firstPoint` to `newPoint`
}
}
override func mouseMoved(theEvent: NSEvent) {
if case .drawingLine(let firstPoint, _) = state {
needsDisplay = true
var newPoint = theEvent.locationInWindow
newPoint.x -= frame.origin.x
newPoint.y -= frame.origin.y
state = .drawingLine(from: firstPoint, to: newPoint)
}
}
override func draw(_ dirtyRect: NSRect) {
if case .drawingLine(let firstPoint, let secondPoint) = state {
//draw your line from `firstPoint` to `secondPoint`
}
}
回答2:
I understand what you are attempting to achieve here! So I have added lines of code to the andyvn22's solution that should assist your endeavours, please note: for simplicity purposes start a new 'Xcode Project' and we can make sure everything is covered together.
In your new project Right Click 'ViewController.swift' and add New File... Select 'Cocoa Class' and Click Next, name this file 'DrawingView' assure Subclass of: is 'NSView' and select Next.
Now your finished that lets setup your interface, enter 'Main.storyboard' and drag/drop a 'Custom View' make sure to add constrains according to your preferences. Now enter your 'Identity Inspector' while 'Custom View' is selected and add 'DrawingView' to Class at the top.
I hope this is making sense soo far! Ok open 'Assistant Editor' so you can view 'ViewController.swift' and 'Main.storyboard' simultaneously, Right Click and drag from 'Custom View' to 'ViewController.swift', name this 'draw' and select connect.
You will notice Xcode has automatically updated @IBOutlet to your Subclass, and your 'ViewController.swift' looks like the example below.
import Cocoa
class ViewController: NSViewController {
@IBOutlet var draw: DrawingView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Now select the 'DrawingView.swift' file and clear all content on page, highlight the code below, copy and paste into your project.
import Cocoa
class DrawingView: NSView {
// >>>CODE ADDED BY LC<<<
private var path: NSBezierPath = {
let path = NSBezierPath()
path.lineWidth = 50.0
path.lineJoinStyle = .roundLineJoinStyle
path.lineCapStyle = .roundLineCapStyle
return path
}()
enum State {
case normal
case drawingLine(from: CGPoint, to: CGPoint)
}
var state = State.normal
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
var lastPoint = event.locationInWindow
lastPoint.x -= frame.origin.x
lastPoint.y -= frame.origin.y
state = .drawingLine(from: lastPoint, to: lastPoint)
}
override func mouseUp(with event: NSEvent) {
if case .drawingLine(let firstPoint, _) = state {
var newPoint = event.locationInWindow
newPoint.x -= frame.origin.x
newPoint.y -= frame.origin.y
// >>>CODE ADDED BY LC<<<
path.move(to: convert(event.locationInWindow, from: nil))
path.line(to: firstPoint)
needsDisplay = true
}
}
override func draw(_ dirtyRect: NSRect) {
if case .drawingLine(let firstPoint, let secondPoint) = state {
// >>>CODE ADDED BY LC<<<
NSColor.orange.set()
path.lineWidth = 5.0
path.stroke()
path.line(to: firstPoint)
path.line(to: secondPoint)
}
}
}
Everything should be running as intended, you will be incapable of drawing a crocked line. I hope this has helped and feel free to reply for additional information.
来源:https://stackoverflow.com/questions/38222054/how-to-code-drawing-a-straight-line-with-two-mouse-clicks-osx-mac-app