html 绘制ios时钟,iOS-模仿苹果时钟选择控件

最近看了苹果自带应用时钟上的时间选择工具感觉挺巧妙的,就尝试着模仿它做出一个控件工具。工程Demo运行效果如下:

AppleAlram.gif

根据时钟选择工具上面的功能,大概可以确定,圆环的绘制我们可以通过CAShapeLayer结合UIBezierPath绘制出来,当拖动起始点或者结束点View时,通过手势判断拖动的角度,从而改变UIBezierPath的角度,并且让起始点或者结束点View根据拖动的角度对中心点进行公转,并且自身进行自转。如果拖动圆环的话,改变UIBezierPath角度的同时,还要让起始点和结束点View同时进行公转和自转。

1.时钟AlarmView

时钟View是该控件的核心区,里面包含了图形的绘制和旋转,旋转类型的判断等多种处理。

核心代码:

-(CAShapeLayer *)alramLayer{

if (!_alramLayer) {

_alramLayer = [[CAShapeLayer alloc] init];

_alramLayer.bounds = CGRectMake(0,0, self.bounds.size.width, self.bounds.size.height);

_alramLayer.path = [self drawAlarmPathWithStartAngle:self.startAngle endAngle:self.endAngle].CGPath;

_alramLayer.fillColor = UIColor.orangeColor.CGColor;

}

return _alramLayer;

}

#pragma mark - Method - Method -

-(void)beiginRotationWithAngle:(CGFloat)angle beiginPiont:(CGPoint)point{

switch (self.rotationType) {

case kRotationType_StartAngle:

[self changeStartAngle:angle];

break;

case kRotationType_EndAngle:

[self changeEndAngle:angle];

break;

case kRotationType_CircularingLocation:

[self changeCircularingLocation:angle];

break;

default:

break;

}

}

/** 改变起始时间 */

-(void)changeStartAngle:(CGFloat)startAngle{

NSLog(@"角度差 = %f",fabs(self.endAngle - self.startAngle - startAngle));

if (fabs(self.endAngle - self.startAngle - startAngle) >360) {//修复BUG

if (startAngle > 0) {

startAngle = startAngle -360;

}else{

startAngle = startAngle +360;

}

}

NSLog(@"角度差2 = %f",fabs(self.endAngle - self.startAngle - startAngle));

self.sleepSuperView.transform = CGAffineTransformMakeRotation(kDgreesToRadoans(self.startAngle+startAngle));//公转

self.sleepView.transform = CGAffineTransformMakeRotation(-kDgreesToRadoans(self.startAngle+startAngle));//自转

self.alramLayer.path = [self drawAlarmPathWithStartAngle:startAngle+self.startAngle endAngle:self.endAngle].CGPath;

}

/** 改变结束时间 */

-(void)changeEndAngle:(CGFloat)endAngle{

if (fabs(self.startAngle - self.endAngle - endAngle) >360) {

if (endAngle > 0) {

endAngle = endAngle -360;

}else{

endAngle = endAngle +360;

}

}

NSLog(@"角度差 = %f",fabs(self.endAngle - self.startAngle - endAngle));

self.ringSuperView.transform = CGAffineTransformMakeRotation(kDgreesToRadoans(self.endAngle+endAngle));

self.ringView.transform = CGAffineTransformMakeRotation(-kDgreesToRadoans(self.endAngle+endAngle));

self.alramLayer.path = [self drawAlarmPathWithStartAngle:self.startAngle endAngle:self.endAngle+endAngle].CGPath;

}

/** 改变圆环位置 */

-(void)changeCircularingLocation:(CGFloat)angle{

self.sleepSuperView.transform = CGAffineTransformMakeRotation(kDgreesToRadoans(self.startAngle+angle));//公转

self.sleepView.transform = CGAffineTransformMakeRotation(-kDgreesToRadoans(self.startAngle+angle));//自转

self.ringSuperView.transform = CGAffineTransformMakeRotation(kDgreesToRadoans(self.endAngle+angle));

self.ringView.transform = CGAffineTransformMakeRotation(-kDgreesToRadoans(self.endAngle+angle));

self.alramLayer.path = [self drawAlarmPathWithStartAngle:self.startAngle+angle endAngle:self.endAngle +angle].CGPath;

}

