Object-C重写自Swift代码:实现音乐跳动动画。

最近在做和音乐相关的功能模块,其中需要开发一个控件,音乐跳动动画View。用Object-C写了一个控件,仅供参考。代码放到Github上:

效果:

 

头文件:HGMusicIndicatorContentView.h  动画layer容器:

 

  1. #import <UIKit/UIKit.h>
  2. @interface HGMusicIndicatorContentView : UIView
  3. - (CGSize)intrinsicContentSize;
  4. - (BOOL) isOscillating;
  5. - (void) startDecay;
  6. - (void) stopOscillation;
  7. - (void) stopDecay;
  8. - (void) startOscillation;
  9. @end

HGMusicIndicatorContentView.m文件,主要实现动画的添加和移除,以及栅栏的布局和运动定义:

 

  1. #import "HGMusicIndicatorContentView.h"
  2. @interface HGMusicIndicatorContentView(){
  3.     NSInteger kBarCount;
  4.     CGFloat kBarWidth;
  5.     CGFloat kBarIdleHeight;
  6.     CGFloat kHorizontalBarSpacing ; // Measured on iPad 2 (non-Retina)
  7.     CGFloat kRetinaHorizontalBarSpacing; // Measured on iPhone 5s (Retina)
  8.     CGFloat kBarMinPeakHeight;
  9.     CGFloat kBarMaxPeakHeight;
  10.     CFTimeInterval kMinBaseOscillationPeriod ;
  11.     CFTimeInterval kMaxBaseOscillationPeriod ;
  12.     NSString *kOscillationAnimationKey;
  13.     CFTimeInterval kDecayDuration ;
  14.     NSString *kDecayAnimationKey ;
  15. }
  16. @property (strong,nonatomic) NSMutableArray *barLayers;
  17. @property (assign,nonatomic) BOOL hasInstalledConstraints;
  18. @end
  19. @implementation HGMusicIndicatorContentView
  20. - (NSMutableArray *)barLayers {
  21.     if (!_barLayers) {
  22.         _barLayers = [[NSMutableArray alloc] init];
  23.     }
  24.     return _barLayers;
  25. }
  26. - (instancetype)initWithFrame:(CGRect)frame {
  27.    self = [super initWithFrame:frame];
  28.     if (self) {
  29.         kBarCount = 5;
  30.         kBarWidth = 2.0;
  31.         kBarIdleHeight = 3.0;
  32.         kHorizontalBarSpacing = 2.0// Measured on iPad 2 (non-Retina)
  33.         kRetinaHorizontalBarSpacing = 1.5// Measured on iPhone 5s (Retina)
  34.         kBarMinPeakHeight = 6.0;
  35.         kBarMaxPeakHeight = 12.0;
  36.         kMinBaseOscillationPeriod = 0.6;
  37.         kMaxBaseOscillationPeriod = 0.8;
  38.         kOscillationAnimationKey = @"oscillation";
  39.         kDecayDuration = 0.3;
  40.         kDecayAnimationKey = @"decay";
  41.         self.hasInstalledConstraints = NO;
  42.         self.translatesAutoresizingMaskIntoConstraints = NO;
  43.         [self prepareBarLayers];
  44.         [self tintColorDidChange];
  45.         [self setNeedsUpdateConstraints];
  46.     }
  47.     return self;
  48. }
  49. - (instancetype)init {
  50.     return [self initWithFrame:CGRectZero];
  51. }
  52. - (void) prepareBarLayers {
  53.     CGFloat xOffset = 0.0;
  54.     for (int i = 0;i<kBarCount;i++) {
  55.         CALayer *newlayer = [self createBarLayerWithXOffset:xOffset layerIndex:i];
  56.         newlayer.cornerRadius = 1;
  57.         [self.barLayers addObject:newlayer];
  58.         [self.layer addSublayer:newlayer];
  59.         xOffset = CGRectGetMaxX(newlayer.frame) + [self horizontalBarSpacing];
  60.     }
  61. }
  62. - (CGFloat)horizontalBarSpacing {
  63.     if ([[UIScreen mainScreen] scale] == 2.0) {
  64.         return kRetinaHorizontalBarSpacing;
  65.     } else {
  66.         return kHorizontalBarSpacing;
  67.     }
  68. }
  69. - (CALayer *) createBarLayerWithXOffset:(CGFloat)XOffset layerIndex:(NSInteger)index {
  70.     CALayer *layer = [[CALayer alloc] init];
  71.     layer.anchorPoint = CGPointMake(0,  1.0); // At the bottom-left corner
  72.     layer.position = CGPointMake(XOffset, kBarMaxPeakHeight);
  73.     layer.backgroundColor = [UIColor colorWithRed:102/255.0 green:146/255.0 blue:247/255.0 alpha:1].CGColor;
  74.     layer.bounds = CGRectMake(0.00.0, kBarWidth, ((CGFloat)index * kBarMaxPeakHeight / (CGFloat)kBarCount));
  75.     return layer;
  76. }
  77. - (void) tintColorDidChange {
  78.     for (CALayer *layer in self.barLayers){
  79.         layer.backgroundColor = [self.tintColor CGColor];
  80.     }
  81. }
  82. - (CGSize)intrinsicContentSize {
  83.     CGRect uFrame = CGRectZero;
  84.     for (CALayer * layer in self.barLayers) {
  85. //        unionFrame = unionFrame.union(layer.frame)
  86.         uFrame = CGRectUnion(uFrame, layer.frame);
  87.     }
  88.     return uFrame.size;
  89. }
  90. - (void) updateConstraints {
  91.     if (!self.hasInstalledConstraints) {
  92.         CGSize size = [self intrinsicContentSize];
  93.         [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:size.width]];
  94.         [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:size.height]];
  95.         self.hasInstalledConstraints = YES;
  96.     }
  97.     [super updateConstraints];
  98. }
  99. - (void) startOscillation {
  100.     CFTimeInterval basePeriod = kMinBaseOscillationPeriod + (drand48() * (kMaxBaseOscillationPeriod - kMinBaseOscillationPeriod));
  101.     for (CALayer * layer in self.barLayers) {
  102.         [self startOscillatingBarLayer:layer basePeriod:basePeriod];
  103.     }
  104. }
  105. - (void) stopOscillation {
  106.     for (CALayer *layer in self.barLayers) {
  107.         [layer removeAnimationForKey:kOscillationAnimationKey];
  108.     }
  109. }
  110. - (BOOL) isOscillating {
  111.     CALayer *layer = [self.barLayers firstObject];
  112.     CAAnimation *anim = [layer animationForKey:kOscillationAnimationKey];
  113.     return anim == nil ? NO:YES ;
  114. }
  115. - (void) startDecay {
  116.     for(CALayer *layer in self.barLayers) {
  117.         [self startDecayingBarLayer:layer];
  118.     }
  119. }
  120. - (void) stopDecay {
  121.     for (CALayer * layer in self.barLayers ){
  122.         [layer removeAnimationForKey:kDecayAnimationKey];
  123.     }
  124. }
  125. - (void) startOscillatingBarLayer:(CALayer *)layer basePeriod:(CFTimeInterval)basePeriod {
  126.     // arc4random_uniform() will return a uniformly distributed random number **less** upper_bound.
  127.     CGFloat peakHeight = kBarMinPeakHeight + (CGFloat)(arc4random_uniform((UInt32)(kBarMaxPeakHeight - kBarMinPeakHeight + 1)));
  128.     CGRect fromBouns = layer.bounds;
  129.     fromBouns.size.height = kBarIdleHeight;
  130.     CGRect toBounds = layer.bounds;
  131.     toBounds.size.height = peakHeight;
  132.     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
  133.     NSValue *fromvalue = [NSValue valueWithCGRect:fromBouns];
  134.     animation.fromValue = fromvalue;
  135.     NSValue *tovalue = [NSValue valueWithCGRect:toBounds];
  136.     animation.toValue = tovalue;
  137.     animation.repeatCount = HUGE_VALF; // Forever
  138.     animation.autoreverses = YES;
  139.     animation.duration = (CFTimeInterval)((CGFloat)basePeriod / 2) * (kBarMaxPeakHeight / peakHeight);
  140.     animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn] ;
  141.     [layer addAnimation:animation forKey:kOscillationAnimationKey];
  142. }
  143. -(void) startDecayingBarLayer: (CALayer *)layer {
  144.     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
  145.     CALayer *prelayer = [layer presentationLayer];
  146.     if (prelayer != nil) {
  147. //        CALayer *tempLayer = [[CALayer alloc] initWithLayer:prelayer];
  148.         animation.fromValue = [NSValue valueWithCGRect:prelayer.bounds];
  149.     }
  150.     animation.toValue = [NSValue valueWithCGRect:layer.bounds];
  151.     animation.duration = kDecayDuration;
  152.     animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut] ;
  153.     [layer addAnimation:animation forKey:kDecayAnimationKey];
  154. }

