Как преобразовать CAShapeLayer в CAGradientLayer?

мой код создания CAShapeLayer

UIBezierPath *circle = [UIBezierPath bezierPathWithArcCenter:CGPointMake(75, 125)
                                                          radius:50
                                                      startAngle:0
                                                        endAngle:1.8 * M_PI
                                                       clockwise:YES];

    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    [circleLayer setFrame:CGRectMake(200 - 50, 300 - 50, 100, 100)];
    circleLayer.path   = circle.CGPath;
    circleLayer.bounds = CGPathGetBoundingBox(circle.CGPath);
    circleLayer.strokeColor = [UIColor colorWithRed:0.4 green:1.0 blue:0.2 alpha:0.5].CGColor;
    [circleLayer setFillColor:[UIColor clearColor].CGColor];
    circleLayer.lineWidth   = 3.0;

    if ([circleLayer animationForKey:@"SpinAnimation"] == nil) {
        CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        animation.fromValue = [NSNumber numberWithFloat:0.0f];
        animation.toValue = [NSNumber numberWithFloat: 2 * M_PI];
        animation.duration = 2.0f;
        animation.repeatCount = INFINITY;
        animation.removedOnCompletion = NO;
        [circleLayer addAnimation:animation forKey:@"SpinAnimation"];
    }
    [self.view.layer addSublayer:circleLayer];

Я хочу сделать CAGradientLayer вместо CAShapeLayer.

ПРИЧИНА: мне нужно использовать градиентный цвет в слое, что невозможно в CAShapeLayer.

Я хочу использовать градиент от желтого к черному на круге.

Изображение :

введите здесь описание изображения

В моем требуемом выводе цвет извлекается из фонового изображения и добавляется немного черного в конец или непрозрачность 0 внутри слоя.

Любая идея, предложение.

Спасибо.


person Sam Shaikh    schedule 28.11.2015    source источник
comment
Фиксирован ли цвет фона представления? Я имею в виду, что цвет фона — это какой-то цвет, который вы уже знаете (скажем, белый).   -  person Cheng-Yu Hsu    schedule 28.11.2015
comment
Да, цвет фона View исправлен @ChengYuHsu   -  person Sam Shaikh    schedule 28.11.2015


Ответы (2)


Один быстрый и грязный трюк - добавить слой градиента в качестве подслоя, а затем добавить еще несколько слоев с цветами фона, такими же, как у суперпредставления (в исходном вопросе он белый).

Например:

  1. Добавьте слой градиента (и установите правильный cornerRadius).

заполненный градиентный слой

  1. Добавьте слой меньшего размера с белым фоном в центре слоя с градиентом (это создаст кольцо).

кольцо

  1. Создайте небольшой сегмент дуги с белым цветом фона.

кольцо с зазором

Однако это может не подходить для просмотра с фоновыми изображениями. Чтобы решить эту проблему, вы можете создать собственный подкласс UIView и переопределить метод drawRect.

Предположим, что есть переменные экземпляра: startColor, endColor, radius, strokeWidth и mirrored.

Переменная mirrored используется для управления положением промежутка (отрезанная левая сторона или правая сторона) и направлением вращения анимации (по часовой стрелке или против часовой стрелки).

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGContextSetBlendMode(ctx, kCGBlendModeNormal);

    CGFloat gradientColors[4 * 2];

    extractColorComponents(_startColor, gradientColors, 0);
    extractColorComponents(_endColor, gradientColors, 4);


    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, gradientColors, NULL, 2);
    CGColorSpaceRelease(baseSpace);
    baseSpace = nil;

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
    CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
    CGGradientRelease(gradient);
    gradient = nil;

    // fill 'clear' color
    CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
    CGContextSetStrokeColorWithColor(ctx, [UIColor clearColor].CGColor);

    CGContextSetBlendMode(ctx, kCGBlendModeClear);

    CGFloat innerCircleRadius = _radius - _strokeWidth;

    // fill an 'empty' hole inside the gradient part
    CGContextFillEllipseInRect(ctx, (CGRect){
        {_strokeWidth, _strokeWidth},
        {innerCircleRadius * 2, innerCircleRadius * 2}
    });


    // fill an 'empty' segment of arc
    CGFloat startAngle = _mirrored ? (0.9 * M_PI) : (-0.1 * M_PI);
    CGFloat endAngle = startAngle + 0.2 * M_PI;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:(CGPoint){_radius, _radius}
                                                        radius:_radius - _strokeWidth * 0.5
                                                    startAngle:startAngle
                                                      endAngle:endAngle
                                                     clockwise:YES];
    path.lineWidth = _strokeWidth;
    CGContextAddPath(ctx, path.CGPath);
    CGContextSetLineWidth(ctx, _strokeWidth + 2);
    CGContextStrokePath(ctx);

    CGContextSetBlendMode(ctx, kCGBlendModeNormal);
}

