在UIView中实现一个钟面

时间:2015-04-01 08:50:03

标签: objective-c cocoa-touch uiview geometry

我正试图在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

我确信这是非常简单的,我做错了 - 我的数学错误。任何人都可以看到这个错误(以及任何其他可以提出的改进建议都会受到欢迎,当然这是一个简单的测试工具)。

1 个答案:

答案 0 :(得分: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。

如果您想稍后更新手,只需将手层存储为变量并再次旋转它们(只需记住首先转换为身份!)。