仪表的核心图形角度梯度 [英] Core Graphics angle gradient for gauge

查看:74
本文介绍了仪表的核心图形角度梯度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正尝试将角度渐变应用于使用自定义UIView类中编写的代码创建的破折号,如下所示.尽管需要进行调整,但我对到目前为止产生的结果感到满意.

I'm trying to apply an angle gradient to the dashes created with the code I've written inside a custom UIView class, as below. Although it needs tweaking, I'm happy with the results it produces so far.

鉴于视图初始化中的输入参数(如下),以及在iPad Air2上以纵向模式拍摄的768 * 768帧,它会产生以下量规:

Given the input parameters in the view initialisation (below), and a frame of 768 * 768 on an iPad Air2 in portrait mode, it produces the following gauge:

第一个量规

我想做的是使每个破折号穿过用户定义的渐变,例如绿色到红色,非常像这样(在Photoshop中显示):

What I'd like to do is to cause each of the dashes to step through a user-defined gradient, e.g. green to red, much like this (kludged in Photoshop):

带有颜色的量规

我搜索过很多东西,但找不到任何东西可以实现这一目标.唯一接近的东西使用不同的绘制方法,并且我想保留我的绘制例程.

I've searched high and low, and cannot find anything to achieve this. The only things that come close use different drawing methods, and I want to keep my drawing routine.

就我而言,我应该可以打电话:

As far as I'm concerned, I should simply be able to call:

CGContextSetStrokeColorWithColor(myContext,[渐变色在这里])

CGContextSetStrokeColorWithColor(myContext, [gradient color goes here])

就是这样,但是我不知道如何创建相关的颜色数组/渐变,并根据该数组中的索引更改线条绘制颜色.

inside the draw loop, and that's it, but I don't know how to create the relevant color array/gradient, and change the line drawing color according to an index into that array.

任何帮助将不胜感激.

- (void)drawRect:(CGRect)rect {

    myContext = UIGraphicsGetCurrentContext();
    UIImage *gaugeImage = [self radials:300 andSteps:3 andLineWidth:10.0];
    UIImageView *gaugeImageView = [[UIImageView alloc] initWithImage:gaugeImage];
    [self addSubview:gaugeImageView];

}

