簡體   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