继承的控件不绘制

Posted

技术标签:

【中文标题】继承的控件不绘制【英文标题】:Inherited control does not draw 【发布时间】:2014-06-19 06:47:36 【问题描述】:

我正在尝试通过继承 NSProgressIndicator 来创建自己的进度条。我在 Xcode 6 中使用 Playground 编写了代码,它工作正常(内容绘制正确)。

只要我将类放到 GUI 上(作为“自定义视图”或“不确定进度指示器”的类型),尽管 drawRect(dirtyRect: NSRect) 方法已被覆盖并被调用,但控件不会绘制由框架决定。

这是我的代码:

class AbProgressBar : NSProgressIndicator

    let drawStep = 10

    var rounded: Bool = true
    var margin: CGFloat = 4.0
    var barColor: NSColor = NSColor.blueColor()
    var barBorderColor: NSColor = NSColor.whiteColor()
    var borderColor: NSColor = NSColor.grayColor()
    var backgroundColor: NSColor = NSColor.blackColor()

    init(coder: NSCoder!)
    
        println(__FUNCTION__)
        super.init(coder: coder)
    

    init(frame frameRect: NSRect)
    
        println("\(__FUNCTION__) with frame \(frameRect)")
        super.init(frame: frameRect)
    

    override func drawRect(dirtyRect: NSRect)
    
        println(__FUNCTION__)

        // Here we calculate the total value area from minValue to maxValue in order to find out the percental width of the inner bar
        let area = minValue < 0 && maxValue < 0 ? abs(minValue) + maxValue : abs(maxValue) + abs(minValue)
        let currentPercentageFilled: Double = doubleValue >= maxValue ? maxValue : 100 / area * doubleValue
        let innerWidth = (frame.width - (margin * 2)) / 100 * currentPercentageFilled

        let radOuterX = rounded ? frame.height / 2 : 0
        let radOuterY = rounded ? frame.width / 2 : 0
        let radInnerX = rounded ? (frame.height - margin) / 2 : 0
        let radInnerY = rounded ? innerWidth / 2 : 0

        // The inner frame depends on the width filled by the current value
        let innerFrame = NSRect(x: frame.origin.x + margin, y: frame.origin.y + margin, width: innerWidth, height: frame.height - (margin * 2))

        let pathOuter: NSBezierPath = NSBezierPath(roundedRect: frame, xRadius: radOuterX, yRadius: radOuterY)
        let pathInner: NSBezierPath = NSBezierPath(roundedRect: innerFrame, xRadius: radInnerX, yRadius: radInnerY)

        let gradientOuter: NSGradient = NSGradient(startingColor: NSColor.whiteColor(), endingColor: backgroundColor)

        let gradientInner: NSGradient = NSGradient(startingColor: NSColor.grayColor(), endingColor: barColor)

        gradientOuter.drawInBezierPath(pathOuter, angle: 270.0)

        if(pathInner.elementCount > 0)
        
            gradientInner.drawInBezierPath(pathInner, angle: 270.0)
        

        borderColor.set()
        pathOuter.stroke()

        barBorderColor.set()
        pathInner.stroke()
    

在操场上使用它可以正常工作。将其设置为放置在 UI 上的控件的类型不起作用。

有人知道可能出了什么问题吗?

编辑:只是为了添加该信息:我使用 Xcode 6 中包含的新“调试视图层次结构”功能检查了视图。结果:控件确实存在。

【问题讨论】:

drawRect 在我的测试中被调用 OK,但之后似乎也调用了原始实现。我只用 NSColor.redColor().set(); 替换了您的绘图代码; NSBezierPath.fillRect(self.bounds) 并正确绘制它,但随后将原始绘制在顶部。 这很奇怪......我也试过了(只是用任何颜色填写frame),它也可以工作。但是,一旦我切换回原始代码,它就不再起作用了。并且(增加混淆):在 Xcode 的调试视图中观看 pathOuter 会显示正确的路径... 【参考方案1】:

我想通了... 问题是我使用了超类的frame 属性,而不是传递给drawRect(...) 方法的dirtyRect 参数。

我刚刚用dirtyRect 交换了对frame 的所有访问权限,现在它可以工作了。

我觉得有点奇怪,因为(据我了解)dirtyRect 应该指代与frame 相同的矩形。

【讨论】:

以上是关于继承的控件不绘制的主要内容,如果未能解决你的问题,请参考以下文章

自定义view之kotlin绘制精简小米时间控件

自定义控件的绘制流程和测量规则

Android中的自定义控件

VC绘制控件如何防止闪烁

Android进阶之绘制-自定义View完全掌握

自定义控件