-(UIImage *)radials:(NSInteger)degrees andSteps:(NSInteger)steps andLineWidth:(CGFloat)lineWidth{

    UIGraphicsBeginImageContext(self.bounds.size);
    myContext = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(myContext, lineWidth);
    CGContextSetStrokeColorWithColor(myContext, [[UIColor blackColor] CGColor]);

    CGPoint center = CGPointMake(self.bounds.origin.x+(self.bounds.size.width/2), self.bounds.origin.y+(self.bounds.size.height/2));
    CGFloat r1 = center.x * 0.87f;
    CGFloat r2 = center.x * 0.95f;

    CGContextTranslateCTM(myContext, center.x, center.y);
    CGContextBeginPath(myContext);

    CGFloat offset  = 0;

    if(degrees < 360){
        offset = (360-degrees) / 2;
    }

    for(int lp = offset + 0 ; lp < offset + degrees+1 ; lp+=steps){

        CGFloat theta = lp * (2 * M_PI / 360);
        CGContextMoveToPoint(myContext, 0, 0);

        r1 = center.x * 0.87f;
        if(lp % 10 == 0){
            r1 = center.x * 0.81f;
        }

        CGContextMoveToPoint(myContext, sin(theta) * r1, cos(theta) * r1);
        CGContextAddLineToPoint(myContext, sin(theta) * r2, cos(theta) * r2);
        CGContextStrokePath(myContext);
    }

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

推荐答案

所以,您想要这样的东西:

So, you want something like this:

首先,提出一些温和的建议:

First, a couple of gentle suggestions:

  1. 不要在drawRect:内添加子视图.如果drawRect:被第二次调用(例如,视图的大小发生变化)怎么办?

  1. Don't add subviews inside drawRect:. What if drawRect: gets called a second time, if for example the view's size changes?

以下是

drawRect:方法的实现应该做的只是一件事:绘制内容.此方法不是更新应用程序的数据结构或执行与绘图无关的任何任务的地方.它应该配置绘图环境,绘制内容并尽快退出.而且,如果您可能经常调用drawRect:方法,则应尽一切努力优化绘图代码,并在每次调用该方法时尽可能少地绘制.

The implementation of your drawRect: method should do exactly one thing: draw your content. This method is not the place to be updating your application’s data structures or performing any tasks not related to drawing. It should configure the drawing environment, draw your content, and exit as quickly as possible. And if your drawRect: method might be called frequently, you should do everything you can to optimize your drawing code and draw as little as possible each time the method is called.

如果需要添加或删除子视图,则应在视图初始化时执行,或者最晚在layoutSubviews中执行.

If you need to add or remove subviews, you should do that when the view is initialized, or in layoutSubviews at the latest.

根本不需要绘制图像或使用图像视图. drawRect:的全部要点是绘制到当前的图形上下文中,UIKit已将其设置为以视图的后备存储为目标.

There's no need to draw into an image or use an image view at all. The whole point of drawRect: is to draw into the current graphics context, which UIKit has already set up to target the view's backing store.

撇开这些建议,Core Graphics不支持角度渐变.但是,对于您的图形,您可以分别为每个刻度线设置颜色,并获得相当不错的近似值,这就是我在上面创建图像的方式.使用 +[UIColor colorWithHue:saturation:brightness:alpha:] 来创建颜色,并进行计算基于刻度角的色调参数.

Those suggestions aside, there is no support for angular gradients in Core Graphics. However, for your graphic, you can set the color for each tick mark separately and get a pretty good approximation, which is how I created the image above. Use +[UIColor colorWithHue:saturation:brightness:alpha:] to create your color, calculating the hue parameter based on the tick angle.

如果将绘图代码分解为一个单独的类,则可以轻松地将其直接绘制到视图(在drawRect:中)或在需要时绘制到图像.这是界面:

If you factor out the drawing code into a separate class, it's easy to use it to draw either directly to a view (in drawRect:), or to an image if you need to. Here's the interface:

@interface RainbowGaugeAppearance: NSObject

@property (nonatomic) CGFloat startDegrees;
@property (nonatomic) CGFloat endDegrees;
@property (nonatomic) CGFloat degreesPerMajorTick;
@property (nonatomic) int subdivisionsPerMajorTick;
@property (nonatomic) CGFloat tickThickness;
@property (nonatomic) CGFloat startHue;
@property (nonatomic) CGFloat endHue;
@property (nonatomic) CGFloat outerRadiusFraction;
@property (nonatomic) CGFloat minorInnerRadiusFraction;
@property (nonatomic) CGFloat majorInnerRadiusFraction;

- (instancetype _Nonnull)init;
- (void)drawInRect:(CGRect)rect;
@end

执行:

@implementation RainbowGaugeAppearance

static CGFloat radiansForDegrees(CGFloat degrees) { return degrees * M_PI / 180; }

- (instancetype _Nonnull)init {
    if (self = [super init]) {
        _startDegrees = 120;
        _endDegrees = _startDegrees + 300;
        _degreesPerMajorTick = 30;
        _subdivisionsPerMajorTick = 10;
        _tickThickness = 4;
        _outerRadiusFraction = 0.95;
        _minorInnerRadiusFraction = 0.87;
        _majorInnerRadiusFraction = 0.81;
        _startHue = 1/ 3.0;
        _endHue = 0;
    }
    return self;
}

- (void)drawInRect:(CGRect)rect {
    CGContextRef gc = UIGraphicsGetCurrentContext();
    CGContextSaveGState(gc); {
        CGContextTranslateCTM(gc, CGRectGetMidX(rect), CGRectGetMidY(rect));
        CGContextSetLineWidth(gc, self.tickThickness);
        CGContextSetLineCap(gc, kCGLineCapButt);

        CGFloat outerRadius = _outerRadiusFraction / 2 * rect.size.width;
        CGFloat minorInnerRadius = _minorInnerRadiusFraction / 2 * rect.size.width;
        CGFloat majorInnerRadius = _majorInnerRadiusFraction / 2 * rect.size.width;

        CGFloat degreesPerTick = _degreesPerMajorTick / _subdivisionsPerMajorTick;
        for (int i = 0; ; ++i) {
            CGFloat degrees = _startDegrees + i * degreesPerTick;
            if (degrees > _endDegrees) { break; }

            CGFloat t = (degrees - _startDegrees) / (_endDegrees - _startDegrees);
            CGFloat hue = _startHue + t * (_endHue - _startHue);
            CGContextSetStrokeColorWithColor(gc, [UIColor colorWithHue:hue saturation:0.8 brightness:1 alpha:1].CGColor);

            CGFloat sine = sin(radiansForDegrees(degrees));
            CGFloat cosine = cos(radiansForDegrees(degrees));
            CGFloat innerRadius = (i % _subdivisionsPerMajorTick == 0) ? majorInnerRadius : minorInnerRadius;
            CGContextMoveToPoint(gc, outerRadius * cosine, outerRadius * sine);
            CGContextAddLineToPoint(gc, innerRadius * cosine, innerRadius * sine);
            CGContextStrokePath(gc);
        }
    } CGContextRestoreGState(gc);
}

@end

使用它绘制视图很简单:

Using it to draw a view is then trivial:

@implementation RainbowGaugeView {
    RainbowGaugeAppearance *_appearance;
}

- (RainbowGaugeAppearance *_Nonnull)appearance {
    if (_appearance == nil) { _appearance = [[RainbowGaugeAppearance alloc] init]; }
    return _appearance;
}

- (void)drawRect:(CGRect)rect {
    [self.appearance drawInRect:self.bounds];
}

@end

这篇关于仪表的核心图形角度梯度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