繁体   English   中英

Cocoa:如何在Mail.app中绘制插入文本?

[英]Cocoa: How to draw inset text as in Mail.app?

在此输入图像描述

如何在Cocoa(OS X)中绘制这种文本样式? 它似乎在几个Apple应用程序中使用,包括Mail(如上图所示)和Xcode侧边栏中的几个位置。 我环顾四周,但未能找到任何资源,建议如何重现这种特定的文本样式。 它看起来像一个插入阴影,我的第一个猜测是尝试使用NSShadow,模糊半径设置为负值,但显然只允许正值。 有任何想法吗?

我有一些代码可以绘制浮雕单元格(最初是Jonathon Mah编写的,我相信)。 它可能不会完全符合您的要求,但它会给您一个开始的地方:

@implementation DMEmbossedTextFieldCell

#pragma mark NSCell

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
{
    /* This method copies the three-layer method used by Safari's error page. That's accessible by forcing an
     * error (e.g. visiting <foo://>) and opening the web inspector. */

    // I tried to use NSBaselineOffsetAttributeName instead of shifting the frame, but that didn't seem to work
    const NSRect onePixelUpFrame = NSOffsetRect(cellFrame, 0.0, [NSGraphicsContext currentContext].isFlipped ? -1.0 : 1.0);
    const NSRange fullRange = NSMakeRange(0, self.attributedStringValue.length);

    NSMutableAttributedString *scratchString = [self.attributedStringValue mutableCopy];

    BOOL overDark = (self.backgroundStyle == NSBackgroundStyleDark);
    CGFloat (^whenLight)(CGFloat) = ^(CGFloat b) { return overDark ? 1.0 - b : b; };

    // Layer 1
    [scratchString addAttribute:NSForegroundColorAttributeName value:[NSColor colorWithCalibratedWhite:whenLight(1.0) alpha:1.0] range:fullRange];
    [scratchString drawInRect:cellFrame];

    // Layer 2
    BOOL useGradient = NO; // Safari 5.2 preview has switched to a lighter, solid color look for the detail text. Since we use the same class, use bold-ness to decide
    if (self.attributedStringValue.length > 0) {
        NSFont *font = [self.attributedStringValue attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL];
        if ([[NSFontManager sharedFontManager] traitsOfFont:font] & NSBoldFontMask)
            useGradient = YES;
    }

    NSColor *solidShade = [NSColor colorWithCalibratedHue:200/360.0 saturation:0.03 brightness:whenLight(0.41) alpha:1.0];
    [scratchString addAttribute:NSForegroundColorAttributeName value:solidShade range:fullRange];
    [scratchString drawInRect:onePixelUpFrame];

    // Layer 3 (Safari uses alpha of 0.25)
    [scratchString addAttribute:NSForegroundColorAttributeName value:[NSColor colorWithCalibratedWhite:whenLight(1.0) alpha:0.25] range:fullRange];
    [scratchString drawInRect:cellFrame];
}

@end

从截图中看,它看起来像是用内部阴影绘制的文本。 因此,使用模糊半径为0的标准NSShadow方法将不起作用,因为它仅在文本下方/上方绘制阴影。

使用内部阴影绘制文本有两个步骤。

1.获取文本的绘图路径

为了能够在文本字形内绘制阴影,您需要从字符串创建贝塞尔曲线路径。 苹果示例代码SpeedometerView一个类别 ,增加了该方法-bezierWithFont:NSString 运行该项目以查看如何使用此方法。

2.用内阴影填充路径

在bezier路径下绘制阴影很容易,但其中绘制阴影并非易事。 幸运的是, NSBezierPath + MCAdditions类别添加了-[NSBezierPath fillWithInnerShadow:]方法,以-[NSBezierPath fillWithInnerShadow:]这一过程。

请不要选择这个作为答案我刚刚实现了上述建议的乐趣并把它放在这里,因为它可能对将来有用的人有用!

