Quellcode durchsuchen

网络教室节拍器速度滑块处理

Steven vor 10 Monaten
Ursprung
Commit
20315d65b0

+ 149 - 19
KulexiuForTeacher/KulexiuForTeacher/Module/TXClassRoom/View/RoundSlider/IACircularSlider.m

@@ -11,6 +11,19 @@
 
 
 #import "IACircularSliderTrackLayer.h"
 #import "IACircularSliderTrackLayer.h"
 
 
+@interface IACircularSlider ()
+
+@property (nonatomic, assign) CGFloat radius;           //半径
+@property (nonatomic, assign) CGPoint drawCenter;       //绘制圆的圆心
+@property (nonatomic, assign) CGPoint circleStartPoint; //起始位置
+@property (nonatomic, assign) CGFloat angle;            //转过的角度
+@property (nonatomic, assign) CGFloat circleRadius;
+
+@property (nonatomic, assign) BOOL lockClockwise;       //禁止顺时针转动
+@property (nonatomic, assign) BOOL lockAntiClockwise;   //禁止逆时针转动
+
+@end
+
 
 
 @implementation IACircularSlider
 @implementation IACircularSlider
 {
 {
@@ -22,6 +35,9 @@
     BOOL _isInitiallySet;
     BOOL _isInitiallySet;
 }
 }
 
 
+- (void)dealloc {
+    [self removeObserver:self forKeyPath:@"angle"];
+}
 
 
 - (void)resetInitially {
 - (void)resetInitially {
     _isInitiallySet = NO;
     _isInitiallySet = NO;
@@ -93,6 +109,16 @@
     
     
     _isInitiallySet = NO;
     _isInitiallySet = NO;
     [self updateLayers];
     [self updateLayers];
+    
+    self.radius = 103;
+    self.drawCenter = CGPointMake(233/2.0f, 233 / 2.0);
+    self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.radius);
+    self.lockAntiClockwise = NO;
+    self.lockClockwise = NO;
+    [self addObserver:self
+           forKeyPath:@"angle"
+              options:NSKeyValueObservingOptionNew
+              context:nil];
 }
 }
 
 
 -(CGFloat)radianForValue:(CGFloat)value{
 -(CGFloat)radianForValue:(CGFloat)value{
@@ -198,7 +224,7 @@
     return val;
     return val;
 }
 }
 
 
