2015-04-01 3 views
0

Я пытаюсь реализовать циферблат в UIView - без большой удачи. Проблема в том, что руки на циферблате оказываются в совершенно неправильном положении (т. Е. Указывают на неправильное время). Я уверен, что логика моего кода верна, но ясно, что что-то значительно не так.Реализация циферблата в UIView

Мое тестовое приложение - приложение с одним представлением iOS. ViewController.h выглядит следующим образом:

#import <UIKit/UIKit.h> 

@interface Clock : UIView { 
    UIColor* strokeColour; 
    UIColor* fillColour; 
    float strokeWidth; 
    NSDate* clockTime; 
    CGPoint clockCentre; 
    float clockRadius; 
} 
- (id)initWithCentre:(CGPoint)centre 
       radius:(float)radius 
     borderWidth:(float)width 
      facecolour:(UIColor*)fill 
      handcolour:(UIColor*)handFill 
       time:(NSDate*)time; 
@end 

@interface ViewController : UIViewController 

@end 

и ViewController.m выглядит следующим образом:

#import "ViewController.h" 

@implementation Clock 

- (id)initWithCentre:(CGPoint)centre 
       radius:(float)radius 
     borderWidth:(float)width 
      facecolour:(UIColor*)fill 
      handcolour:(UIColor*)handFill 
       time:(NSDate*)time { 

    CGRect frame = CGRectMake(centre.x-radius, 
           centre.y-radius, 
           radius*2, 
           radius*2); 

    if (self = [super initWithFrame:frame]) { 
    // Initialization code 
    strokeColour = fill; 
    fillColour = handFill; 
    strokeWidth = width; 
    clockRadius = radius; 
    clockTime = time; 
    clockCentre.x = frame.size.width/2; 
    clockCentre.y = frame.size.height/2; 

    } 
    return self; 
} 

- (void)drawRect:(CGRect)rect { 

    double red, green, blue, alpha; 

    CGRect circleRect; 
    circleRect.origin.x = rect.origin.x+strokeWidth; 
    circleRect.origin.y = rect.origin.y+strokeWidth; 
    circleRect.size.width = rect.size.width - (strokeWidth*2); 
    circleRect.size.height = rect.size.height - (strokeWidth*2); 
    CGContextRef contextRef = UIGraphicsGetCurrentContext(); 

    CGContextSetLineWidth(contextRef, strokeWidth); 

    [fillColour getRed:&red green:&green blue:&blue alpha:&alpha]; 
    CGContextSetRGBFillColor(contextRef, red, green, blue, alpha); 

    [strokeColour getRed:&red green:&green blue:&blue alpha:&alpha]; 
    CGContextSetRGBStrokeColor(contextRef, red, green, blue, alpha); 

    CGContextFillEllipseInRect(contextRef, circleRect); 

    CGContextStrokeEllipseInRect(contextRef, circleRect); 

    CGContextSetLineWidth(contextRef, strokeWidth); 

    [strokeColour getRed:&red green:&green blue:&blue alpha:&alpha]; 
    CGContextSetRGBStrokeColor(contextRef, red, green, blue, alpha); 

    float hourAngle, minuteAngle, secondAngle, angle; 
    double endX, endY; 

    NSCalendar *cal = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 
    NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; 
    NSDateComponents *components = [cal components:units fromDate:clockTime]; 
    hourAngle = (components.hour /12.0) * M_PI * 2.0; 
    minuteAngle = (components.minute/60.0) * M_PI * 2.0; 
    secondAngle = (components.second/60.0) * M_PI * 2.0; 

    //minute hand 
    angle = minuteAngle; 
    endX = cos(angle) * (clockRadius*0.85) + clockCentre.x; 
    endY = sin(angle) * (clockRadius*0.85) + clockCentre.y; 
    CGContextMoveToPoint(contextRef,clockCentre.x,clockCentre.y); 
    CGContextAddLineToPoint(contextRef,endX,endY); 
    CGContextStrokePath(contextRef); 

    //hour hand 
    angle = hourAngle; 
    endX = cos(angle) * (clockRadius*0.65) + clockCentre.x; 
    endY = sin(angle) * (clockRadius*0.65) + clockCentre.y; 
    CGContextSetLineWidth(contextRef, strokeWidth*1.5); 
    CGContextMoveToPoint(contextRef,clockCentre.x,clockCentre.y); 
    CGContextAddLineToPoint(contextRef,endX,endY); 
    CGContextStrokePath(contextRef); 
} 

@end 

@interface ViewController() 

@end 

@implementation ViewController 