Вот полная реализация представления.

Конечный результат:

анимированный счетчик

Два кольца:

Вы можете добавить два кольца в свой viewDidLoad или куда-то еще с помощью следующего кода для достижения желаемого эффекта:

- (void)viewDidLoad 
{
    [super viewDidLoad];

    UIColor *startColor = [UIColor colorWithHue:0.02 saturation:0.74 brightness:0.91 alpha:1];
    UIColor *endColor = [UIColor colorWithHue:0.57 saturation:0.76 brightness:0.86 alpha:1];

    CGPoint center = CGPointMake(160, 200);

    Spinner *outerSpinner = [[Spinner alloc] initWithCenter:center
                                                     radius:50
                                                strokeWidth:3
                                                 startColor:startColor
                                                   endColor:endColor
                                                   mirrored:NO];

    Spinner *innerSpinner = [[Spinner alloc] initWithCenter:center
                                                     radius:40
                                                strokeWidth:3
                                                 startColor:startColor
                                                   endColor:endColor
                                                   mirrored:YES];


    [self.view addSubview:outerSpinner];
    [self.view addSubview:innerSpinner];

    [outerSpinner startAnimating];
    [innerSpinner startAnimating];

}

Результат:

двойные анимированные кольца

person Cheng-Yu Hsu    schedule 28.11.2015
comment
Уважаемый, я добился этого. У меня сейчас другая проблема. Я не могу сделать два. :) Если вы видите мое изображение, там два круглых, один по часовой стрелке, другой против часовой стрелки :) - person Sam Shaikh; 28.11.2015
comment
Если бы вы могли помочь мне сделать два, один по часовой стрелке, другой против часовой стрелки. Поскольку у нас есть полный круг, мы можем видеть его штрих, так как же мы можем добиться этого с помощью двух кругов? - person Sam Shaikh; 28.11.2015
comment
Вы можете создать 2 экземпляра вида Spinner с разными радиусами и добавить соответствующую анимацию для каждого вида. - person Cheng-Yu Hsu; 28.11.2015
comment
Но, дорогая, цвета будут перекрываться :) Поскольку оба находятся в одном положении, один круг меньше другого. - person Sam Shaikh; 28.11.2015
comment
Невозможно добавить два круга. Предоставление EXC_BAD_ACCESS. Spinner *spinner = [[Spinner alloc] initWithCenter:CGPointMake(75, 125) radius:50 strokeWidth:3 startColor:[UIColor yellowColor] endColor:[UIColor blackColor]]; Spinner *spinner2 = [[Spinner alloc] initWithCenter:CGPointMake(75, 125) radius:40 strokeWidth:3 startColor:[UIColor yellowColor] endColor:[UIColor blackColor]]; [self.view addSubview:spinner]; [self.view addSubview:spinner2]; - person Sam Shaikh; 28.11.2015
comment
Я прокомментировал эту строку //CGColorRelease(colorRef); - person Sam Shaikh; 28.11.2015
comment
Вы правы, эту строку нужно закомментировать, иначе цвет будет освобожден и его нельзя будет использовать снова. - person Cheng-Yu Hsu; 29.11.2015
comment
@SamShaikh Я обновил свой ответ, пожалуйста, проверьте его. - person Cheng-Yu Hsu; 29.11.2015
comment
Я также рекомендую вам проверить линию выпуска, CGColorRelease. Это была проблема. Я прокомментировал это вчера. - person Sam Shaikh; 29.11.2015

Свойство маски в CALayer использует альфа-компонент слоя маски, чтобы определить, что должно быть видимым, а что нет. Поскольку оба ваших цвета (черный и белый) полностью непрозрачны (без прозрачности), маска не действует. Чтобы исправить это, вы должны изменить один из цветов на чистый цвет:

gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], 
                                            (id)[[UIColor clearColor] CGColor], nil];

ИЗМЕНИТЬ

class Colors {
let colorTop = UIColor(red: 192.0/255.0, green: 38.0/255.0, blue: 42.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 35.0/255.0, green: 2.0/255.0, blue: 2.0/255.0, alpha: 1.0).CGColor

let gl: CAGradientLayer

init() {
    gl = CAGradientLayer()
    gl.colors = [ colorTop, colorBottom]
    gl.locations = [ 0.0, 1.0]
}
}
person Moin Shirazi    schedule 28.11.2015
comment
О, отлично, но можете ли вы объяснить, что такое градиент в вашем коде! - person Sam Shaikh; 28.11.2015
comment
Итак, можете ли вы найти какой-либо отдельный объект, имеющий CAGradientLayer? Я думаю, вы не поняли мой вопрос. Просто мне нужно изменить цвет обводки CAShapeLayer или использовать CAGradientLayer с самого начала. - person Sam Shaikh; 28.11.2015