Как нарисовать конусообразную линию + овальную тень в Какао - PullRequest
9 голосов
/ 16 июня 2011

Справочная информация:

Снимок ниже - это Mail.app в OS X Lion. Когда список источников становится слишком длинным, над кнопками внизу списка источников появляется симпатичная теневая линия. При прокрутке список источников перемещается под эту теневую линию. Когда вы расширяете окно так, чтобы все в исходном списке помещалось без прокрутки, теневая линия исчезала.

Вопрос:

Как я могу нарисовать эту теневую линию, используя Какао? Я знаю о NSShadow и тому подобном, но мне кажется, здесь происходит нечто большее, чем просто тень. Есть линия, которая слегка исчезает в точках (как если бы вы применили градиентную маску к каждому концу в Photoshop.) Точно так же тень имеет овал и сужается по мере приближения к концу линий. Так что это не просто обычный NSShadow? (Это определенно не изображение, так как оно хорошо масштабируется при увеличении ширины исходного вида.)

Буду очень признателен за советы о том, как подойти к рисованию этой фигуры.

enter image description here

И для сторонников нет, это не нарушает NDA, так как Mail.app был публично показан Apple.

Ответы [ 2 ]

16 голосов
/ 16 июня 2011

Общая идея:

.

  1. Создайте слой "Слой A" с размерами 150px × 10px
    и заполните его Градиентом :
    • нижний цвет: #535e71 непрозрачность: 33%
    • верхний цвет: #535e71 непрозрачность: 0%
  2. Создайте слой «Слой B» с размерами 150px × 1px
    и заполните его сплошной #535e71 непрозрачностью: 50%
  3. Составьте"Слой A" и "Слой B" вместе в "Слой C".
  4. Применить отраженную градиентную маску от #ffffff до #000000 до "Слоя C".

Визуальные шаги:

enter image description here

Функциональный код:

MyView.h

#import <Cocoa/Cocoa.h>

@interface MyView : NSView {
@private

}

@end

MyView.m

#import "MyView.h"

@implementation MyView

- (CGImageRef)maskForRect:(NSRect)dirtyRect {
    NSSize size = [self bounds].size;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

    CGContextClipToRect(context, *(CGRect*)&dirtyRect);

    CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height);

    size_t num_locations = 3;
    CGFloat locations[3] = { 0.0, 0.5, 1.0 };
    CGFloat components[12] = {
        1.0, 1.0, 1.0, 1.0,  // Start color
        0.0, 0.0, 0.0, 1.0,  // Middle color
        1.0, 1.0, 1.0, 1.0,  // End color
    };

    CGGradientRef myGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, num_locations);

    CGPoint myStartPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPoint myEndPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));

    CGContextDrawLinearGradient(context, myGradient, myStartPoint, myEndPoint, 0);

    CGImageRef theImage = CGBitmapContextCreateImage(context);
    CGImageRef theMask = CGImageMaskCreate(CGImageGetWidth(theImage), CGImageGetHeight(theImage), CGImageGetBitsPerComponent(theImage), CGImageGetBitsPerPixel(theImage), CGImageGetBytesPerRow(theImage), CGImageGetDataProvider(theImage), NULL, YES);

    [(id)theMask autorelease];

    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);

    return theMask;
}

- (void)drawRect:(NSRect)dirtyRect {
    NSRect nsRect = [self bounds];
    CGRect rect = *(CGRect*)&nsRect;
    CGRect lineRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, (CGFloat)1.0);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
    CGContextClipToRect(context, *(CGRect*)&dirtyRect);
    CGContextClipToMask(context, rect, [self maskForRect:dirtyRect]);

    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = {
        0.315, 0.371, 0.450, 0.3,  // Bottom color
        0.315, 0.371, 0.450, 0.0  // Top color
    };

    CGGradientRef myGradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, num_locations);

    CGPoint myStartPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPoint myEndPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));

    CGContextDrawLinearGradient(context, myGradient, myStartPoint, myEndPoint, 0);

    CGContextSetRGBFillColor(context, 0.315, 0.371, 0.450, 0.5 );
    CGContextFillRect(context, lineRect);

    CGColorSpaceRelease(colorSpace);    
}

@end

(Мой первый раз, когда я использую чисто низкоуровневую CoreGraphics, поэтому, возможно, на уровне ниже оптимального, открыт для улучшений.)

Это фактический снимок экрана с кодом, приведенным выше:
enter image description here
Чертеж растягивается до размеров вида.

(Ранее у меня было две техники, показанные здесь: «Техника А» и «Техника Б».
«Техника Б» показала превосходные результаты, и ее было гораздо проще реализовать, поэтому я отказался от «Техники А».
Некоторые комментарии могут по-прежнему ссылаться на «Технику А». Просто игнорируйте их и наслаждайтесь полностью функциональным фрагментом кода.).

0 голосов
/ 16 июня 2011

Как насчет изображения, растянутого по горизонтали?

Или, если вы знаете, как сделать его в Photoshop, вы можете применить те же самые шаги программно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...