【问题标题】:Make 2 contradictory methods work in drawRect使 2 个相互矛盾的方法在 drawRect 中起作用
【发布时间】:2016-10-04 14:47:32
【问题描述】:

我正在编写一个由 CGPoints 组成的应用程序构建元素。我有 2 个按钮:makeRectanglemakeTriangle。对于构建/绘图阶段,我在drawRect 中使用了三种rectangle 方法和三种triangle 方法。

我在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。如果你按下一个按钮(我猜你启用了某种点击监听器),状态会变为 1。所以应该只绘制两个选项中的一个。检查您的代码,如果您的默认状态已正确初始化为 0,并在绘制之后(或之前)更改回 0。
  • 你必须用更多的代码来支持我们。代码现在的方式,只应该绘制具有有效状态的代码,并且方法的顺序没有区别,因此它们是条件块
  • @AndyFedoroff:我重新创建了要再次编译的项目。请参阅拉取请求。通过查看代码,我想知道为什么您对点而不是表示由点组成的形状的数据结构进行操作。
  • 我认为这个问题受到XY-Problem的影响。
  • @AndyFedoroff,仅仅因为你没有让它工作,并不意味着它根本不起作用。

标签: macos if-statement swift2 drawrect cgcontext


【解决方案1】:

好的,因为我可以使用这种做法,所以我创建了一个 example project 来说明 vikingosegundo 和我的意思。

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

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

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

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

【讨论】:

  • 谢谢。我去看看。
  • 我会在 10 小时内奖励赏金。
  • 很高兴我能帮上忙。我意识到这意味着要进行相当多的重构才能进入您的项目,但我相信这最终也会帮助您维护代码。 :) 保重!
  • 我需要大规模重构))但这是必要的。
  • @Gero,做得很好。不过,我会给形状一个点数组,以允许任何类型的形状。
【解决方案2】:

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

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

【讨论】:

  • 我必须使用标签还是在这里没用?
  • 标签几乎总是没用的。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 2022-12-04
    • 2016-08-09
    • 2014-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 1970-01-01
    相关资源
    最近更新 更多