- (UIView*)drawClockFaceWithCentre:(CGPoint)centre 
          radius:(float)radius 
           time:(NSDate*)time 
          colour:(UIColor*)colour 
        backgroundColour:(UIColor*)bgcolour { 

    UIView* clock = [[Clock alloc]initWithCentre:centre 
              radius:radius 
            borderWidth:6.0 
             facecolour:bgcolour 
             handcolour:colour 
              time:time]; 

    clock.backgroundColor = [UIColor clearColor]; 
    return clock; 
} 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 

    CGPoint centre; 
    centre.x=200; 
    centre.y=200; 

    [self.view addSubview:[self drawClockFaceWithCentre:centre 
               radius:100 
                time:[NSDate date] 
               colour:[UIColor blackColor] 
             backgroundColour:[UIColor whiteColor]]]; 
} 

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

@end 

Я уверен, что это что-то очень простое, что я сделал неправильно - ошибка в моей математике. Может ли кто-нибудь увидеть ошибку (и любые другие предложения по улучшениям, которые можно было бы сделать, были бы очень желанными, имея в виду (разумеется), что это всего лишь простой тестовый жгут).

+0

Рассмотрите возможность использования CAShapeLayer для часов (по одному для каждого) и сохранения их в качестве переменных. Затем просто измените их вращение – Kegluneq

+0

Это не устранит проблему, правда? Потому что мне все равно нужно их вращать - и это моя проблема в ротации. Углы ошибочны. – headbanger

+0

Нет, но это сделает более понятный код, упростит обслуживание и упростит вычисления (вместо тригонометрии, где легко ошибиться, вам просто нужно получить правильный процент 2pi). – Kegluneq

ответ

2

Как я писал в комментарии, рассмотрите возможность делать все в drawRect и использовать слои. Кроме того, вместо вычисления тригонометрии, чтобы нарисовать правильный путь, рассмотрите только рисование рук в 12:00 и преобразование поверните их с правильным углом. Вы можете сделать это, как это в INIT ...

CAShapeLayer *faceLayer = [CAShapeLayer layer]; 
faceLayer.frame = self.bounds; 
faceLayer.fillColor = [UIColor whiteColor].CGColor; 
faceLayer.strokeColor = [UIColor blackColor].CGColor; 
faceLayer.path = [UIBezierPath bezierPathWithOvalInRect:faceLayer.bounds].CGPath; 
faceLayer.lineWidth = 3.0; 
[self.layer addSublayer:faceLayer]; 

CGPoint middle = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 

NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 
NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; 
NSDateComponents *components = [cal components:units fromDate:time]; 

UIBezierPath *path = [UIBezierPath bezierPath]; 
[path moveToPoint:middle]; 
[path addLineToPoint:CGPointMake(middle.x, middle.y-radius*0.5)]; 

CGFloat hourAreaAngle = (2*M_PI)/12.0; 
CAShapeLayer *hourLayer = [CAShapeLayer layer]; 
hourLayer.frame = self.bounds; 
hourLayer.strokeColor = [UIColor redColor].CGColor; 
hourLayer.lineWidth = 3.0; 
hourLayer.path = path.CGPath; 
[self.layer addSublayer:hourLayer]; 
hourLayer.transform = CATransform3DMakeRotation((components.hour/12.0*(M_PI*2.0))+(hourAreaAngle*(components.minute/60.0)), 0.0, 0.0, 1.0); 

[path removeAllPoints]; 
[path moveToPoint:middle]; 
[path addLineToPoint:CGPointMake(middle.x, middle.y-radius*0.8)]; 

CAShapeLayer *minuteLayer = [CAShapeLayer layer]; 
minuteLayer.frame = self.bounds; 
minuteLayer.strokeColor = [UIColor blueColor].CGColor; 
minuteLayer.lineWidth = 2.0; 
minuteLayer.path = path.CGPath; 
[self.layer addSublayer:minuteLayer]; 
minuteLayer.transform = CATransform3DMakeRotation(components.minute/60.0*(M_PI*2.0), 0.0, 0.0, 1.0); 

[path removeAllPoints]; 
[path moveToPoint:middle]; 
[path addLineToPoint:CGPointMake(middle.x, middle.y-radius)]; 

CAShapeLayer *secondsLayer = [CAShapeLayer layer]; 
secondsLayer.frame = self.bounds; 
secondsLayer.strokeColor = [UIColor greenColor].CGColor; 
secondsLayer.lineWidth = 1.0; 
secondsLayer.path = path.CGPath; 
[self.layer addSublayer:secondsLayer]; 
secondsLayer.transform = CATransform3DMakeRotation(components.second/60.0*(M_PI*2.0), 0.0, 0.0, 1.0); 

Примечание Я положил цвета и ширины линий, потому что я ленивый и не хочу, чтобы проверить код: P.

Если вы хотите обновить руки позже, просто сохраните слои рук в качестве переменных и преобразуйте их снова (просто помните, чтобы сначала преобразовать в личность!).

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