我正在编写一个构建包含CGPoints的元素。我有2个按钮:makeRectangle
和makeTriangle
。对于构建/绘制阶段,我在矩形中使用了三种方法,在drawRect
内使用了三种三角形方法。
我在drawRect
中遇到了我的代码。在if-else-statement
中,每次按下按钮时,每个方法都会交换前一个元素的构建/绘制方案。
如果我已经构建了矩形,然后点击makeTriangle
按钮,我会得到新的三角形,但是我的矩形会变成三角形,并且有一个未连接的点。
是否有解决方法,或者我不应该使用drawRect
方法?
以下是drawRect
主题的SO帖子:To drawRect or not to drawRect
元素声明:
enum Element {
case point1(point: CGPoint)
case point2(point: CGPoint)
case point3(point: CGPoint)
case point4(point: CGPoint)
func coord() -> [CGPoint] {
switch self {
case .point1(let point): return [point]
case .point2(let point): return [point]
case .point3(let point): return [point]
case .point4(let point): return [point]
}
}
func buildQuadPath(path: CGMutablePath) {
switch self {
case .point1(let point): CGPathMoveToPoint(path, nil, point.x, point.y)
case .point2(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
case .point3(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
case .point4(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
CGPathCloseSubpath(path)
}
}
func buildTriPath(path: CGMutablePath) {
switch self {
case .point1(let point): CGPathMoveToPoint(path, nil, point.x, point.y)
case .point2(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
case .point3(let point): CGPathAddLineToPoint(path, nil, point.x, point.y)
default:
CGPathCloseSubpath(path)
}
}
}
构建和绘制三角形和矩形的方法:
func buildTriPath() -> CGMutablePath {
let path = CGPathCreateMutable()
_ = array.map { $0.buildTriPath(path) }
return path
}
func drawTriPath() {
let path = buildTriPath()
GraphicsState {
CGContextAddPath(self.currentContext, path)
CGContextStrokePath(self.currentContext)
}
}
func drawTriFill() {
let fill = buildTriPath()
GraphicsState {
CGContextAddPath(self.currentContext, fill)
CGContextFillPath(self.currentContext)
}
}
/////////////////////////////////////////////// ////////
func buildQuadPath() -> CGMutablePath {
let path = CGPathCreateMutable()
_ = array.map { $0.buildQuadPath(path) }
return path
}
func drawQuadPath() {
let path = buildQuadPath()
GraphicsState {
CGContextAddPath(self.currentContext, path)
CGContextStrokePath(self.currentContext)
}
}
func drawQuadFill() {
let fill = buildQuadPath()
GraphicsState {
CGContextAddPath(self.currentContext, fill)
CGContextFillPath(self.currentContext)
}
}
两个变量有助于确定是否按下按钮:
var squareB: Int = 0
var triangleB: Int = 0
@IBAction func makeTriangle(sender: AnyObject?) {
....................
....................
triangleB += 1
squareB = 0
}
@IBAction func makeRectangle(sender: AnyObject?) {
....................
....................
triangleB = 0
squareB += 1
}
drawRect
方法:
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
drawBG()
GraphicsState { self.drawMyPoints() }
if squareB >= 1 && triangleB == 0 {
buildQuadPath()
drawQuadPath()
drawQuadFill()
needsDisplay = true
}
else if triangleB >= 1 && squareB == 0 {
buildTriPath()
drawTriPath()
drawTriFill()
needsDisplay = true
}
drawBorder()
}
...最后是一个Context.swift
文件:
import Cocoa
import CoreGraphics
extension NSView {
var currentContext : CGContext? {
get {
let unsafeContextPointer = NSGraphicsContext.currentContext()?.graphicsPort
if let contextPointer = unsafeContextPointer {
let opaquePointer = COpaquePointer(contextPointer)
let context: CGContextRef = Unmanaged.fromOpaque(opaquePointer).takeUnretainedValue()
return context }
else { return nil }
}
}
func GraphicsState(drawStuff: () -> Void) {
CGContextSaveGState(currentContext)
drawStuff()
CGContextRestoreGState(currentContext)
}
}
//the end of code
答案 0 :(得分:3)
if (makeTriangle != nil) {
和if (makeRectangle != nil) {
没有多大意义。根据您的评论,makerRectangle
和makeTriangle
是按钮。通过你的陈述你正在检查它们的存在 - 我们可以假设它们总是存在 - 第一个if子句将永远执行。
你想要的是:创建将由按钮执行的方法。每种方法都会设置bool值或单个枚举值的组合,然后通过调用setNeedsDisplay()
告诉视图重绘。
答案 1 :(得分:3)
好的,因为我可以使用这种做法,我创建了一个example project来显示vikingosegundo和我的意思。
这是它的要点:
对于此示例,除了在GHShapeDemoView
中添加和删除形状外,我保留了所有相关代码。
我使用结构来定义形状,但将它们视为在绘图期间处理的数据的“单位”,添加到视图等。所有形状都保存在一个数组中,并且在绘制期间迭代并且所有找到的形状都被绘制使用简单的NSBezierPath
。
为了简单起见,我只是为每个形状选择了一些随机的固定点,在一个真正的项目中显然会以另一种方式确定(我懒得添加输入字段......)。
即使在这里,也有很多方法可以重构(可能)。例如,甚至可以使每个形状成为它自己的一个类(或者通常使用一个类来形成形状)。甚至可能是NSView的子类,然后导致“绘图区域”本身不是自定义视图,但是正常按钮和按钮按下相关形状视图将被添加为子视图。那么那可能也会摆脱所有这些计算点数的东西(主要是)。 在一个真实的项目中,我可能已经将形状作为图层子类,然后我将其添加到子视图中。我不是专家,但我认为这可能会带来性能上的好处,具体取决于有多少形状以及我是否会为它们制作动画。 (显然,使用OpenGL ES或其他东西可能会获得最高性能,但我对此没有任何线索,这远远超出了这个问题的范围。)
我希望这为您提供了一个很好的起点来处理您的绘图。如上所述,我强烈建议您以类似的方式重构项目,以正确定义您绘制的内容以及如何绘制它。如果你某种程度上必须依赖于在枚举或结构中保留点数据,那么在绘图数据结构中写下足够的映射器。
答案 2 :(得分:0)
以下是由Gero撰写的代码:
此代码适用于Swift 2.2。
//
// GHShapeDemoView.swift
// GHShapeDrawingExample
//
// Created by Gero Herkenrath on 21/10/2016.
// Copyright © 2016 Gero Herkenrath. All rights reserved.
//
import Cocoa
class GHShapeDemoView: NSView {
struct Shape {
var p1:CGPoint = NSMakePoint(0.0, 0.0)
var p2:CGPoint = NSMakePoint(0.0, 0.0)
var p3:CGPoint = NSMakePoint(0.0, 0.0)
var p4:CGPoint?
}
var shapes:[Shape] = []
override internal var flipped: Bool {
return true
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
NSColor.whiteColor().setFill()
let updatedRect = NSBezierPath.init(rect: dirtyRect)
updatedRect.fill()
for shape in shapes {
drawShape(shape)
}
}
func drawShape(shape:Shape) {
let shapePath = NSBezierPath()
shapePath.moveToPoint(shape.p1)
shapePath.lineToPoint(shape.p2)
shapePath.lineToPoint(shape.p3)
if let lastPoint = shape.p4 {
shapePath.lineToPoint(lastPoint)
}
shapePath.closePath()
NSColor.blackColor().setStroke()
shapePath.stroke()
}
func addTrapezoid(p1:NSPoint, p2:NSPoint, p3:NSPoint, p4:NSPoint) {
var shape = Shape()
shape.p1 = p1
shape.p2 = p2
shape.p3 = p3
shape.p4 = p4
shapes.append(shape)
}
func addTriangle(p1:NSPoint, p2:NSPoint, p3:NSPoint) {
var shape = Shape()
shape.p1 = p1
shape.p2 = p2
shape.p3 = p3
shapes.append(shape)
}
func removeShapeAt(index:Int) {
if index < shapes.count {
shapes.removeAtIndex(index)
}
}
}
/////////////////////////////////////////////// //
//
// ViewController.swift
// GHShapeDrawingExample
//
// Created by Gero Herkenrath on 21/10/2016.
// Copyright © 2016 Gero Herkenrath. All rights reserved.
//
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
if #available(OSX 10.10, *) {
super.viewDidLoad()
}
else {
// Fallback on earlier versions
}
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
// first Shape
let pointA1 = NSMakePoint(115.0, 10.0)
let pointA2 = NSMakePoint(140.0, 10.0)
let pointA3 = NSMakePoint(150.0, 40.0)
let pointA4 = NSMakePoint(110.0, 40.0)
// second Shape
let pointB1 = NSMakePoint(230.0, 10.0)
let pointB2 = NSMakePoint(260.0, 40.0)
let pointB3 = NSMakePoint(200.0, 40.0)
// thirdShape
let pointC1 = NSMakePoint(115.0, 110.0)
let pointC2 = NSMakePoint(140.0, 110.0)
let pointC3 = NSMakePoint(150.0, 140.0)
let pointC4 = NSMakePoint(110.0, 140.0)
@IBOutlet weak var shapeHolderView: GHShapeDemoView!
@IBAction func newTrapezoid(sender: AnyObject) {
if shapeHolderView.shapes.count < 1 {
shapeHolderView.addTrapezoid(pointA1, p2: pointA2, p3: pointA3, p4: pointA4)
}
else {
shapeHolderView.addTrapezoid(pointC1, p2: pointC2, p3: pointC3, p4: pointC4)
}
shapeHolderView.setNeedsDisplayInRect(shapeHolderView.bounds)
}
@IBAction func newTriangle(sender: AnyObject) {
shapeHolderView.addTriangle(pointB1, p2: pointB2, p3: pointB3)
shapeHolderView.setNeedsDisplayInRect(shapeHolderView.bounds)
}
@IBAction func removeLastShape(sender: AnyObject) {
if shapeHolderView.shapes.count > 0 {
shapeHolderView.removeShapeAt(shapeHolderView.shapes.count - 1)
shapeHolderView.setNeedsDisplayInRect(shapeHolderView.bounds)
}
}
}