Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
413 views
in Technique[技术] by (71.8m points)

macos - Make 2 contradictory methods work in drawRect

I'm writing an app building elements consisting of CGPoints. I have 2 buttons: makeRectangle and makeTriangle. For building/drawing stage I use three methods for rectangle and three methods for triangle inside drawRect.

I'm stuck with my code in drawRect. In if-else-statement each method swaps the building/drawing scheme for previous element every time a button pressed.

If I already have built rectangle and then I click makeTriangle button, I get new triangle but my rectangle turns into triangle with one unconnected point.

Is there a workaround or I shouldn't use drawRect method?

Here's an SO post on the drawRect topic: To drawRect or not to drawRect

Animation of incorrectly-drawn trapezoid and triangle

Image of correctly-drawn trapezoid and triangle

Element declaration:

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

Methods for building and drawing triangle and rectangle:

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

Two variables help determine whether button is pressed:

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 method:

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()
}

...and at last a Context.swift file:

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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

if (makeTriangle != nil) { and if (makeRectangle != nil) { doesnt make much sense. according to your comment, makerRectangle and makeTriangle are buttons. By your statements you are checking their existence — and we can assume they always exists — the first if-clause will always be executed.

what you want instead: create method that will be executed by the buttons. Each of this method will set either a combination of bool values or a single enum value and then tell the view to redraw by calling setNeedsDisplay().


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...