Вопрос по ios, cocoa-touch, uiview, quartz-graphics, core-graphics – Создайте вид с отступом, найденный в UINavigationBarButton - программно

3

Я пытаюсь программно воссоздать вид кнопки с отступом, который можно увидеть на UINavigationBarButton. Не блестящий двухцветный вид или градиент, а только затенение по периметру:

enter image description here

Это выглядит как внутренняя темная тень по всему периметру вида, немного темнее в верхней части? А затем внешняя подсветка тени по нижнему периметру обзора.

Я немного поиграл с Core Graphics и экспериментировал с QuartzCore и затенением с view.layer.shadowRadius и .shadowOffset, но даже не могу заставить нижнюю подсветку выглядеть правильно. Я также не уверен, с чего начать, чтобы добиться темного затенения с внутренним смещением и светлого затенения с внешним смещением.

Он не "немного темнее в верхней части"; скорее всего, он имеет вертикальное смещение (свет сверху, расстояние от тени! = 0 в терминах Photoshop). Похоже, радиус тени составляет около 2 пикселей, а вертикальное расстояние составляет около 1 пикселя вниз. (именно поэтому он «кровоточит» и в верхней части) Nicolas Miari
Попробуйте покопаться здесь:stackoverflow.com/search?q=inner+shadow+core+graphics No Grabbing
Вы смотрели наPaintCode? Вы должны быть в состоянии воссоздать отступ, используя темную внутреннюю тень и светлую тень. David Rönnqvist

Ваш Ответ

2   ответа
2

Использование тени слоя не сделает этого. Вам нужен и светлая внешняя тень, и темная внутренняя тень, чтобы получить этот эффект. Слой может иметь только одну (внешнюю) тень. (Кроме того, тени слоев перерисовываются динамически, что вызывает рендеринг на базе процессора, который снижает производительность.)

Вам нужно будет сделать свой собственный рисунок с помощью CoreGraphics, либо в видеdrawRect: метод или слойdrawInContext: метод. (Или вы рисуете в контексте изображения, а затем повторно используете изображение.) Указанный рисунок будет в основном использоватьCGContext functions, (Я назову некоторые из них ниже, но по этой ссылке есть документация для них всех.)

Для круглой прямоугольной кнопки вам может показаться утомительным создание соответствующего CGPath - вместо этого вы можете использовать+[UIBezierPath bezierPathWithRoundedRect:cornerRadius:] и затем путиCGPath свойство для установки текущего пути контекста сCGContextAddPath.

Вы можете создать внутреннюю тень, установив обтравочный контур (см.CGContextClip и связанные с ними функции) по форме кнопки, настройка тени (см.CGContextSetShadowWithColor и связанные с ними функции), а затем рисуем вокруг фигуры, которую вы хотите заштриховать. Для внутренней тени, инсульт (CGContextStrokePath) круглый прямоугольник, который немного больше, чем ваша кнопка, с использованием толстой ширины штриха (CGContextSetLineWidth) так что "много" чернил " чтобы создать тень (помните, этот штрих не будет виден из-за обтравочного контура).

Вы можете создать внешнюю тень почти таким же образом - на этот раз не используйте обтравочный контур, потому что вы хотите, чтобы тень находилась вне фигуры, и заполните ее (CGContextFillPath) форму вашей кнопки вместо того, чтобы гладить ее. Обратите внимание, что рисование тени является своего рода «режимом»: вы сохраняете графическое состояние (CGContextSaveGState), установите тень, затем нарисуйте фигуру, тень которой вы хотите видеть (сама фигура не рисуется, когда вы находитесь в этом режиме), и, наконец, восстановите состояние (CGContextRestoreGState) выйти из «теневого режима». Поскольку этот режим не рисует фигуру, а только тень, вам необходимо рисовать саму фигуру отдельно.

Также есть приказ сделать все это. Это должно быть очевидно, если вы думаете о порядке, в котором вы рисуете эти вещи с помощью физического носителя: сначала нарисуйте внешнюю тень, затем заливку кнопки, затем внутреннюю тень. Вы можете добавить штрих после этого, если внутренняя тень не дает достаточно четкого контура.

Есть несколько инструментов рисования, которые могут выводить исходный код для CoreGraphics:помутнение это тот, который я использую. Однако будьте осторожны с ними, так как они генерируют код, который может быть неэффективным.

5

Кажется, что вы хотите, чтобы граница выглядела как тень. Поскольку тень имеет вид какого-то градиента, установка границы в качестве градиента будет невозможна с первого взгляда. Тем не менее, можно создать путь, представляющий границу, а затем заполнить его градиентом. Apple предоставляет то, что кажется малоизвестной функцией под названиемCGPathCreateCopyByStrokingPath, Это берет путь (скажем, прямоугольник с закругленными углами, например) и создает новый путь, который будет штрихом старого пути с учетом настроек, которые вы передаете в функцию (например, ширина линии, настройка соединения / ограничения, ограничение митры и т. Д. ). Допустим, вы определили путь (это не совсем то, что предоставляет Apple, а то, что он похож):