/** 旋转类型 */

-(kRotationType)rotationTypeWithPiont:(CGPoint)piont{

CGPoint alarmViewCenter = CGPointMake(kAlarmViewRadius, kAlarmViewRadius);

CGPoint startCenter = CGPointMake(cos(((_currentStartAngle-90)/180)*M_PI)*(kAlarmViewRadius-kIconViewHW/2) +kAlarmViewRadius, sin(((_currentStartAngle-90)/180)*M_PI)*(kAlarmViewRadius-kIconViewHW/2) +kAlarmViewRadius);

CGPoint endCenter = CGPointMake(cos(((_currentEndAngle-90)/180)*M_PI)*(kAlarmViewRadius-kIconViewHW/2) +kAlarmViewRadius, sin(((_currentEndAngle-90)/180)*M_PI)*(kAlarmViewRadius-kIconViewHW/2) +kAlarmViewRadius);

if ([UIView distanceBetweenPointA:alarmViewCenter AndPiontB:piont] >= kAlarmViewRadius-kIconViewHW && [UIView distanceBetweenPointA:alarmViewCenter AndPiontB:piont] <= kAlarmViewRadius) {

if ([UIView distanceBetweenPointA:startCenter AndPiontB:piont] < kIconViewHW/2) {

return kRotationType_StartAngle;

}else if ([UIView distanceBetweenPointA:endCenter AndPiontB:piont] < kIconViewHW/2){

return kRotationType_EndAngle;

}else{

return kRotationType_CircularingLocation;

}

}

return kRotationType_None;

}

/** 绘制BezierPath */

-(UIBezierPath *)drawAlarmPathWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle{

CGRect circleRect = CGRectMake(kAlarmViewRadius,kAlarmViewRadius, self.bounds.size.width, self.bounds.size.height);

UIBezierPath* circlePath = [UIBezierPath bezierPath];

[circlePath addArcWithCenter: CGPointMake(CGRectGetMidX(circleRect), CGRectGetMidY(circleRect)) radius: circleRect.size.width/2 startAngle: kDgreesToRadoans(startAngle) endAngle: kDgreesToRadoans(endAngle) clockwise: YES];

[circlePath addLineToPoint: CGPointMake(CGRectGetMidX(circleRect), CGRectGetMidY(circleRect))];

[circlePath closePath];

_currentStartAngle = fmodf(startAngle,360);

_currentEndAngle = fmodf(endAngle, 360);

self.costTimeLbl.attributedText = [self timeBlockWithAngle:_currentEndAngle - _currentStartAngle];

self.beginTime = [self beginTimeWithAngle:_currentStartAngle];

self.endTime = [self endTimeWithAngle:_currentEndAngle];

if (self.delegate && [self.delegate respondsToSelector:@selector(alramViewIsChangedWithBeginTime:endTime:)]) {

[self.delegate alramViewIsChangedWithBeginTime:self.beginTime endTime:self.endTime];

}

return circlePath;

}

2.手势的处理类 DWRotaionGestureRecognizer

根据上面圆盘的效果,拖动手势时,我们需要知道起始点与拖动点之间的角度和方向。

核心代码:

/**拖动结束后自动重置 */

- (void)reset

{

[super reset];

// _previousRotation = [self rotation];

_previousRotation = 0;

_currentRotation = 0;

}

#pragma mark - eventResponse - Method -

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

[super touchesBegan:touches withEvent:event];

self.startingPoint = [[touches anyObject] locationInView:self.view];

self.state = UIGestureRecognizerStateBegan;

if (self.rotaionGestureRecognizerDelegate && [self.rotaionGestureRecognizerDelegate respondsToSelector:@selector(touchesBegan:withEvent:)]) {

[self.rotaionGestureRecognizerDelegate touchesBegan:touches withEvent:event];

}

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

[super touchesMoved:touches withEvent:event];

CGPoint point = [[touches anyObject] locationInView:self.view];