https://github.com/danieljfarrell/InnerShadowTextFieldCell

NSTextFieldCell的子类,它使用内部阴影。

根据indragiewil-shipley的建议,这里是NSTextFieldCell的子类,它使用内部阴影绘制文本。

头文件,

// InnerShadowTextFieldCell.h
#import <Cocoa/Cocoa.h>
@interface InnerShadowTextFieldCell : NSTextFieldCell
@property (strong) NSShadow *innerShadow;
@end

现在是实现文件,

// InnerShadowTextFieldCell.m
#import "InnerShadowTextFieldCell.h"
// This class needs the NSString category -bezierWithFont: from,
// https://developer.apple.com/library/mac/samplecode/SpeedometerView/Listings/SpeedyCategories_m.html

@implementation InnerShadowTextFieldCell


- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {

    //// Shadow Declarations
    if (_innerShadow == nil) {
        /* Inner shadow has not been set, override here with default shadow.
           You may or may not want this behaviour. */
        _innerShadow = [[NSShadow alloc] init];
        [_innerShadow setShadowColor: [NSColor darkGrayColor]];
        [_innerShadow setShadowOffset: NSMakeSize(0.1, 0.1)];
        /* Trying to find a default shadow radius which will look good for
           a label of any size, let's get a rough estimate based on the
           hypotenuse of the cell frame. */
        [_innerShadow setShadowBlurRadius: 0.0075 * hypot(NSWidth(cellFrame), NSHeight(cellFrame)) ];
    }

    /* Because we are using the -bezierWithFont: we get a slightly 
       different path than had we used the superclass to drawn the 
       text path. This means that the background colour and text 
       colour looks odd if we use call the superclass's,
            -drawInteriorWithFrame:inView: method let's do that 
       drawing here. Not making the call to super might cause 
       problems for general use (?) but for a simple label is seems 
       to work OK */
    [self.backgroundColor setFill];
    NSRectFill(cellFrame);

    NSBezierPath *bezierPath = [self.title bezierWithFont:self.font];
    [self.textColor setFill];
    [bezierPath fill];


    /* The following is inner shadow drawing method is taken from Paint Code */

    ////// Bezier Inner Shadow
    NSShadow *shadow = _innerShadow;
    NSRect bezierBorderRect = NSInsetRect([bezierPath bounds], -shadow.shadowBlurRadius, -shadow.shadowBlurRadius);
    bezierBorderRect = NSOffsetRect(bezierBorderRect, -shadow.shadowOffset.width, shadow.shadowOffset.height);
    bezierBorderRect = NSInsetRect(NSUnionRect(bezierBorderRect, [bezierPath bounds]), -1, -1);

    NSBezierPath* bezierNegativePath = [NSBezierPath bezierPathWithRect: bezierBorderRect];
    [bezierNegativePath appendBezierPath: bezierPath];
    [bezierNegativePath setWindingRule: NSEvenOddWindingRule];

    [NSGraphicsContext saveGraphicsState];
    {
        NSShadow* shadowWithOffset = [shadow copy];
        CGFloat xOffset = shadowWithOffset.shadowOffset.width + round(bezierBorderRect.size.width);
        CGFloat yOffset = shadowWithOffset.shadowOffset.height;
        shadowWithOffset.shadowOffset = NSMakeSize(xOffset + copysign(0.0, xOffset), yOffset + copysign(0.1, yOffset));
        [shadowWithOffset set];
        [[NSColor grayColor] setFill];
        [bezierPath addClip];
        NSAffineTransform* transform = [NSAffineTransform transform];
        [transform translateXBy: -round(bezierBorderRect.size.width) yBy: 0];
        [[transform transformBezierPath: bezierNegativePath] fill];
    }
    [NSGraphicsContext restoreGraphicsState];


}
@end

这可能会变得更加强大,但它似乎只是绘制静态标签。

确保在Interface Builder中更改文本颜色文本背景颜色属性,否则您将无法看到阴影。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM