Subclass NSProgressIndicator

2019-05-04 04:35发布

问题:

i like to subclass a NSProgressIndicator. I've used this code and i set the Subclass in the Interface Builder:

- (void)drawRect:(NSRect)dirtyRect {
NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
CGFloat radius = rect.size.height / 2;
NSBezierPath *bz = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[bz setLineWidth:2.0];
[[NSColor blackColor] set];
[bz stroke];

rect = NSInsetRect(rect, 2.0, 2.0);
radius = rect.size.height / 2;
bz = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[bz setLineWidth:1.0];
[bz addClip];
rect.size.width = floor(rect.size.width * ([self doubleValue] / [self maxValue]));
NSRectFill(rect);

When the app starts its looks like this:

But during the copy progress the old bar shows up.

Whats wrong?

回答1:

It seems that the progress bar's progress is not drawn in drawRect:, so just overriding drawRect: is not enough. However, if you make the progress bar layer backed, you are responsible for doing all the drawing.

From the documentation:

The view class automatically creates a backing layer for you (using makeBackingLayer if overridden), and you must use the view class’s drawing mechanisms.

Check the "Core Animation Layer" in IB or add this to your sub class:

- (void)awakeFromNib {
    [super awakeFromNib];
    [self setWantsLayer:YES];
}


回答2:

I followed the above advice (thanks!), but unfortunately discovered that when the NSProgressIndicator was resized, it disappeared, but only on the first viewing (it was inside a drawer).

Rather than try and understand what was happening, I realised you don't actually need the old control, as what I was doing is very simple (a strength indicator that changes color). Just create a subclass of NSView.

So this is it:

@interface SSStrengthIndicator : NSView
/// Set the indicator based upon a score from 0..4
@property (nonatomic) double strengthScore;
@end

@implementation SSStrengthIndicator
- (void)setStrengthScore:(double)strength
{
    if (_strengthScore != strength) {
        _strengthScore = strength;
        [self setNeedsDisplay:YES];
   }
}

- (void)drawRect:(NSRect)dirtyRect
{
    NSRect rect = NSInsetRect([self bounds], 1.0, 2.0);
    double  val = (_strengthScore + 1) * 20;
    if (val <= 40)
        [[NSColor redColor] set];
    else if (val <= 60)
        [[NSColor yellowColor] set];
    else
        [[NSColor greenColor] set];

    rect.size.width = floor(rect.size.width * (val / 100.0));
    [NSBezierPath fillRect:rect];
}
@end