首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Cocoa:如何像在Mail.app中一样绘制插图文本?

Cocoa:如何像在Mail.app中一样绘制插图文本?
EN

Stack Overflow用户
提问于 2014-01-05 08:57:43
回答 3查看 542关注 0票数 3

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

EN

回答 3

Stack Overflow用户

发布于 2014-01-06 10:33:45

我有一些绘制浮雕单元格的代码(我相信最初写的是Jonathon Mah )。它可能做不到你想要的,但它会给你一个开始的地方:

代码语言:javascript
复制
@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
票数 4
EN

Stack Overflow用户

发布于 2014-01-19 20:03:36

请不要选择这个作为答案,我只是为了好玩而实现了上面的建议,并把它放在这里,因为它可能会在未来对某些人有用!

https://github.com/danieljfarrell/InnerShadowTextFieldCell

根据indragie和wil-shipley的建议,这里是NSTextFieldCell的一个子类,它用内部阴影绘制文本。

头文件,

代码语言:javascript
复制
// InnerShadowTextFieldCell.h
#import <Cocoa/Cocoa.h>
@interface InnerShadowTextFieldCell : NSTextFieldCell
@property (strong) NSShadow *innerShadow;
@end

现在是实现文件,

代码语言:javascript
复制
// 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中更改文本颜色和文本背景颜色属性,否则将看不到阴影。

票数 3
EN

Stack Overflow用户

发布于 2014-01-06 12:10:32

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

绘制带有内阴影的文本需要两个步骤。

1.获取文本的绘制路径

为了能够在文本字形内部绘制阴影,您需要从字符串创建bezier路径。苹果公司的示例代码SpeedometerView具有将方法-bezierWithFont:添加到NSStringa category。运行项目以查看如何使用此方法。

2.使用内部阴影填充路径

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20928743

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档