一个好玩的demo


做这个demo的起因是看到了一个神经病一样的音量调节UI

这个微博里的第二张gif

直接附代码吧

感觉也没有什么值得写的地方

不过这个代码里还没实现音量按钮变色的功能

还有待研究

//
//  SPTVolumeBar.m
//  tmp
//
//  Created by Spt on 2017/6/15.
//  Copyright © 2017年 Spt. All rights reserved.
//

#import "SPTVolumeBar.h"
#import "Masonry.h"



@interface SPTVolumeBar () <CAAnimationDelegate>

@property (nonatomic, strong) UIImageView *loudspeakerImageView;

@property (nonatomic, strong) UIView      *purplePoint;

@property (nonatomic, strong) UIView      *line;

@end



@implementation SPTVolumeBar

- (instancetype)init {
    self = [super init];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];

        self.loudspeakerImageView = ({
            UIImageView *loudspeakerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Loudspeaker"]];
            
            UILongPressGestureRecognizer *longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressLoudspeakerImageView:)];
            longPressGestureRecognizer.minimumPressDuration = 0.1f;
            [loudspeakerImageView addGestureRecognizer:longPressGestureRecognizer];
            loudspeakerImageView.userInteractionEnabled = YES;
            
            loudspeakerImageView;
        });
        [self addSubview:self.loudspeakerImageView];
        
        self.line = ({
            UIView *line = [[UIView alloc] init];
            line.backgroundColor = [UIColor grayColor];
            line.layer.cornerRadius = 3.0f;
            line;
        });
        [self addSubview:self.line];
        
        self.purplePoint = ({
            UIView *purplePoint = [[UIView alloc] init];
            purplePoint.backgroundColor = [UIColor purpleColor];
            purplePoint.layer.cornerRadius = 5.0f;
            purplePoint;
        });
        [self addSubview:self.purplePoint];
        
        [self.loudspeakerImageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self);
            make.centerY.equalTo(self);
            make.size.mas_equalTo(self.loudspeakerImageView.image.size);
        }];
        [self.line mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerY.equalTo(self);
            make.left.equalTo(self.loudspeakerImageView.mas_right).offset(22.0f);
            make.right.equalTo(self).offset(-12.0f);
            make.height.mas_equalTo(6.0f);
        }];
        [self.purplePoint mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerY.equalTo(self.line);
            make.centerX.equalTo(self.line.mas_left);
            make.size.mas_equalTo(CGSizeMake(10.0f, 10.0f));
        }];
    }
    return self;
}

- (void)setPurplePointPositionWithAngle:(double)angle {
    [self.purplePoint.layer removeAnimationForKey:@"parabola"];

    CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    // 首先计算一下起点
    CGFloat startX = (self.loudspeakerImageView.bounds.size.width + 10.0f) * cos(angle);
    CGFloat startY = CGRectGetMidX(self.loudspeakerImageView.frame) - (self.loudspeakerImageView.bounds.size.width + 10.0f) * sin(angle);
    
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, startX, startY);
    CGPathAddQuadCurveToPoint(path, NULL, startX + 40, startY - 40, CGRectGetMinX(self.line.frame) + (self.line.bounds.size.width * angle / M_PI_4) - self.purplePoint.bounds.size.width / 2.0f, CGRectGetMidY(self.line.frame));
    keyframeAnimation.path = path;
    CGPathRelease(path);

    keyframeAnimation.duration = 0.3f;
    keyframeAnimation.repeatCount = 0;
    keyframeAnimation.removedOnCompletion = NO;
    keyframeAnimation.fillMode = kCAFillModeBoth;
    keyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    
    self.purplePoint.hidden = NO;
    [self.purplePoint.layer addAnimation:keyframeAnimation forKey:@"parabola"];
}

#pragma mark -- Actions --

- (void)longPressLoudspeakerImageView:(UILongPressGestureRecognizer *)longPressGestureRecognizer {
    if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan) {
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];

        animation.fromValue = @(0.0f);
        animation.toValue = @(-M_PI_4);
        animation.duration = 2.0f;
        animation.delegate = self;
        
        CGRect oldFrame = self.loudspeakerImageView.frame;
        self.loudspeakerImageView.layer.anchorPoint = CGPointMake(0.0f, 0.3f);
        self.loudspeakerImageView.frame = oldFrame;
        
        [self.loudspeakerImageView.layer addAnimation:animation forKey:@"rotate"];
        
        self.purplePoint.hidden = YES;
    }
    else if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded) {
        if ([self.loudspeakerImageView.layer animationForKey:@"rotate"]) {
            CALayer *currentLayer = (CALayer *)[self.loudspeakerImageView.layer presentationLayer];
            double currentAngle = -[(NSNumber *)[currentLayer valueForKeyPath:@"transform.rotation.z"] doubleValue];
            
            [self setPurplePointPositionWithAngle:currentAngle];
            [self.loudspeakerImageView.layer removeAnimationForKey:@"rotate"];
        }
    }
}

#pragma mark -- CAAnimationDelegate --

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if (flag) {
        [self setPurplePointPositionWithAngle:M_PI_4];
        [self.loudspeakerImageView.layer removeAnimationForKey:@"rotate"];
    }
}

@end