在 UIView 中实现钟面
Implementing a clock face in 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
我敢肯定这是我做错的非常简单的事情 - 我的数学错误。任何人都可以看到该错误(以及任何其他可以提出的改进建议,请记住(当然)这只是一个简单的测试工具)。
正如我在评论中所写,考虑放弃在 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.
如果你想稍后更新手,只需将手层存储为变量并再次变换旋转它们(记住先变换为身份!)。
我正在尝试在 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
我敢肯定这是我做错的非常简单的事情 - 我的数学错误。任何人都可以看到该错误(以及任何其他可以提出的改进建议,请记住(当然)这只是一个简单的测试工具)。
正如我在评论中所写,考虑放弃在 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.
如果你想稍后更新手,只需将手层存储为变量并再次变换旋转它们(记住先变换为身份!)。