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

Make 2 contradictory methods work in drawRect

我正在编写一个由 CGPoints 组成的应用构建元素。我有 2 个按钮:makeRectanglemakeTriangle。对于 building/drawing 阶段,我在 drawRect.

中对 rectangle 使用三种方法,对 triangle 使用三种方法

我受困于 drawRect 中的代码。在 if-else-statement 中,每次按下按钮时,每个方法都会为前一个元素交换 building/drawing 方案。

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

是否有解决方法 或者我不应该使用 drawRect 方法?

这是关于 drawRect 主题的 SO post: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 { [=11=].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 { [=12=].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

if (makeTriangle != nil) {if (makeRectangle != nil) { 没有多大意义。根据您的评论,makerRectanglemakeTriangle 是按钮。通过您的陈述,您正在检查它们的存在——我们可以假设它们始终存在——第一个 if 子句将始终被执行。

您想要什么:创建将由按钮执行的方法。每个方法都将设置 bool 值的组合或单个枚举值,然后通过调用 setNeedsDisplay().

告诉视图重绘

好的,因为我可以使用我创建的练习 example project 来展示 vikingosegundo 和我的意思。

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

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

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

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

这是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)
        }
    }
}