震动效果
震動效果
?
效果
?
源碼
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)shakeOptionsforce:(CGFloat)force duration:(CGFloat)durationiterationDuration:(CGFloat)iterationDurationcompletionHandler:(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:kDefaultShakeOptionsforce:kDefaultShakeForceduration:kDefaultShakeDurationiterationDuration:kDefaultShakeIterationDurationcompletionHandler:nil]; }- (void)shakeWithOptions:(SCShakeOptions)shakeOptionsforce:(CGFloat)forceduration:(CGFloat)durationiterationDuration:(CGFloat)iterationDurationcompletionHandler:(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 functionsstatic 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?
轉載于:https://www.cnblogs.com/YouXianMing/p/5218416.html
總結
- 上一篇: iOS9 白名单问题 -canOpen
- 下一篇: 五子棋评分表算法