HGMusicIndicatorView:

  1. #import <UIKit/UIKit.h>
  2. typedef enum
  3. {
  4.     stopped = 0,  //默认
  5.     playing,
  6.     paused
  7. } HGMusicIndicatorState;
  8. @interface HGMusicIndicatorView : UIView
  9. @property(assign, nonatomic) BOOL hidesWhenStopped;
  10. @property(assign, nonatomic) HGMusicIndicatorState state;
  11. @end
  1. #import "HGMusicIndicatorView.h"
  2. #import "HGMusicIndicatorContentView.h"
  3. @interface HGMusicIndicatorView ()
  4. @property (nonatomic, assign) BOOL hasInstalledConstraints;
  5. @property (nonatomic, strong) HGMusicIndicatorContentView *contentView;
  6. @property (nonatomic, assign) CGSize intrinsicContentSize;
  7. @end
  8. @implementation HGMusicIndicatorView
  9. - (void) awakeFromNib {
  10.     [super awakeFromNib];
  11.     self.hasInstalledConstraints = NO;
  12.     [self commonInit];
  13. }
  14. - (instancetype)initWithFrame:(CGRect)frame {
  15.     self = [super initWithFrame:frame];
  16.     if (self) {
  17.         self.hasInstalledConstraints = NO;
  18.         [self commonInit];
  19.     }
  20.     return self;
  21. }
  22. - (void) commonInit {
  23.     self.layer.masksToBounds = YES;
  24.     self.contentView = [[HGMusicIndicatorContentView alloc] init];
  25.     [self addSubview:_contentView];
  26.     [self prepareLayoutPriorities];
  27.     [self setNeedsUpdateConstraints];
  28.     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ifEnterBackgroud) name:UIApplicationWillResignActiveNotification object:nil];
  29.     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ifEnterActice) name:UIApplicationDidBecomeActiveNotification object:nil];
  30. }
  31. - (void) prepareLayoutPriorities {
  32.     [self setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
  33.     [self setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
  34.     [self setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
  35.     [self setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical];
  36. }
  37. - (void)setHidesWhenStopped:(BOOL) hidesWhenStopped {
  38.     if (_state == stopped && _hidesWhenStopped) {
  39.         self.hidden = hidesWhenStopped;
  40.     }
  41. }
  42. - (void)setState:(HGMusicIndicatorState)state {
  43.     if (state == stopped || state == paused) {
  44.         [self stopAnimating];
  45.         if (self.hidesWhenStopped) {
  46.             self.hidden = YES;
  47.         }
  48.     }
  49.     else {
  50.         self.hidden = NO;
  51.         if (state == playing) {
  52.             [self startAnimating];
  53.         }
  54.         else {
  55.             [self stopAnimating];
  56.         }
  57.     }
  58. }
  59. - (void) stopAnimating {
  60.     if (![self.contentView isOscillating])
  61.     {
  62.         return ;
  63.     }
  64.     [self.contentView stopOscillation];
  65.     [self.contentView startDecay];
  66. }
  67. - (CGSize) intrinsicContentSize {
  68.     return [self.contentView intrinsicContentSize];
  69. }
  70. - (void) updateConstraints {
  71.     if (!self.hasInstalledConstraints) {
  72.         [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
  73.         [self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
  74.         self.hasInstalledConstraints = YES;
  75.     }
  76.     [super updateConstraints];
  77. }
  78. - (UIView *)forBaselineLayout {
  79.     return self.contentView;
  80. }
  81. - (CGSize ) sizeThatFits:(CGSize)size {
  82.     return self.intrinsicContentSize;
  83. }
  84. - (void) startAnimating {
  85.     if ([self.contentView isOscillating]) {
  86.         return;
  87.     }
  88.     [self.contentView stopDecay];
  89.     [self.contentView startOscillation];
  90. }
  91. //如果进入后台
  92. -(void)ifEnterBackgroud
  93. {
  94.     [self stopAnimating];
  95. }
  96. - (void)ifEnterActice {
  97.     if (self.state == playing) {
  98.         [self startAnimating];
  99.     }
  100. }

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:1   其中:访客  1   博主  0

  1. avatar 来语直搜 0

    【给博主的一封简信】
    每个独立博客的背后都有一位用心经营的博主,优秀的博客应当被铭记!来语直搜针对独立博客发起“孤岛直达”计划,为精品博客特别提供免费的搜索直达服务(搜索 博客名 直达博客主页),喜欢你博客的人不必在茫茫导航中寻找链接。由于独立博客常出现变动,请博主提交站点前先确认满足以下条件,提交后48小时内进行内部审核,届时会考虑博客综合情况,故提交不等同于审核通过。
    站点提交条件:
    1.博客有明确、特异、长期稳定的博客站名,不随意变更站名,如有变更时主动重新提交站点;
    2.博客坚持以原创为主,非原创内容不超过全站30%;
    3.博客已在友链明显位置添加来语直搜主页链接;
    4.博主Email请留常用邮箱,以便需要时沟通联系。
    站点提交地址:
    http://www.zhisou.cc/linksubmit.php