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

时间:2016-10-04 14:47:32

标签: macos if-statement swift2 drawrect cgcontext

我正在编写一个构建包含CGPoints的元素。我有2个按钮:makeRectanglemakeTriangle。对于构建/绘制阶段,我在矩形中使用了三种方法,在drawRect内使用了三种三角形方法。

我在drawRect中遇到了我的代码。在if-else-statement中,每次按下按钮时,每个方法都会交换前一个元素的构建/绘制方案。

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

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

以下是drawRect主题的SO帖子:To drawRect or not to drawRect

Animation of incorrectly-drawn trapezoid and triangle

Image of correctly-drawn trapezoid and triangle

元素声明:

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

3 个答案:

答案 0 :(得分:3)

if (makeTriangle != nil) {if (makeRectangle != nil) {没有多大意义。根据您的评论,makerRectanglemakeTriangle是按钮。通过你的陈述你正在检查它们的存在 - 我们可以假设它们总是存在 - 第一个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)
        }
    }
}