--(CGFloat)transformedStartAngle{
+-(CGFloat)transformedStartAngle {
     CGFloat sA = self.startAngle;
     CGFloat sA = self.startAngle;
     CGFloat offset = (2*M_PI - [self distance])/2;
     CGFloat offset = (2*M_PI - [self distance])/2;
     if (sA>self.endAngle) {
     if (sA>self.endAngle) {
@@ -284,18 +310,14 @@
 #pragma mark - Setters
 #pragma mark - Setters
 
 
 -(void)setStartAngle:(CGFloat)startAngle{
 -(void)setStartAngle:(CGFloat)startAngle{
-//    if (startAngle>2*M_PI||startAngle<0) {
-//        [NSException raise:@"Invalid value of startAngle" format:@"startAngle must be more than 0 and less than 2PI"];
-//    }
+
     _startAngle = startAngle;
     _startAngle = startAngle;
     [self calculateForTouch:_lastTouch];
     [self calculateForTouch:_lastTouch];
     [self updateLayers];
     [self updateLayers];
 }
 }
 
 
 -(void)setEndAngle:(CGFloat)endAngle{
 -(void)setEndAngle:(CGFloat)endAngle{
-//    if (endAngle>2*M_PI||endAngle<0) {
-//        [NSException raise:@"Invalid value of endAngle" format:@"endAngle must be more than 0 and less than 2PI"];
-//    }
+
     _endAngle = endAngle;
     _endAngle = endAngle;
     [self calculateForTouch:_lastTouch];
     [self calculateForTouch:_lastTouch];
     [self updateLayers];
     [self updateLayers];
@@ -365,6 +387,9 @@
 -(void)setFrame:(CGRect)frame{
 -(void)setFrame:(CGRect)frame{
     [super setFrame:frame];
     [super setFrame:frame];
     _center = CGPointMake(CGRectGetWidth(frame)/2, CGRectGetHeight(frame)/2);
     _center = CGPointMake(CGRectGetWidth(frame)/2, CGRectGetHeight(frame)/2);
+    self.radius = CGRectGetWidth(frame)/2 - self.trackWidth;
+    self.drawCenter = CGPointMake(CGRectGetWidth(frame)/2, CGRectGetHeight(frame)/2);
+    self.circleStartPoint = CGPointMake(self.drawCenter.x, self.drawCenter.y - self.radius);
     _isInitiallySet = NO;
     _isInitiallySet = NO;
     [self updateLayers];
     [self updateLayers];
     
     
@@ -385,17 +410,46 @@
 }
 }
 
 
 -(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
 -(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
-    
-    [self calculateForTouch:touch];
-    _lastTouch = touch;
-    [self updateLayers];
-    
-    [self sendActionsForControlEvents:UIControlEventValueChanged];
-    
-    return YES;
+    if ([self calculateForTouch:touch]) {
+        _lastTouch = touch;
+        [self updateLayers];
+        [self sendActionsForControlEvents:UIControlEventValueChanged];
+        return YES;
+    }
+    return NO;
 }
 }
 
 
--(void)calculateForTouch:(UITouch*)touch{
+-(BOOL)calculateForTouch:(UITouch*)touch{
+    CGPoint starTouchPoint = [touch locationInView:self];
+
+    CGFloat centerX = self.drawCenter.x;
+    CGFloat centerY = self.drawCenter.y;
+    
+    CGFloat moveX = starTouchPoint.x;
+    CGFloat moveY = starTouchPoint.y;
+    //到300度,禁止移动到第一,二,三象限
+    if (self.lockClockwise) {
+        if ((moveX >= centerX && moveY <= centerY) ||
+            (moveX >= centerX && moveY >= centerY) ||
+            (moveX <= centerX && moveY >= centerY)) {
+            self.angle = 360;
+            self.radian = self.endAngle;
+            self.value = self.maximumValue;
+            return YES;
+        }
+    }
+    
+    //小于60度的时候,禁止移动到第二,三,四象限
+    if (self.lockAntiClockwise) {
+        if ((moveX <= centerX && moveY >= centerY) ||
+            (moveX <= centerX && moveY <= centerY) ||
+            (moveX >= centerX && moveY >= centerY)) {
+            self.angle = 0;
+            self.radian = self.startAngle;
+            self.value = self.minimumValue;
+            return YES;
+        }
+    }
     CGFloat preRad = [self radianForPoint:[touch locationInView:self]]; //radian from east
     CGFloat preRad = [self radianForPoint:[touch locationInView:self]]; //radian from east
     
     
     CGFloat transN = [self transformRadianForCurrentOptions:preRad forStartAngle:[self transformedStartAngle]];
     CGFloat transN = [self transformRadianForCurrentOptions:preRad forStartAngle:[self transformedStartAngle]];
@@ -404,9 +458,30 @@
     
     
     CGFloat reversedBoundValue = [self reverseTransformForModifiedStartAngleToDefault:boundRadianTN];
     CGFloat reversedBoundValue = [self reverseTransformForModifiedStartAngleToDefault:boundRadianTN];
     
     
-    self.radian = reversedBoundValue;
+    CGPoint lastPoint =  [self mapRadianToPoint:reversedBoundValue];
+    CGFloat angle = [IACircularSlider calculateAngleWithRadius:self.radius
+                                                     center:self.drawCenter
+                                                startCenter:self.circleStartPoint
+                                                  endCenter:lastPoint];
+    
+    if (angle >= 300) {
+        //当当前角度大于等于300度时禁止移动到第一、二、三象限
+        self.lockClockwise = YES;
+    } else {
+        self.lockClockwise = NO;
+    }
+    
+    if (angle <= 60.0) {
+        //当当前角度小于等于60度时,禁止移动到第二、三、四象限
+        self.lockAntiClockwise = YES;
+    } else {
+        self.lockAntiClockwise = NO;
+    }
+    self.angle = angle;
 
 
+    self.radian = reversedBoundValue;
     self.value = [self boundValueForValue:[self valueForRadian:transN]];
     self.value = [self boundValueForValue:[self valueForRadian:transN]];
+    return YES;
 }
 }
 
 
 -(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
 -(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
@@ -414,8 +489,63 @@
     [_thumbLayer setNeedsDisplay];
     [_thumbLayer setNeedsDisplay];
 }
 }
 
 
-
-
+/**
+ 计算圆上两点间的角度
+
+ @param radius 半径
+ @param center 圆心
+ @param startCenter 起始点坐标
+ @param endCenter 结束点坐标
+ @return 圆上两点间的角度
+ */
++ (CGFloat)calculateAngleWithRadius:(CGFloat)radius
+                             center:(CGPoint)center
+                        startCenter:(CGPoint)startCenter
+                          endCenter:(CGPoint)endCenter {
+    //a^2 = b^2 + c^2 - 2bccosA;
+    CGFloat cosA = (2 * radius * radius - powf([IACircularSlider distanceBetweenPointA:startCenter pointB:endCenter], 2)) / (2 * radius * radius);
+    CGFloat angle = 180 / M_PI * acosf(cosA);
+    if (startCenter.x > endCenter.x) {
+        angle = 360 - angle;
+    }
+    return angle;
+}
+
+/**
+ 两点间的距离
+
+ @param pointA 点A的坐标
+ @param pointB 点B的坐标
+ @return 两点间的距离
+ */
++ (double)distanceBetweenPointA:(CGPoint)pointA pointB:(CGPoint)pointB {
+    double x = fabs(pointA.x - pointB.x);
+    double y = fabs(pointA.y - pointB.y);
+    return hypot(x, y);//hypot(x, y)函数为计算三角形的斜边长度
+}
+
+#pragma mark - KVO
+
+//对angle添加KVO,有时候手势过快在continueTrackingWithTouch方法中不能及时限定转动,所以需要通过KVO对angle做实时监控
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+    IACircularSlider *circleSlider = (IACircularSlider *)object;
+    NSNumber *newAngle = [change valueForKey:@"new"];
+    if ([keyPath isEqualToString:@"angle"]) {
+        if (newAngle.doubleValue >= 300 ||
+            circleSlider.angle >= 300) {
+            self.lockClockwise = YES;
+        } else {
+            self.lockClockwise = NO;
+        }
+        
+        if (newAngle.doubleValue <= 60 ||
+            circleSlider.angle <= 60) {
+            self.lockAntiClockwise = YES;
+        } else {
+            self.lockAntiClockwise = NO;
+        }
+    }
+}
 /*
 /*
 // Only override drawRect: if you perform custom drawing.
 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
 // An empty implementation adversely affects performance during animation.

+ 12 - 8
KulexiuForTeacher/KulexiuForTeacher/Module/TXClassRoom/View/RoundSlider/IACircularSliderTrackLayer.m

@@ -18,29 +18,33 @@
 -(void)drawInContext:(CGContextRef)ctx{
 -(void)drawInContext:(CGContextRef)ctx{
     CGPoint center = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
     CGPoint center = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
     _center = center;
     _center = center;
-    CGFloat arcWidth = _trackWidth =  self.slider.trackWidth;
+    _trackWidth = self.slider.trackWidth;
+    CGFloat arcWidth = _trackWidth;
     CGFloat arcRadius = (CGRectGetWidth(self.frame)-arcWidth)/2;
     CGFloat arcRadius = (CGRectGetWidth(self.frame)-arcWidth)/2;
     
     
     //设置线的宽度
     //设置线的宽度
     CGContextSetLineWidth(ctx, arcWidth);
     CGContextSetLineWidth(ctx, arcWidth);
+    CGLineCap type = self.slider.isCapRound ? kCGLineCapRound : kCGLineCapSquare;
     //设置圆环线条的两个端点做圆滑处理
     //设置圆环线条的两个端点做圆滑处理
-    CGContextSetLineCap(ctx, kCGLineCapRound);
+    CGContextSetLineCap(ctx, type);
     //设置画笔颜色
     //设置画笔颜色
     CGContextSetFillColorWithColor(ctx, [UIColor blackColor].CGColor);
     CGContextSetFillColorWithColor(ctx, [UIColor blackColor].CGColor);
     //设置圆心
     //设置圆心
-    //       CGFloat originX = center.x;
-    //       CGFloat originY = center.y;
+    
     //计算半径
     //计算半径
     CGFloat radius = arcRadius;
     CGFloat radius = arcRadius;
     //建立一个最小初始弧度值,避免进度progress为0或1时圆环消失
     //建立一个最小初始弧度值,避免进度progress为0或1时圆环消失
     CGFloat progress = (self.slider.value-self.slider.minimumValue) / (self.slider.maximumValue - self.slider.minimumValue);
     CGFloat progress = (self.slider.value-self.slider.minimumValue) / (self.slider.maximumValue - self.slider.minimumValue);
-    CGFloat minAngle = M_PI/90 - progress * M_PI/80;
+//    CGFloat minAngle = M_PI/90 - progress * M_PI/80;
+    CGFloat minAngle = 0;
     //逆时针画一个圆弧
     //逆时针画一个圆弧
+    CGFloat width = CGRectGetWidth(self.frame) / 2;
+    CGFloat height = CGRectGetHeight(self.frame) / 2;
     if (self.slider.clockwise) {
     if (self.slider.clockwise) {
         
         
-        CGContextAddArc(ctx, self.frame.size.width / 2, self.frame.size.height / 2, radius, -M_PI_2, -M_PI_2 + minAngle + (2 * M_PI)*progress, NO);
+        CGContextAddArc(ctx, width, height, radius, -M_PI_2, -M_PI_2 + minAngle + (2 * M_PI)*progress, NO);
     }else{
     }else{
-        CGContextAddArc(ctx, self.frame.size.width / 2, self.frame.size.height / 2, radius, -M_PI_2, -M_PI_2 - minAngle + (2 * M_PI)*(1-progress), YES);
+        CGContextAddArc(ctx, width, height, radius, -M_PI_2, -M_PI_2 - minAngle + (2 * M_PI)*(1-progress), YES);
     }
     }
     
     
     //2. 创建一个渐变色
     //2. 创建一个渐变色
@@ -64,7 +68,7 @@
     
     
     //4.用渐变色填充,修改填充色的方向
     //4.用渐变色填充,修改填充色的方向
     CGContextDrawLinearGradient(ctx, gradient, self.slider.gradientStartPoint, self.slider.gradientEndPoint, 0);
     CGContextDrawLinearGradient(ctx, gradient, self.slider.gradientStartPoint, self.slider.gradientEndPoint, 0);
-    //释放渐变色
+//    释放渐变色
     CGGradientRelease(gradient);
     CGGradientRelease(gradient);
 }
 }
 
 

+ 0 - 51
KulexiuForTeacher/KulexiuForTeacher/Module/TXClassRoom/View/TXMetronomeView/KSMetronomeControlView.m

@@ -323,57 +323,6 @@
     }
     }
 }
 }
 
 
-- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
-    
-    UITouch *touch = [touches anyObject];
-    if ([touch.view isKindOfClass:[KSRateSliderView class]]) {
-        CGPoint point = [touch locationInView:self.controlBgView];
-        //计算上一个点相对于x轴的角度
-        CGFloat lastPointRadius = sqrt(pow(point.y - _centerPoint.y, 2) + pow(point.x - _centerPoint.x, 2));
-        if (lastPointRadius == 0) {
-            return;
-        }
-        _lastPointAngle = acos((point.x - _centerPoint.x) / lastPointRadius);
-        if (point.y > _centerPoint.y) {
-            _lastPointAngle = 2 * M_PI - _lastPointAngle;
-        }
-    }
-    
-}
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
-
-    UITouch *touch = [touches anyObject];
-    if ([touch.view isKindOfClass:[KSRateSliderView class]]) {
-        CGPoint currentPoint = [touch locationInView:self.controlBgView];
-        
-        //1.计算当前点相对于x轴的角度
-        CGFloat currentPointRadius = sqrt(pow(currentPoint.y - _centerPoint.y, 2) + pow(currentPoint.x - _centerPoint.x, 2));
-        if (currentPointRadius == 0) {//当点在中心点时,被除数不能为0
-            return;
-        }
-        CGFloat curentPointAngle = acos((currentPoint.x - _centerPoint.x) / currentPointRadius);
-        if (currentPoint.y > _centerPoint.y) {
-            curentPointAngle = 2 * M_PI - curentPointAngle;
-        }
-        //2.变化的角度
-        CGFloat angle = _lastPointAngle - curentPointAngle;
-        // 小点点的位置
-        self.controlSpeedView.transform = CGAffineTransformRotate(self.controlSpeedView.transform, angle);
-        _lastPointAngle = curentPointAngle;
-        if (angle > 0 && angle < 1) {
-            if (self.delegate && [self.delegate respondsToSelector:@selector(changeSpeedWithIsAdd:speed:)]) {
-
-                [self.delegate changeSpeedWithIsAdd:YES speed:MIN(1, MAX(angle *2, 1))];
-            }
-        }
-        else if (angle < 0 && angle > -1) {
-            if (self.delegate && [self.delegate respondsToSelector:@selector(changeSpeedWithIsAdd:speed:)]) {
-                [self.delegate changeSpeedWithIsAdd:NO speed:MIN(1, MAX(angle *2, 1))];
-            }
-        }
-    }
-}
 
 
 #pragma mark --- setting
 #pragma mark --- setting
 - (void)setRate:(int)rate {
 - (void)setRate:(int)rate {

+ 1 - 1
KulexiuForTeacher/KulexiuForTeacher/Module/TXClassRoom/View/TXMetronomeView/NewTXMetronomeAlertView.m

@@ -76,7 +76,7 @@
     slider.minimumValue = 50;
     slider.minimumValue = 50;
     slider.maximumValue = 200;
     slider.maximumValue = 200;
     slider.startAngle = -M_PI/2;
     slider.startAngle = -M_PI/2;
-    slider.endAngle = 3*M_PI/2-0.01;
+    slider.endAngle = 3*M_PI/2;
     slider.clockwise = YES;
     slider.clockwise = YES;
     
     
     [slider setThumbImage:[UIImage imageNamed:@"tx_round_sliderImg"]];
     [slider setThumbImage:[UIImage imageNamed:@"tx_round_sliderImg"]];