+ (UIBezierPath *) bezierPathForBackButtonInRect:(CGRect)rect withRoundingRadius:(CGFloat)radius{
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGPoint mPoint = CGPointMake(CGRectGetMaxX(rect) - radius, rect.origin.y);
    CGPoint ctrlPoint = mPoint;
    [path moveToPoint:mPoint];

    ctrlPoint.y += radius;
    mPoint.x += radius;
    mPoint.y += radius;
    if (radius > 0) [path addArcWithCenter:ctrlPoint radius:radius startAngle:M_PI + M_PI_2 endAngle:0 clockwise:YES];

    mPoint.y = CGRectGetMaxY(rect) - radius;
    [path addLineToPoint:mPoint];

    ctrlPoint = mPoint;
    mPoint.y += radius;
    mPoint.x -= radius;
    ctrlPoint.x -= radius;
    if (radius > 0) [path addArcWithCenter:ctrlPoint radius:radius startAngle:0 endAngle:M_PI_2 clockwise:YES];

    mPoint.x = rect.origin.x + (10.0f);
    [path addLineToPoint:mPoint];

    [path addLineToPoint:CGPointMake(rect.origin.x, CGRectGetMidY(rect))];

    mPoint.y = rect.origin.y;
    [path addLineToPoint:mPoint];

    [path closePath];
    return path;
}

Это возвращает путь, аналогичный кнопке возврата Apple (я использую это в своем приложении). Я добавил этот метод (наряду с десятками других) в качестве категории в UIBezierPath.

Теперь давайте добавим эту внутреннюю тень в процедуру рисования:

- (void) drawRect:(CGRect)rect{
    UIBezierPath *path = [UIBezierPath bezierPathForBackButtonInRect:rect withRoundingRadius:5.0f];
    //Just fill with blue color, do what you want here for the button
    [[UIColor blueColor] setFill]; 
    [path fill];

    [path addClip]; //Not completely necessary, but borders are actually drawn 'around' the path edge, so that half is inside your path, half is outside adding this will ensure the shadow only fills inside the path

    //This strokes the standard path, however you might want to might want to  inset the rect, create a new 'back button path' off the inset rect and create the inner shadow path off that.  
    //The line width of 2.0f will actually show up as 1.0f with the above clip: [path addClip];, due to the fact that borders are drawn around the edge 
    UIBezierPath *innerShadow = [UIBezierPath bezierPathWithCGPath: CGPathCreateCopyByStrokingPath(path.CGPath, NULL, 2.0f, path.lineCapStyle, path.lineJoinStyle, path.miterLimit)];
    //You need this, otherwise the center (inside your path) will also be filled with the gradient, which you don't want
    innerShadow.usesEvenOddFillRule = YES;
    [innerShadow addClip];

    //Now lets fill it with a vertical gradient
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGPoint start = CGPointMake(0, 0);
    CGPoint end = CGPointMake(0, CGRectGetMaxY(rect));
    CGFloat locations[2] = { 0.0f, 1.0f};
    NSArray *colors =  [NSArray arrayWithObjects:(id)[UIColor colorWithWhite:.7f alpha:.5f].CGColor, (id)[UIColor colorWithWhite:.3f alpha:.5f].CGColor, nil];
    CGGradientRef gradRef = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), (__bridge CFArrayRef)colors, locations);
    CGContextDrawLinearGradient(context, gradRef, start, end, 0);
    CGGradientRelease(gradRef);
}

Теперь это просто простой пример. Я не сохраняю / не восстанавливаю контексты или что-либо еще, что вы, вероятно, захотите сделать. Есть вещи, которые вы, возможно, все еще захотите сделать, чтобы сделать его лучше, например, можете вставить «тень». путь, если вы хотите использовать нормальную границу. Возможно, вы захотите использовать больше / разных цветов и мест. Но это должно помочь вам начать.

UPDATE

Есть еще один метод, который вы можете использовать для создания этого эффекта. Я написал алгоритм для создания произвольных кривых Безье в основной графике. Это может быть использовано для создания эффекта, который вы ищете. Это пример того, как я использую его в своем приложении:

Bevelled Back Button

Вы передаете в подпрограмму CGContextRef, CGPathRef, размер скоса и какие цвета вы хотите использовать для подсветки / тени.

Код, который я использовал для этого, можно найти здесь:Github - алгоритм скашивания.

Я также объясняю код и мою методологию здесь:Скос-формы в основной графике

Похожие вопросы