仪表

时间:2017-02-04 02:11:29

标签: ios core-graphics gradient angle gauge

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

鉴于视图初始化(下方)中的输入参数,以及纵向模式下iPad Air2上的768 * 768帧,它会产生以下指标:

First gauge

我想做的是让每个短划线逐步执行用户定义的渐变,例如绿色到红色,很像这样(在Photoshop中克服):

Gauge with colours

我搜索了高低,找不到任何东西来实现这一目标。唯一接近的东西使用不同的绘图方法,我想保持我的绘图程序。

就我而言,我应该只能致电:

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

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

非常感谢任何帮助。

- (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;
}

2 个答案:

答案 0 :(得分:2)

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

rainbow gauge view

首先,提出几点温和的建议:

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

    以下是View Programming Guide for iOS关于实施drawRect:的说法:

      

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

    如果您需要添加或删除子视图,则应在初始化视图时执行此操作,或者最迟在layoutSubviews中执行此操作。

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

  3. 除了这些建议外,Core Graphics中不支持角度渐变。但是,对于图形,您可以分别为每个刻度标记设置颜色,并获得非常好的近似值,这就是我创建上图的方式。使用+[UIColor colorWithHue:saturation:brightness:alpha:]创建颜色,根据刻度角计算色调参数。

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

    @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
    

    使用它来绘制视图是微不足道的:

    @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
    

答案 1 :(得分:1)

  

就我而言,我应该只能调用CGContextSetStrokeColorWithColor

然而,就你和你所关注的而言,现实并不感兴趣。&#34;。您正在描述角度渐变。实际情况是,没有内置的Core Graphics工具可用于创建角度渐变。

但是,您可以使用AngleGradientLayer这样的好库轻松完成。然后绘制角度渐变并将您的仪表绘图用作蒙版是一件简单的事情。

通过这种方式,我得到了这个 - 在Photoshop中使用,但在iOS中使用AngleGradientLayer完全实时完成,再加上刚刚复制并粘贴的radials:andSteps:andLineWidth:方法并用于生成面具:

enter image description here

这是我必须编写的唯一代码。首先,生成角度梯度图层:

+ (Class)layerClass {
    return [AngleGradientLayer class];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        AngleGradientLayer *l = (AngleGradientLayer *)self.layer;
        l.colors = [NSArray arrayWithObjects:
           (id)[UIColor colorWithRed:1 green:0 blue:0 alpha:1].CGColor,
           (id)[UIColor colorWithRed:0 green:1 blue:0 alpha:1].CGColor,
           nil];
        l.startAngle = M_PI/2.0;
    }
    return self;
}

其次,面具(这部分是在Swift中,但那是无关紧要的):

let im = self.v.radials(300, andSteps: 3, andLineWidth: 10)
let iv = UIImageView(image:im)
self.v.mask = iv