在drawRect中使2个相互矛盾的方法起作用

ARG*_*Geo 5 macos if-statement cgcontext drawrect swift2

我正在编写一个由CGPoints组成的应用程序构建元素.我有2个按钮:makeRectanglemakeTriangle.对于建筑/绘图阶段,我使用三种方法用于矩形,三种方法用于三角形内部drawRect.

我坚持使用我的代码drawRect.在if-else-statement每次按下按钮时,每种方法都会为前一个元素交换构建/绘制方案.

如果我已经建立了矩形,然后我点击了makeTriangle按钮,我得到了新的三角形,但我的矩形变成了一个三角形,有一个未连接的点.

有解决方法还是我不应该使用drawRect方法?

这是关于该drawRect主题的SO帖子:drawRect或不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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

构建和绘制三角形和矩形的方法:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

////////////////////////////////////////////////// /////

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

两个变量有助于确定是否按下了按钮:

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
}
Run Code Online (Sandbox Code Playgroud)

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()
}
Run Code Online (Sandbox Code Playgroud)

......最后是一个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
Run Code Online (Sandbox Code Playgroud)

Ger*_*ero 3

好吧,因为我可以使用实践,所以我创建了一个示例项目来展示 vikingosegundo 和我的意思。

其要点如下:
对于这个示例,我保留了除了在 .a 文件中添加和删除形状之外的所有相关代码GHShapeDemoView。我使用结构来定义形状,但将它们视为在绘图、添加到视图等过程中处理的一个数据“单元”。所有形状都保存在一个数组中,在绘图期间进行迭代,并绘制所有找到的形状使用一个简单的NSBezierPath.

为了简单起见,我只是为每个形状选择了一些随机固定点,在一个真实的项目中,这显然会以另一种方式确定(我懒得添加输入字段......)。

即使在这里也有很多重构的方法(可能)。例如,甚至可以将每个形状设为自己的一类(或者对一般形状使用一个类)。甚至可能是 NSView 的子类,这将导致“绘图区域”本身不是自定义视图,而是普通视图和按下按钮时相关形状视图将被添加为子视图。那么这可能也会摆脱所有这些积分计算的东西(大部分)。在实际项目中,我可能会选择形状作为图层子类,然后将其添加到子视图中。我不是专家,但我认为这可能会带来性能优势,具体取决于形状的数量以及我是否会对它们进行动画处理。(显然,使用 OpenGL ES 或其他东西可能会获得最高的性能,但我对此一无所知,这远远超出了这个问题的范围)。

我希望这为您的绘图提供了一个良好的起点。如上所述,我强烈建议以类似的方式重组您的项目,以正确定义您绘制的内容以及绘制方式的流程。如果您以某种方式必须依赖于将点数据保存在枚举或结构或其他内容中,请为您的绘图数据结构编写足够的映射器。