2017-02-04 5 views
-1

Я пытаюсь применить градиент угла к тире, созданному с кодом, написанным внутри пользовательского класса UIView, как показано ниже. Хотя он нуждается в настройке, я доволен результатами, которые он производит до сих пор.Градиент угла градиента ядра для датчика

Учитывая входные параметры в окне просмотра инициализации (ниже), и рамки 768 * 768 на IPad в AIR2 в портретном режиме, он производит следующий калибр:

First gauge

Что я бы как сделать, чтобы заставить каждую из тире перейти через определяемый пользователем градиент, например зеленого до красного, так же, как это (kludged в 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; 
} 
+0

Не злоупотреблять 'drawRect' подобное. 'drawRect' предназначен для рисования - не для добавления subviews. Помните, что 'drawRect' вызывается много раз, и вы просто добавляете этот subview снова и снова, что смешно. – matt

+0

Точка снята! Виноват! – Max

ответ

1

Насколько я понимаю, я должен просто быть в состоянии назвать CGContextSetStrokeColorWithColor

Реальность, однако, не заинтересован в «насколько вы обеспокоены». Вы описываете градиент угла. Реальность такова, что нет встроенного средства Core Graphics для создания градиента угла.

Однако вы можете сделать это легко с хорошей библиотекой, такой как AngleGradientLayer. Тогда просто сделать градиент угла и использовать рисунок вашего манометра как маска .

Таким образом, я получил это - не kludged в Photoshop, но сделано исключительно вживую, в прошивкой, используя 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 
+0

Отлично. SO говорят, что не следует использовать комментарии, такие как «спасибо», но я понятия не имею, почему, так что спасибо за простое решение и за урок. – Max

+0

Проблема с 'AngleGradientLayer' заключается в том, что он интерполирует линейный путь через пространство RGB. Вот почему желтый и оранжевый выглядят так мутно. Интерполяция через пространство HSB обычно дает лучшие результаты. –

+0

Кроме того, Макс, помните, что вам нужно будет обновить фрейм представления 'mask', если размер изображения будет изменен, потому что ни макет, ни автореализация не влияют на« маску »представления. –

2

Итак, вы хотите что-то вроде этого:

rainbow gauge view

Во-первых, пара нежных предложений:

  1. Не добавляйте subviews внутри drawRect:. Что делать, если drawRect: вызывается во второй раз, если, например, изменяется размер представления?

    Вот что View Programming Guide for iOS говорит о реализации drawRect::

    Реализация вашего метода drawRect: должен делать то одно: обратить ваше содержание. Этот метод не является местом для обновления структур данных приложения или выполнения каких-либо задач, не связанных с чертежом. Он должен настроить среду рисования, нарисовать свой контент и выйти как можно быстрее. И если ваш метод drawRect: может быть вызван часто, вы должны сделать все возможное, чтобы оптимизировать код чертежа и рисовать как можно меньше при каждом вызове метода.

    Если вам нужно добавить или удалить subviews, вы должны сделать это, когда представление инициализировано, или в layoutSubviews не позднее.

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

Эти предложения в стороне, нет поддержки угловых градиентов в 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 
+0

Отлично. Опять же, советую не комментировать, как «спасибо», но что еще я могу сказать! – Max

+0

Если ответы, размещенные здесь, помогли вам, пожалуйста, повысьте их. Также нажмите галочку рядом с наиболее полезным ответом, чтобы принять ее. –

Смежные вопросы