Question

To add a badge to your application in the dock is easy, you just call [NSDockTile setBadgeLabel]. But I want to add a badge to a generic NSView. Is there any way to do this? Or failing that, how do you go about drawing one so that it looks consistent with the rest of the system UI?

Was it helpful?

Solution

There is no builtin methods to draw a badge on NSView so you should draw it by yourself. Here is my code to draw a badge:

- (void)drawBadgeImageWithText:(NSString*)text atPoint:(NSPoint)point
{
    NSSize badgeSize = [self badgeSizeForString:text];
    NSRect badgeRect = NSMakeRect(point.x - badgeSize.width, point.y - badgeSize.height, badgeSize.width, badgeSize.height);

    [NSGraphicsContext saveGraphicsState];

    // Set a shadow
    NSShadow* shadow = [[NSShadow alloc] init];
    shadow.shadowColor = [[NSColor blackColor] colorWithAlphaComponent:0.4];
    shadow.shadowBlurRadius = 1;
    shadow.shadowOffset = NSMakeSize(0, -1);
    [shadow set];

    // Draw white border
    NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:badgeRect xRadius:badgeSize.height / 2.0 yRadius:badgeSize.height / 2.0];
    [[NSColor whiteColor] setFill];
    [path fill];

    [NSGraphicsContext restoreGraphicsState];

    // Fill the background with red gradient 
    badgeRect = NSInflateRect(badgeRect, -1.5, -1.5);
    path = [NSBezierPath bezierPathWithRoundedRect:badgeRect xRadius:badgeSize.height / 2.0 yRadius:badgeSize.height / 2.0];
    [[NSColor colorWithCalibratedRed:192.0 / 255.0 green:0.0 blue:0.0 alpha:1.0] setFill];
    [path fill];

    NSGradient* gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedRed:241.0 / 255.0 green:113.0 / 255.0 blue:115.0 / 255.0 alpha:1.0]
                                                         endingColor:[NSColor colorWithCalibratedRed:192.0 / 255.0 green:0.0 blue:0.0 alpha:1.0]];
    [gradient drawInBezierPath:path angle:-90.0];

    // Draw the text
    NSMutableParagraphStyle* paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    [paragraphStyle setAlignment:NSCenterTextAlignment];

    NSDictionary* textAttributes = @{NSForegroundColorAttributeName:[NSColor whiteColor], NSParagraphStyleAttributeName:paragraphStyle};

    [text drawInRect:badgeRect withAttributes:textAttributes];
}

- (NSSize)badgeSizeForString:(NSString*)string
{
    NSDictionary* attributes = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:13] forKey: NSFontAttributeName];
    NSSize size = [string sizeWithAttributes:attributes];

    // Paddings
    size.height += 2.0;
    size.width += 12.0;

    return size;
}

OTHER TIPS

I ported Dmitry's answer to Swift 3; here it is in case it saves anyone else some time.

func badgeSizeForString(string:String) -> NSSize {
    var size = string.size(withAttributes: [NSFontAttributeName: NSFont.systemFont(ofSize: 13.0)])

    // Paddings
    size.height += 2.0
    size.width += 12.0

    return size
}

func drawBadgeImageWithText(text:NSString, atPoint point: NSPoint) -> Void {
    let badgeSize = badgeSizeForString(string: text as String)
    var badgeRect = NSMakeRect(point.x - badgeSize.width,
                               point.y - badgeSize.height,
                               badgeSize.width,
                               badgeSize.height)

    NSGraphicsContext.saveGraphicsState()

    // Set a shadow
    let shadow = NSShadow()
    shadow.shadowColor = NSColor.black.withAlphaComponent(0.4)
    shadow.shadowBlurRadius = 1;
    shadow.shadowOffset = NSMakeSize(0, -1);
    shadow.set()

    // Draw white border
    var path = NSBezierPath(roundedRect: badgeRect,
                            xRadius:badgeSize.height / 2.0,
                            yRadius:badgeSize.height / 2.0)
    NSColor.white.setFill()
    path.fill()

    NSGraphicsContext.restoreGraphicsState()

    // Fill the background with red gradient
    badgeRect = NSInsetRect(badgeRect, -1.5, -1.5)
    path = NSBezierPath(roundedRect: badgeRect,
                        xRadius:badgeSize.height / 2.0,
                        yRadius:badgeSize.height / 2.0)
    NSColor(calibratedRed:192.0 / 255.0,
            green:0.0,
            blue:0.0,
            alpha:1.0).setFill()
    path.fill()

    let gradient = NSGradient(starting: NSColor(calibratedRed:241.0 / 255.0,
                                                green:113.0 / 255.0,
                                                blue:115.0 / 255.0,
                                                alpha:1.0),
                              ending: NSColor(calibratedRed:192.0 / 255.0,
                                              green:0.0,
                                              blue:0.0,
                                              alpha:1.0))
    gradient?.draw(in: path, angle:-90.0)

    // Draw the text
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center

    let textAttributes = [ NSForegroundColorAttributeName: NSColor.white, NSParagraphStyleAttributeName: paragraphStyle ]
    text.draw(in: badgeRect, withAttributes: textAttributes)
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top