震动效果
2016-02-25 21:04
232 查看
震动效果
![](http://images2015.cnblogs.com/blog/607542/201602/607542-20160225210157161-660706911.gif)
效果
![](http://images2015.cnblogs.com/blog/607542/201602/607542-20160225210212411-1015496612.gif)
源码
https://github.com/rFlex/SCViewShaker
https://github.com/YouXianMing/Animations
![](http://images2015.cnblogs.com/blog/607542/201602/607542-20160225210157161-660706911.gif)
效果
![](http://images2015.cnblogs.com/blog/607542/201602/607542-20160225210212411-1015496612.gif)
源码
https://github.com/rFlex/SCViewShaker
https://github.com/YouXianMing/Animations
// // UIView+Shake.h // Animations // // Created by YouXianMing on 16/2/25. // Copyright © 2016年 YouXianMing. All rights reserved. // #import <UIKit/UIKit.h> // https://github.com/rFlex/SCViewShaker #define kDefaultShakeOptions (SCShakeOptionsDirectionHorizontal | SCShakeOptionsForceInterpolationExpDown | SCShakeOptionsAtEndComplete) #define kDefaultShakeForce (0.075) #define kDefaultShakeDuration (0.5) #define kDefaultShakeIterationDuration (0.03) typedef enum : NSUInteger { SCShakeOptionsDirectionRotate = 0, SCShakeOptionsDirectionHorizontal = 1, SCShakeOptionsDirectionVertical = 2, SCShakeOptionsDirectionHorizontalAndVertical = 3, SCShakeOptionsForceInterpolationNone = 4, SCShakeOptionsForceInterpolationLinearUp = 8, SCShakeOptionsForceInterpolationLinearDown = 16, SCShakeOptionsForceInterpolationExpUp = 32, SCShakeOptionsForceInterpolationExpDown = 64, SCShakeOptionsForceInterpolationRandom = 128, SCShakeOptionsAtEndRestart = 256, SCShakeOptionsAtEndComplete = 512, SCShakeOptionsAtEndContinue = 1024, SCShakeOptionsAutoreverse = 2048 } SCShakeOptions; typedef void(^ShakeCompletionHandler)(); @interface UIView (Shake) /** * Returns whether the view is currently shaking or not. */ @property (readonly, nonatomic) BOOL isShaking; /** * Shake the view using the default options. The view will be shaken for a short amount of time. */ - (void)shake; /* Shake the view using the specified options. |shakeOptions| is an enum of options that can be activated by using the OR operator (like SCShakeOptionsDirectionRotate | SCShakeOptionsForceInterpolationNone). |force| is the coefficient of force to apply on each shake iteration (typically between 0 and 1 even though nothing prevents you for setting a higher value if you want). |duration| is the total duration of the shaking motion. This may be ignored depending of the options you set. iterationDuration is how long each shake iteration will last. You may want to set a very low value (like 0.02) if you want a proper shake effect. |completionHandler|, if not null, is the block that will be invoked when the shake finishes. */ - (void)shakeWithOptions:(SCShakeOptions)shakeOptions force:(CGFloat)force duration:(CGFloat)duration iterationDuration:(CGFloat)iterationDuration completionHandler:(ShakeCompletionHandler)completionHandler; /** * End the current shaking action, if any */ - (void)endShake; @end
// // UIView+Shake.m // Animations // // Created by YouXianMing on 16/2/25. // Copyright © 2016年 YouXianMing. All rights reserved. // #import "UIView+Shake.h" #import <objc/runtime.h> #define HAS_OPT(options, option) ((options & option) == option) @interface SCShakeInfo : NSObject @property (assign, nonatomic) CGAffineTransform baseTransform; @property (assign, nonatomic) BOOL shaking; @property (assign, nonatomic) SCShakeOptions options; @property (assign, nonatomic) CGFloat force; @property (assign, nonatomic) CGFloat duration; @property (assign, nonatomic) CGFloat iterationDuration; @property (assign, nonatomic) CFTimeInterval startTime; @property (strong, nonatomic) ShakeCompletionHandler completionHandler; @property (assign, nonatomic) BOOL reversed; @property (readonly, nonatomic) CGFloat completionRatio; @end @implementation SCShakeInfo - (CGFloat)completionRatio { return (CACurrentMediaTime() - self.startTime) / self.duration; } @end @implementation UIView (Shake) static const char *ShakeInfoKey = "ShakeInfo"; - (void)shake { [self shakeWithOptions:kDefaultShakeOptions force:kDefaultShakeForce duration:kDefaultShakeDuration iterationDuration:kDefaultShakeIterationDuration completionHandler:nil]; } - (void)shakeWithOptions:(SCShakeOptions)shakeOptions force:(CGFloat)force duration:(CGFloat)duration iterationDuration:(CGFloat)iterationDuration completionHandler:(ShakeCompletionHandler)completionHandler { SCShakeInfo *shakeInfo = [self shakeInfo]; shakeInfo.options = shakeOptions; shakeInfo.force = force; shakeInfo.startTime = CACurrentMediaTime(); shakeInfo.duration = duration; shakeInfo.iterationDuration = iterationDuration; shakeInfo.completionHandler = completionHandler; if (!shakeInfo.shaking) { shakeInfo.baseTransform = self.transform; shakeInfo.shaking = YES; [self _doAnimation:1]; } } - (CGFloat)_getInterpolationRatio:(CGFloat)completionRatio options:(SCShakeOptions)options { CGFloat (*interpFunc)(CGFloat) = nil; if (HAS_OPT(options, SCShakeOptionsForceInterpolationRandom)) { interpFunc =& InterpolateRandom; } else if (HAS_OPT(options, SCShakeOptionsForceInterpolationExpDown)) { interpFunc =& InterpolateExpDown; } else if (HAS_OPT(options, SCShakeOptionsForceInterpolationExpUp)) { interpFunc =& InterpolateExpUp; } else if (HAS_OPT(options, SCShakeOptionsForceInterpolationLinearDown)) { interpFunc =& InterpolateLinearDown; } else if (HAS_OPT(options, SCShakeOptionsForceInterpolationLinearUp)) { interpFunc =& InterpolateLinearUp; } else { interpFunc =& InterpolateNone; } return interpFunc(completionRatio); } - (void)_animate:(CGFloat)force shakeInfo:(SCShakeInfo *)shakeInfo { CGAffineTransform baseTransform = shakeInfo.baseTransform; SCShakeOptions options = shakeInfo.options; if (HAS_OPT(options, SCShakeOptionsDirectionHorizontalAndVertical)) { if (arc4random_uniform(2) == 1) { self.transform = CGAffineTransformTranslate(baseTransform, 0, force * self.bounds.size.height); } else { self.transform = CGAffineTransformTranslate(baseTransform, force * self.bounds.size.width, 0); } } else if (HAS_OPT(options, SCShakeOptionsDirectionVertical)) { self.transform = CGAffineTransformTranslate(baseTransform, 0, force * self.bounds.size.height); } else if (HAS_OPT(options, SCShakeOptionsDirectionHorizontal)) { self.transform = CGAffineTransformTranslate(baseTransform, force * self.bounds.size.width, 0); } else { self.transform = CGAffineTransformRotate(baseTransform, force * M_PI_2); } } - (void)_doAnimation:(CGFloat)direction { SCShakeInfo *shakeInfo = [self shakeInfo]; SCShakeOptions options = shakeInfo.options; CGFloat completionRatio = shakeInfo.completionRatio; if (completionRatio > 1) { completionRatio = 1; } if (shakeInfo.reversed) { completionRatio = 1 - completionRatio; } CGFloat interpolationRatio = [self _getInterpolationRatio:completionRatio options:options]; CGFloat force = shakeInfo.force * interpolationRatio * direction; CGFloat iterationDuration = shakeInfo.iterationDuration; [UIView animateWithDuration:iterationDuration animations:^{ [self _animate:force shakeInfo:shakeInfo]; } completion:^(BOOL finished) { if (shakeInfo.shaking) { BOOL shouldRecurse = YES; if (shakeInfo.completionRatio > 1) { if (HAS_OPT(shakeInfo.options, SCShakeOptionsAutoreverse)) { shakeInfo.reversed = !shakeInfo.reversed; } if (shakeInfo.reversed || HAS_OPT(shakeInfo.options, SCShakeOptionsAtEndRestart)) { shakeInfo.startTime = CACurrentMediaTime(); } else if (!HAS_OPT(shakeInfo.options, SCShakeOptionsAtEndContinue)) { shouldRecurse = NO; [self endShake]; } } if (shouldRecurse) { [self _doAnimation:direction * -1]; } } }]; } - (void)endShake { SCShakeInfo *shakeInfo = [self shakeInfo]; if (shakeInfo.shaking) { shakeInfo.shaking = NO; self.transform = shakeInfo.baseTransform; ShakeCompletionHandler completionHandler = shakeInfo.completionHandler; shakeInfo.completionHandler = nil; if (completionHandler != nil) { completionHandler(); } } } - (BOOL)isShaking { return [self shakeInfo].shaking; } - (SCShakeInfo *)shakeInfo { SCShakeInfo *shakeInfo = objc_getAssociatedObject(self, ShakeInfoKey); if (shakeInfo == nil) { shakeInfo = [SCShakeInfo new]; objc_setAssociatedObject(self, ShakeInfoKey, shakeInfo, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return shakeInfo; } #pragma Interpolations functions static CGFloat InterpolateLinearUp(CGFloat input) { return input; } static CGFloat InterpolateLinearDown(CGFloat input) { return 1 - input; } static CGFloat Exp(CGFloat a, int power) { if (a < 0.5f) { return (float)pow(a * 2, power) / 2; } else { return (float)pow((a - 1) * 2, power) / (power % 2 == 0 ? -2 : 2) + 1; } } static CGFloat InterpolateExpUp(CGFloat input) { return Exp(input, 4); } static CGFloat InterpolateExpDown(CGFloat input) { return Exp(1 - input, 4); } static CGFloat InterpolateNone(CGFloat input) { return 1; } static CGFloat InterpolateRandom(CGFloat input) { CGFloat randNb = arc4random_uniform(10000); return randNb / 10000.0; } @end
相关文章推荐
- POJ2186 Popular Cows 【强连通分量】+【Kosaraju】+【Tarjan】+【Garbow】
- 160225、解决纯js文件国际化的问题
- 上线流程
- 设计模式
- vector::clear ()方法的使用细节
- C++面向对象编程<十三>:template和reference
- 文件的读取(代码示例)
- 存储器的保护(二)——《x86汇编语言:从实模式到保护模式》读书笔记19
- Poj-2031 最小生成树+几何
- 【xv6学习之Lec8】System calls, Interrupts, and Exceptions
- Python与机器学习(三):K-近邻算法
- 1.1 Built-in Distributions In Matlab
- 前端工程之模块化
- 160224、常用正则表达式
- ps中经常遇到的问题
- 存储器的保护(二)——《x86汇编语言:从实模式到保护模式》读书笔记19
- IOS UI-控制器的生命周期
- Learn Some Framework-3 From Zygote to HOME
- Generic
- Java中播放声音实例