self.currentRotation = [UIView angleBetweenPoint1:self.startingPoint point2:point AndCenter:self.center];

self.state = UIGestureRecognizerStateChanged;

if (self.rotaionGestureRecognizerDelegate && [self.rotaionGestureRecognizerDelegate respondsToSelector:@selector(touchesMoved:withEvent:)]) {

[self.rotaionGestureRecognizerDelegate touchesMoved:touches withEvent:event];

}

}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

[super touchesEnded:touches withEvent:event];

self.endPoint = [[touches anyObject] locationInView:self.view];

self.currentRotation = [UIView angleBetweenPoint1:self.startingPoint point2:self.endPoint AndCenter:self.center];

self.state = UIGestureRecognizerStateEnded;

if (self.rotaionGestureRecognizerDelegate && [self.rotaionGestureRecognizerDelegate respondsToSelector:@selector(touchesEnded:withEvent:)]) {

[self.rotaionGestureRecognizerDelegate touchesEnded:touches withEvent:event];

}

}

- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

{

[super touchesCancelled:touches withEvent:event];

self.state = UIGestureRecognizerStateCancelled;

if (self.rotaionGestureRecognizerDelegate && [self.rotaionGestureRecognizerDelegate respondsToSelector:@selector(touchesCancelled:withEvent:)]) {

[self.rotaionGestureRecognizerDelegate touchesCancelled:touches withEvent:event];

}

}

计算两点间角度和距离的类别 UIView+DWAngle

核心代码:

/** 两个坐标点的角度 */

+ (CGFloat)angleBetweenPoint1:(CGPoint)first point2:(CGPoint)second AndCenter:(CGPoint)center{

// θ=arctan[(y2-y0)/(x2-x0)]-arctan[(y1-y0)/(x1-x0)];

CGPoint centeredPoint1 = CGPointMake(first.x - center.x, first.y - center.y);

CGPoint centeredPoint2 = CGPointMake(second.x - center.x, second.y - center.y);

CGFloat firstAngle = angleBetweenOriginAndPointA(centeredPoint1);

CGFloat secondAngle = angleBetweenOriginAndPointA(centeredPoint2);

CGFloat rads = secondAngle - firstAngle;

return rads;

}

/** 两点的距离 */

+(CGFloat)distanceBetweenPointA:(CGPoint)pointA AndPiontB:(CGPoint)pointB{

// (y2-y1)²+(x2-x1)²=d² sqrt() pow(5, 2)

CGFloat a = pow(pointB.x-pointA.x, 2);

CGFloat b = pow(pointB.y-pointA.y, 2);

return sqrt(a+b);

}

/** 某点和原点间的角度 */

+(CGFloat)angleBetweenOriginAndPointA:(CGPoint)p{

return angleBetweenOriginAndPointA(p);

}

CGFloat angleBetweenOriginAndPointA(CGPoint p) {

if (p.x == 0) {

return signA(p.y) * M_PI;

}

CGFloat angle = atan(-p.y / p.x); // '-' because negative ordinates are positive in UIKit

// atan() is defined in [-pi/2, pi/2], but we want a value in [0, 2*pi]

// so we deal with these special cases accordingly

switch (quadrantForPointA(p)) {

case 1:

case 2: angle += M_PI; break;

case 3: angle += 2* M_PI; break;

}

return angle;

}

/** 点的象限 */

NSInteger quadrantForPointA(CGPoint p) {

if (p.x > 0 && p.y < 0) {

return 0;

} else if (p.x < 0 && p.y < 0) {

return 1;

} else if (p.x < 0 && p.y > 0) {

return 2;

} else if (p.x > 0 && p.y > 0) {

return 3;

}

return 0;

}

NSInteger signA(CGFloat num) {

if (num == 0) {

return 0;

} else if (num > 0) {

return 1;

} else {

return -1;

}

}

以上是核心代码部分,感兴趣的读者可以到Github进行查阅:Github传送门

希望项目工程对您有所帮助,谢谢阅读。

你可能感兴趣的:(html,绘制ios时钟)