├── .gitignore ├── LICENSE ├── YSCAnimation ├── LoadGif │ ├── YSCGifLoadImage.h │ ├── YSCGifLoadImage.m │ ├── YSCGifLoadImageView.h │ └── YSCGifLoadImageView.m ├── carouselTitle │ ├── YSCCarouselTitleView.h │ └── YSCCarouselTitleView.m ├── circleLoad │ ├── YSCCircleLoadAnimationView.h │ └── YSCCircleLoadAnimationView.m ├── commonWave │ ├── YSCWaveView.h │ └── YSCWaveView.m ├── fanshaped │ ├── YSCFanShapedView.h │ ├── YSCFanShapedView.m │ └── fanshaped.jpg ├── heartBeatPulse │ ├── YSCHeartBeatPulseView.h │ └── YSCHeartBeatPulseView.m ├── microPhoneWave │ ├── YSCMicrophoneWaveView.h │ └── YSCMicrophoneWaveView.m ├── replicator │ ├── YSCCircleRippleView.h │ ├── YSCCircleRippleView.m │ ├── YSCMatrixCircleAnimationView.h │ └── YSCMatrixCircleAnimationView.m ├── ripple │ ├── YSCRippleView.h │ └── YSCRippleView.m ├── seaWaterWave │ ├── Shaders │ │ ├── SeaFragment.glsl │ │ └── SeaVertex.glsl │ ├── YSCSeaGLView.h │ └── YSCSeaGLView.m ├── tableRefresh │ ├── CurveView │ │ ├── YSCTableRefreshCurveLayer.h │ │ ├── YSCTableRefreshCurveLayer.m │ │ ├── YSCTableRefreshCurveView.h │ │ └── YSCTableRefreshCurveView.m │ ├── LabelView │ │ ├── YSCTableRefreshLabelView.h │ │ └── YSCTableRefreshLabelView.m │ ├── UITableViewController+YSCRefresh.h │ ├── UITableViewController+YSCRefresh.m │ ├── YSCTableRefreshFooterView.h │ ├── YSCTableRefreshFooterView.m │ ├── YSCTableRefreshHeaderView.h │ └── YSCTableRefreshHeaderView.m ├── voiceWave │ ├── YSCNewVoiceWaveView.h │ ├── YSCNewVoiceWaveView.m │ ├── YSCVoiceLoadingCircleView.h │ ├── YSCVoiceLoadingCircleView.m │ ├── YSCVoiceWaveView.h │ ├── YSCVoiceWaveView.m │ ├── YSCVolumeQueue.h │ ├── YSCVolumeQueue.m │ ├── circleBackground.png │ ├── fill.png │ ├── firstLine.png │ ├── maskImage.png │ └── secondLine.png └── waterWave │ ├── YSCWaterWaveView.h │ └── YSCWaterWaveView.m └── YSCAnimationDemo ├── YSCAnimationDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── YSCAnimationDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── microPhone_bottom.imageset │ │ ├── Contents.json │ │ ├── microPhone_bottom@2x.png │ │ └── microPhone_bottom@3x.png │ └── microPhone_top.imageset │ │ ├── Contents.json │ │ ├── microPhone_top@2x.png │ │ └── microPhone_top@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Demo controllers │ ├── LoadGif │ │ ├── YSCloadGifViewController.h │ │ └── YSCloadGifViewController.m │ ├── carouselTitle │ │ ├── YSCCarouselTitleViewController.h │ │ └── YSCCarouselTitleViewController.m │ ├── custom wave │ │ ├── YSCWaveAnimationViewController.h │ │ └── YSCWaveAnimationViewController.m │ ├── custom │ │ ├── YSCLayerAnimationViewController.h │ │ └── YSCLayerAnimationViewController.m │ ├── emitter │ │ ├── YSCButterflyViewController.h │ │ ├── YSCButterflyViewController.m │ │ ├── YSCEmitterAnimationViewController.h │ │ ├── YSCEmitterAnimationViewController.m │ │ ├── YSCFireViewController.h │ │ └── YSCFireViewController.m │ ├── heartBeatPulse │ │ ├── YSCHeartBeatPulseViewController.h │ │ └── YSCHeartBeatPulseViewController.m │ ├── maskAnimation │ │ ├── YSCCircleLoadAnimationViewController.h │ │ ├── YSCCircleLoadAnimationViewController.m │ │ ├── YSCFanshapedViewController.h │ │ ├── YSCFanshapedViewController.m │ │ ├── YSCMaskAnimationViewController.h │ │ ├── YSCMaskAnimationViewController.m │ │ ├── YSCMicrophoneWaveViewController.h │ │ └── YSCMicrophoneWaveViewController.m │ ├── replicator │ │ ├── YSCCircleReplicatorAnimationViewController.h │ │ ├── YSCCircleReplicatorAnimationViewController.m │ │ ├── YSCCircleRippleViewController.h │ │ ├── YSCCircleRippleViewController.m │ │ ├── YSCReplicatorAnimationViewController.h │ │ └── YSCReplicatorAnimationViewController.m │ ├── ripple │ │ ├── YSCRippleAnimationViewController.h │ │ └── YSCRippleAnimationViewController.m │ ├── seawater │ │ ├── YSCSeaWaterWaveViewController.h │ │ └── YSCSeaWaterWaveViewController.m │ ├── tableRefresh │ │ ├── YSCRefreshTableViewController.h │ │ └── YSCRefreshTableViewController.m │ ├── voice wave │ │ ├── YSCVoiceWaveViewController.h │ │ └── YSCVoiceWaveViewController.m │ └── water wave │ │ ├── YSCWaterWaveViewController.h │ │ └── YSCWaterWaveViewController.m ├── Info.plist ├── Resource │ ├── butterfly1.png │ ├── butterfly2.png │ ├── fire.png │ ├── music.png │ ├── sw3.gif │ └── tree.jpg ├── ViewController.h ├── ViewController.m ├── YSCAnimationDemoPrefixHeader.pch ├── YSCAnimationDemoViewController.h ├── YSCAnimationDemoViewController.m ├── YSCGlobleMethod.h ├── YSCGlobleMethod.m └── main.m ├── YSCAnimationDemoTests ├── Info.plist └── YSCAnimationDemoTests.m └── YSCAnimationDemoUITests ├── Info.plist └── YSCAnimationDemoUITests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /YSCAnimation/LoadGif/YSCGifLoadImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCGifLoadImage.h 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/10/18. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCGifLoadImage : UIImage 12 | 13 | @property (nonatomic,readonly) NSTimeInterval *frameDurations; 14 | @property (nonatomic,readonly) NSUInteger loopCount; 15 | @property (nonatomic,readonly) NSTimeInterval totalDuratoin; 16 | 17 | -(UIImage *)getFrameWithIndex:(NSUInteger)idx; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /YSCAnimation/LoadGif/YSCGifLoadImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCGifLoadImage.m 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/10/18. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCGifLoadImage.h" 10 | #import 11 | #import 12 | 13 | //获取当前ref的时间 14 | inline static NSTimeInterval CGImageSourceGetGifFrameDelay(CGImageSourceRef imageSource, NSUInteger index) 15 | { 16 | NSTimeInterval frameDuration = 0; 17 | CFDictionaryRef theImageProperties; 18 | if ((theImageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, index, NULL))) { 19 | CFDictionaryRef gifProperties; 20 | if (CFDictionaryGetValueIfPresent(theImageProperties, kCGImagePropertyGIFDictionary, (const void **)&gifProperties)) { 21 | const void *frameDurationValue; 22 | if (CFDictionaryGetValueIfPresent(gifProperties, kCGImagePropertyGIFUnclampedDelayTime, &frameDurationValue)) { 23 | frameDuration = [(__bridge NSNumber *)frameDurationValue doubleValue]; 24 | if (frameDuration <= 0) { 25 | if (CFDictionaryGetValueIfPresent(gifProperties, kCGImagePropertyGIFDelayTime, &frameDurationValue)) { 26 | frameDuration = [(__bridge NSNumber *)frameDurationValue doubleValue]; 27 | } 28 | } 29 | } 30 | } 31 | CFRelease(theImageProperties); 32 | } 33 | 34 | #ifndef OLExactGIFRepresentation 35 | //Implement as Browsers do. 36 | //See: http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser-compatibility 37 | //Also: http://blogs.msdn.com/b/ieinternals/archive/2010/06/08/animated-gifs-slow-down-to-under-20-frames-per-second.aspx 38 | 39 | if (frameDuration < 0.02 - FLT_EPSILON) { 40 | frameDuration = 0.1; 41 | } 42 | #endif 43 | return frameDuration; 44 | } 45 | 46 | //判断是否是gif 47 | inline static BOOL CGImageSourceContainsAnimatedGif(CGImageSourceRef imageSource) 48 | { 49 | return imageSource && UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF) && CGImageSourceGetCount(imageSource) > 1; 50 | } 51 | 52 | //判断是否是双倍图 53 | inline static BOOL isRetinaFilePath(NSString *path) 54 | { 55 | NSRange retinaSuffixRange = [[path lastPathComponent] rangeOfString:@"@2x" options:NSCaseInsensitiveSearch]; 56 | return retinaSuffixRange.length && retinaSuffixRange.location != NSNotFound; 57 | } 58 | 59 | @interface YSCGifLoadImage () 60 | { 61 | CGImageSourceRef _imageSourceRef; 62 | CGFloat _scale; 63 | dispatch_queue_t readFrameQueue; 64 | } 65 | @property (nonatomic,readwrite) NSTimeInterval *frameDurations; 66 | @property (nonatomic,readwrite) NSUInteger loopCount; 67 | @property (nonatomic,readwrite) NSMutableArray *images; 68 | @property (nonatomic,readwrite) NSTimeInterval totalDuratoin; 69 | @property (nonatomic,readwrite) CGImageSourceRef incrementalSource; 70 | @end 71 | 72 | static int _prefetchedNum = 10; 73 | 74 | @implementation YSCGifLoadImage 75 | @synthesize images; 76 | 77 | #pragma mark 重写UIImage的创建方法 78 | - (id)initWithContentsOfFile:(NSString *)path 79 | { 80 | return [self initWithData:[NSData dataWithContentsOfFile:path] 81 | scale:isRetinaFilePath(path) ? 2.0f : 1.0f]; 82 | } 83 | 84 | - (id)initWithData:(NSData *)data 85 | { 86 | return [self initWithData:data scale:1.0f]; 87 | } 88 | 89 | - (id)initWithData:(NSData *)data scale:(CGFloat)scale 90 | { 91 | if (!data) { 92 | return nil; 93 | } 94 | 95 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(data), NULL); 96 | //如果是gif图,就用这种方式创建 97 | if (CGImageSourceContainsAnimatedGif(imageSource)) { 98 | self = [self initWithCGImageSource:imageSource scale:scale]; 99 | } else { 100 | if (scale == 1.0f) { 101 | self = [super initWithData:data]; 102 | } else { 103 | self = [super initWithData:data scale:scale]; 104 | } 105 | } 106 | 107 | if (imageSource) { 108 | CFRelease(imageSource); 109 | } 110 | 111 | return self; 112 | } 113 | 114 | //创建gif图片 115 | -(instancetype)initWithCGImageSource:(CGImageSourceRef)imageSource scale:(CGFloat)scale 116 | { 117 | self = [super init]; 118 | if (!imageSource || !self) { 119 | return nil; 120 | } 121 | CFRetain(imageSource); 122 | 123 | NSUInteger numberOfFrames = CGImageSourceGetCount(imageSource); 124 | 125 | NSDictionary *imageProperties = CFBridgingRelease(CGImageSourceCopyProperties(imageSource, NULL)); 126 | NSDictionary *gifProperties = [imageProperties objectForKey:(NSString *)kCGImagePropertyGIFDictionary]; 127 | //开辟空间 128 | self.frameDurations = (NSTimeInterval *)malloc(numberOfFrames * sizeof(NSTimeInterval)); 129 | //读取循环次数 130 | self.loopCount = [gifProperties[(NSString *)kCGImagePropertyGIFLoopCount] unsignedIntegerValue]; 131 | //创建所有图片的数值 132 | self.images = [NSMutableArray arrayWithCapacity:numberOfFrames]; 133 | 134 | NSNull *aNull = [NSNull null]; 135 | for (NSUInteger i = 0; i < numberOfFrames; ++i) { 136 | //读取每张土拍的显示时间,添加到数组中,并计算总时间 137 | [self.images addObject:aNull]; 138 | NSTimeInterval frameDuration = CGImageSourceGetGifFrameDelay(imageSource, i); 139 | self.frameDurations[i] = frameDuration; 140 | self.totalDuratoin += frameDuration; 141 | } 142 | //CFTimeInterval start = CFAbsoluteTimeGetCurrent(); 143 | // Load first frame 144 | NSUInteger num = MIN(_prefetchedNum, numberOfFrames); 145 | for (int i=0; i= self.images.count) { 204 | return self.images[0]; 205 | } 206 | //根据当前index 来获取gif图片的第几个图片 207 | UIImage *frame = nil; 208 | @synchronized (self.images) { 209 | frame = self.images[idx]; 210 | } 211 | //放回对应index的图片 212 | if (!frame) { 213 | CGImageRef image = CGImageSourceCreateImageAtIndex(_imageSourceRef, idx, NULL); 214 | frame = [UIImage imageWithCGImage:image scale:_scale orientation:UIImageOrientationUp]; 215 | CFRelease(image); 216 | } 217 | /** 218 | * 如果图片张数大于10,进行如下操作的目的是 219 | 由于该方法会频繁调用,为加快速度和节省内存,对取值所在的数组进行了替换,只保留10个内容 220 | 并随着的不断增大,对原来被替换的内容进行还原,但是被还原的个数和保留的个数总共为10个,这个是最开始进行的设置的大小 221 | */ 222 | if (self.images.count > _prefetchedNum) { 223 | if (idx != 0) { 224 | [self.images replaceObjectAtIndex:idx withObject:[NSNull null]]; 225 | } 226 | NSUInteger nextReadIdx = idx + _prefetchedNum; 227 | // for (NSUInteger i = idx + 1; i <= nextReadIdx; i++) { 228 | //保证每次的index都小于数组个数,从而使最大值的下一个是最小值 229 | NSUInteger _idx = nextReadIdx%self.images.count; 230 | if ([self.images[_idx] isKindOfClass:[NSNull class]]) { 231 | dispatch_async(readFrameQueue, ^{ 232 | CGImageRef image = CGImageSourceCreateImageAtIndex(_imageSourceRef, _idx, NULL); 233 | @synchronized (self.images) { 234 | [self.images replaceObjectAtIndex:_idx withObject:[UIImage imageWithCGImage:image scale:_scale orientation:UIImageOrientationUp]]; 235 | } 236 | CFRelease(image); 237 | }); 238 | } 239 | // } 240 | } 241 | return frame; 242 | } 243 | 244 | -(CGSize)size 245 | { 246 | if (self.images.count) { 247 | return [[self.images objectAtIndex:0] size]; 248 | } 249 | return [super size]; 250 | } 251 | 252 | -(CGImageRef)CGImage 253 | { 254 | if (self.images.count) { 255 | return [[self.images objectAtIndex:0] CGImage]; 256 | } 257 | return [super CGImage]; 258 | } 259 | 260 | -(UIImageOrientation)imageOrientation 261 | { 262 | if (self.images.count) { 263 | return [[self.images objectAtIndex:0] imageOrientation]; 264 | } 265 | return [super imageOrientation]; 266 | } 267 | 268 | -(CGFloat)scale 269 | { 270 | if (self.images.count) { 271 | return [(UIImage *)[self.images objectAtIndex:0] scale]; 272 | } 273 | return [super scale]; 274 | } 275 | 276 | -(NSTimeInterval)duration 277 | { 278 | return self.images ? self.totalDuratoin : [super duration]; 279 | } 280 | 281 | -(void)dealloc 282 | { 283 | if (_imageSourceRef) { 284 | CFRelease(_imageSourceRef); 285 | } 286 | free(_frameDurations); 287 | if (_incrementalSource) { 288 | CFRelease(_incrementalSource); 289 | } 290 | } 291 | 292 | @end 293 | -------------------------------------------------------------------------------- /YSCAnimation/LoadGif/YSCGifLoadImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCGifLoadImageView.h 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/10/18. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YSCGifLoadImage.h" 11 | 12 | typedef void(^GifAnimatingCompleteCallBack)(void); 13 | 14 | @interface YSCGifLoadImageView : UIImageView 15 | 16 | /** 17 | * 开始播放 18 | * 19 | * @param gifRepeatCount 播放次数 20 | * @param gifRepeatDelayOffset 每次播放时间间隔 21 | * @param completeCallBack gif播放完后的回掉 22 | */ 23 | - (void)startGifAnimatingWithGifRepeatCount:(NSInteger)gifRepeatCount gifRepeatDelayOffset:(CGFloat)gifRepeatDelayOffset animatingCompleteCallBack:(GifAnimatingCompleteCallBack)completeCallBack; 24 | 25 | - (void)stopGifAnimation; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /YSCAnimation/LoadGif/YSCGifLoadImageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCGifLoadImageView.m 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/10/18. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCGifLoadImageView.h" 10 | 11 | @interface YSCGifLoadImageView () 12 | { 13 | int tmp; 14 | } 15 | @property (nonatomic) NSUInteger currentFrameIndex; 16 | @property (nonatomic,strong) YSCGifLoadImage *animatedImage; 17 | @property (nonatomic,strong) CADisplayLink *displayLink; 18 | @property (nonatomic) NSTimeInterval accumulator; 19 | @property (nonatomic,strong) UIImage *currentFrame; 20 | 21 | @property (nonatomic,copy) NSString *runLoopMode; 22 | @property (nonatomic,assign) NSInteger gifRepeatCount; 23 | @property (nonatomic,assign) CGFloat gifRepeatDelayOffset; 24 | @property (nonatomic,copy) GifAnimatingCompleteCallBack completeCallBack; 25 | @end 26 | 27 | @implementation YSCGifLoadImageView { 28 | int _delayOffsetCount;//每次播放间隔时间所对应的displayLink计数(1/60秒一次计数) 29 | BOOL _showCompleteOnce;//完整播放一次gif 30 | } 31 | 32 | const NSTimeInterval kMaxTimeStep = 1; 33 | @synthesize runLoopMode = _runLoopMode; 34 | @synthesize displayLink = _displayLink; 35 | 36 | -(instancetype)init 37 | { 38 | if (self = [super init]) { 39 | self.currentFrameIndex = 0; 40 | self.accumulator = 0; 41 | self.gifRepeatCount = 1; 42 | self.gifRepeatDelayOffset = 0.0; 43 | _showCompleteOnce = NO; 44 | _gifRepeatCount = 0; 45 | } 46 | return self; 47 | } 48 | 49 | //停止动画 50 | - (void)stopGifAnimation 51 | { 52 | [self stopAnimating]; 53 | [self removeFromSuperview]; 54 | [_displayLink invalidate]; 55 | _displayLink = nil; 56 | } 57 | 58 | -(void)stopAnimating 59 | { 60 | //如果不是gif就返回父类方法 61 | if (!self.animatedImage) { 62 | [super stopAnimating]; 63 | return; 64 | } 65 | self.displayLink.paused = YES; 66 | } 67 | 68 | //开始动画 69 | - (void)startGifAnimatingWithGifRepeatCount:(NSInteger)gifRepeatCount gifRepeatDelayOffset:(CGFloat)gifRepeatDelayOffset animatingCompleteCallBack:(GifAnimatingCompleteCallBack)completeCallBack 70 | { 71 | self.gifRepeatCount = gifRepeatCount; 72 | self.gifRepeatDelayOffset = gifRepeatDelayOffset; 73 | _completeCallBack = completeCallBack; 74 | [self startAnimating]; 75 | } 76 | 77 | -(void)startAnimating 78 | { 79 | if (!self.animatedImage) { 80 | [super startAnimating]; 81 | return; 82 | } 83 | if (self.isAnimating) { 84 | return; 85 | } 86 | 87 | _showCompleteOnce = NO; 88 | self.currentFrameIndex = 0; 89 | self.accumulator = 0; 90 | self.displayLink.paused = NO; 91 | } 92 | 93 | //切换动画的关键方法 94 | -(void)changeKeyframe:(CADisplayLink *)displayLink 95 | { 96 | if (self.currentFrameIndex > self.animatedImage.images.count) { 97 | return; 98 | } else if (_showCompleteOnce) {//每次播放间隔delay 99 | if (_delayOffsetCount > 0) { 100 | _delayOffsetCount--; 101 | return; 102 | } else if (0 == _delayOffsetCount) { 103 | self.currentFrameIndex = 0; 104 | _delayOffsetCount = self.gifRepeatDelayOffset * 60; 105 | _showCompleteOnce = NO; 106 | } 107 | } 108 | //这里就是不停的取图,不停的设置,然后不停的调用displayLayer:方法 109 | self.accumulator += fmin(displayLink.duration, kMaxTimeStep); 110 | while (self.accumulator >= self.animatedImage.frameDurations[self.currentFrameIndex]) { 111 | self.accumulator -= self.animatedImage.frameDurations[self.currentFrameIndex]; 112 | if (++self.currentFrameIndex >= self.animatedImage.images.count) { 113 | if (--self.gifRepeatCount == 0) { 114 | [self stopAnimating]; 115 | if (_completeCallBack) { 116 | _completeCallBack(); 117 | } 118 | return; 119 | } 120 | _showCompleteOnce = YES; 121 | } 122 | self.currentFrameIndex = MIN(self.currentFrameIndex, self.animatedImage.images.count - 1); 123 | self.currentFrame = [self.animatedImage getFrameWithIndex:self.currentFrameIndex]; 124 | [self.layer setNeedsDisplay]; 125 | } 126 | } 127 | 128 | //绘制图片 129 | -(void)displayLayer:(CALayer *)layer 130 | { 131 | if (!self.animatedImage || [self.animatedImage.images count] == 0) { 132 | return; 133 | } 134 | if(self.currentFrame && ![self.currentFrame isKindOfClass:[NSNull class]]){ 135 | layer.contents = (__bridge id)([self.currentFrame CGImage]); 136 | } 137 | } 138 | 139 | -(void)setHighlighted:(BOOL)highlighted 140 | { 141 | if (!self.animatedImage) { 142 | [super setHighlighted:highlighted]; 143 | } 144 | } 145 | 146 | -(CGSize)sizeThatFits:(CGSize)size 147 | { 148 | return self.image.size; 149 | } 150 | 151 | #pragma mark - setters 152 | 153 | -(void)setRunLoopMode:(NSString *)runLoopMode 154 | { 155 | //这个地方需要重写,因为CADisplayLink是依赖在runloop中的,所以如果设置了imageview的runloop的话 156 | //就要停止动画,并重新设置CADisplayLink对应的runloop,最后在根据情况是否开始动画 157 | if (runLoopMode != _runLoopMode) { 158 | [self stopAnimating]; 159 | NSRunLoop *runloop = [NSRunLoop mainRunLoop]; 160 | [self.displayLink removeFromRunLoop:runloop forMode:_runLoopMode]; 161 | [self.displayLink addToRunLoop:runloop forMode:runLoopMode]; 162 | 163 | _runLoopMode = runLoopMode; 164 | } 165 | } 166 | 167 | -(void)setImage:(UIImage *)image 168 | { 169 | if (image == self.image) { 170 | return; 171 | } 172 | 173 | [self stopAnimating]; 174 | 175 | if ([image isKindOfClass:[YSCGifLoadImage class]] && image.images) { 176 | 177 | //设置静止态的图片 178 | if (image.images[0]) { 179 | [super setImage:image.images[0]]; 180 | }else{ 181 | [super setImage:nil]; 182 | } 183 | self.animatedImage = (YSCGifLoadImage *)image; 184 | }else{ 185 | self.animatedImage = nil; 186 | [super setImage:image]; 187 | } 188 | [self.layer setNeedsDisplay]; 189 | } 190 | 191 | //如果知道这个图就是gif,那可以直接调用这个方法 192 | -(void)setAnimatedImage:(YSCGifLoadImage *)animatedImage 193 | { 194 | _animatedImage = animatedImage; 195 | if (animatedImage == nil) { 196 | self.layer.contents = nil; 197 | } 198 | } 199 | 200 | - (NSUInteger)loopCount 201 | { 202 | if (self.gifRepeatCount > 0) { 203 | return self.gifRepeatCount; 204 | } else { 205 | return self.animatedImage.loopCount ? : 1; 206 | } 207 | 208 | return 0; 209 | } 210 | 211 | - (void)setGifRepeatDelayOffset:(CGFloat)gifRepeatDelayOffset 212 | { 213 | _gifRepeatDelayOffset = gifRepeatDelayOffset; 214 | _delayOffsetCount = gifRepeatDelayOffset * 60; 215 | } 216 | 217 | #pragma mark - getters 218 | 219 | -(UIImage *)image 220 | { 221 | return self.animatedImage ? : [super image]; 222 | } 223 | 224 | -(CADisplayLink *)displayLink 225 | { 226 | //如果有superview就是已经创建了,创建时新建一个CADisplayLink,并制定方法,最后加到一个Runloop中,完成创建 227 | if (self.superview) { 228 | if (!_displayLink && self.animatedImage) { 229 | 230 | _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(changeKeyframe:)]; 231 | [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.runLoopMode]; 232 | _displayLink.paused = YES; 233 | } 234 | }else{ 235 | [_displayLink invalidate]; 236 | _displayLink = nil; 237 | } 238 | return _displayLink; 239 | } 240 | 241 | -(NSString *)runLoopMode 242 | { 243 | return _runLoopMode ?: NSRunLoopCommonModes; 244 | } 245 | 246 | //判断是否正在进行动画 247 | -(BOOL)isAnimating 248 | { 249 | return [super isAnimating] || (_displayLink && !_displayLink.isPaused); 250 | } 251 | 252 | @end 253 | -------------------------------------------------------------------------------- /YSCAnimation/carouselTitle/YSCCarouselTitleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCarouselTitleView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/12/22. 6 | // Copyright © 2016年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^YSCCarouselTitleViewTappedBlock)(void); 12 | 13 | @interface YSCCarouselTitleView : UIView 14 | 15 | @property (nonatomic, strong) NSString *title; 16 | @property (nonatomic, copy) YSCCarouselTitleViewTappedBlock tappedBlock; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /YSCAnimation/carouselTitle/YSCCarouselTitleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCarouselTitleView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/12/22. 6 | // Copyright © 2016年 MMS. All rights reserved. 7 | // 8 | 9 | #import "YSCCarouselTitleView.h" 10 | //#import "NSString+MMSAdditions.h" 11 | 12 | typedef NS_ENUM(NSInteger, MMSTitleViewType) { 13 | MMSTitleViewTypeStatic, //静态 14 | MMSTitleViewTypeCarousel, //轮播 15 | }; 16 | 17 | @interface YSCCarouselTitleView () 18 | 19 | @property (nonatomic, strong) UIView *contentView; 20 | @property (nonatomic, strong) UILabel *staticTitleLabel; 21 | @property (nonatomic, strong) UILabel *firstCarouselTitleLabel; 22 | @property (nonatomic, strong) UILabel *secondCarouselTitleLabel; 23 | @property (nonatomic, assign) MMSTitleViewType type; 24 | 25 | @end 26 | 27 | static CGFloat enlargeHitRectGap = 15; 28 | static CGFloat labelOffset = 50;//轮播的两个label的间距 29 | static CGFloat carouselSpeed = 25.0;//轮播速度 25个屏幕点单位/秒 30 | 31 | @implementation YSCCarouselTitleView 32 | 33 | - (instancetype)initWithFrame:(CGRect)frame 34 | { 35 | self = [super initWithFrame:frame]; 36 | if (self) { 37 | [self setUpSubviews]; 38 | _type = MMSTitleViewTypeStatic; 39 | self.layer.masksToBounds = YES; 40 | } 41 | return self; 42 | } 43 | 44 | - (instancetype)initWithCoder:(NSCoder *)coder 45 | { 46 | self = [super initWithCoder:coder]; 47 | if (self) { 48 | [self setUpSubviews]; 49 | _type = MMSTitleViewTypeStatic; 50 | self.layer.masksToBounds = YES; 51 | } 52 | return self; 53 | } 54 | 55 | - (void)setUpSubviews 56 | { 57 | self.contentView = [[UIView alloc] init]; 58 | [self addSubview:self.contentView]; 59 | 60 | self.staticTitleLabel = [self getTitleLabel]; 61 | _staticTitleLabel.textAlignment = NSTextAlignmentCenter; 62 | _staticTitleLabel.hidden = YES; 63 | [self.contentView addSubview:_staticTitleLabel]; 64 | 65 | self.firstCarouselTitleLabel = [self getTitleLabel]; 66 | _firstCarouselTitleLabel.textAlignment = NSTextAlignmentLeft; 67 | _firstCarouselTitleLabel.hidden = YES; 68 | [self.contentView addSubview:_firstCarouselTitleLabel]; 69 | 70 | self.secondCarouselTitleLabel = [self getTitleLabel]; 71 | _secondCarouselTitleLabel.textAlignment = NSTextAlignmentLeft; 72 | _secondCarouselTitleLabel.hidden = YES; 73 | [self.contentView addSubview:_secondCarouselTitleLabel]; 74 | 75 | [self addTapGestureRecognizer]; 76 | } 77 | 78 | - (void)addTapGestureRecognizer 79 | { 80 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(titleTapped:)]; 81 | [self addGestureRecognizer:tap]; 82 | } 83 | 84 | - (void)setTitle:(NSString *)title 85 | { 86 | _title = title; 87 | _staticTitleLabel.text = _title; 88 | _firstCarouselTitleLabel.text = _title; 89 | _secondCarouselTitleLabel.text = _title; 90 | [self updateTitleViewType]; 91 | [self setNeedsLayout]; 92 | } 93 | 94 | - (void)layoutSubviews 95 | { 96 | _contentView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 97 | _staticTitleLabel.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 98 | 99 | CGFloat titleWidth = [self getTitleWidth]; 100 | _firstCarouselTitleLabel.frame = CGRectMake(0, 0, titleWidth + labelOffset, self.frame.size.height); 101 | _secondCarouselTitleLabel.frame = CGRectMake(titleWidth + labelOffset, 0, titleWidth + labelOffset, self.frame.size.height); 102 | 103 | [self updateTitleViewType]; 104 | [_contentView.layer removeAllAnimations]; 105 | if (MMSTitleViewTypeStatic == _type) { 106 | _staticTitleLabel.hidden = NO; 107 | _firstCarouselTitleLabel.hidden = YES; 108 | _secondCarouselTitleLabel.hidden = YES; 109 | } else if (MMSTitleViewTypeCarousel == _type) { 110 | _staticTitleLabel.hidden = YES; 111 | _firstCarouselTitleLabel.hidden = NO; 112 | _secondCarouselTitleLabel.hidden = NO; 113 | [self addCarouselAnimation]; 114 | } 115 | } 116 | 117 | - (void)titleTapped:(UITapGestureRecognizer *)gesture 118 | { 119 | if (_tappedBlock) { 120 | _tappedBlock(); 121 | } 122 | } 123 | 124 | - (void)addCarouselAnimation 125 | { 126 | CGFloat titleWidth = [self getTitleWidth]; 127 | 128 | CAKeyframeAnimation *aniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"]; 129 | aniamtion.values = @[@0, @0, @(-titleWidth - labelOffset), @(-titleWidth - labelOffset)]; 130 | aniamtion.keyTimes = @[@0, @0.1, @0.9, @1.0]; 131 | aniamtion.duration = (titleWidth + labelOffset) / carouselSpeed; 132 | aniamtion.repeatCount = INFINITY; 133 | aniamtion.removedOnCompletion = NO; 134 | 135 | [_contentView.layer addAnimation:aniamtion forKey:@"carousel"]; 136 | } 137 | 138 | - (void)updateTitleViewType 139 | { 140 | if (!_title || 0 == self.frame.size.width || 0 == self.frame.size.height) { 141 | return; 142 | } 143 | CGFloat titleWidth = [self getTitleWidth]; 144 | if (titleWidth > self.frame.size.width - 2) { 145 | _type = MMSTitleViewTypeCarousel; 146 | } else { 147 | _type = MMSTitleViewTypeStatic; 148 | } 149 | } 150 | 151 | - (CGFloat)getTitleWidth 152 | { 153 | if (!_title || 0 == self.frame.size.width || 0 == self.frame.size.height) { 154 | return 0.0; 155 | } 156 | NSDictionary *attributes = @{NSFontAttributeName :[UIFont systemFontOfSize:18]}; 157 | CGFloat width = [YSCGlobleMethod getStringWidth:_title withRect:self.frame attributes:attributes]; 158 | 159 | return width; 160 | } 161 | 162 | - (UILabel *)getTitleLabel 163 | { 164 | UILabel *label = [[UILabel alloc] init]; 165 | label.font = [UIFont systemFontOfSize:18]; 166 | label.textColor = [UIColor blackColor]; 167 | label.numberOfLines = 1; 168 | 169 | return label; 170 | } 171 | 172 | 173 | - (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*) event 174 | { 175 | CGRect rect = [self enlargedHitRect]; 176 | if (CGRectEqualToRect(rect, self.bounds)) { 177 | return [super hitTest:point withEvent:event]; 178 | } 179 | return CGRectContainsPoint(rect, point) ? self : nil; 180 | } 181 | 182 | //扩大点击区域 183 | - (CGRect)enlargedHitRect 184 | { 185 | return CGRectMake(self.bounds.origin.x - enlargeHitRectGap, 186 | self.bounds.origin.y - enlargeHitRectGap, 187 | self.bounds.size.width + 2 * enlargeHitRectGap, 188 | self.bounds.size.height + 2 * enlargeHitRectGap); 189 | } 190 | 191 | @end 192 | -------------------------------------------------------------------------------- /YSCAnimation/circleLoad/YSCCircleLoadAnimationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleLoadAnimationView.h 3 | // AnimationDemo 4 | // 5 | // Created by yushichao on 16/2/15. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCCircleLoadAnimationView : UIView 12 | 13 | - (void)startLoading; 14 | 15 | @property (nonatomic, strong) UIImageView *loadingImage; 16 | @property (nonatomic, assign) CGFloat circleRadius; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /YSCAnimation/circleLoad/YSCCircleLoadAnimationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleLoadAnimationView.m 3 | // AnimationDemo 4 | // 5 | // Created by yushichao on 16/2/15. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCCircleLoadAnimationView.h" 10 | 11 | @interface YSCCircleLoadAnimationView () 12 | 13 | @property (nonatomic, strong) CAShapeLayer *shapeLayer; 14 | @property (nonatomic, strong) CAShapeLayer *circleLayer; 15 | @property (nonatomic, strong) CAShapeLayer *pathAnimationLayer; 16 | 17 | @end 18 | 19 | @implementation YSCCircleLoadAnimationView 20 | 21 | - (void)removeFromParentView 22 | { 23 | if (self.superview) { 24 | [_circleLayer removeFromSuperlayer]; 25 | [_pathAnimationLayer removeFromSuperlayer]; 26 | [self removeFromSuperview]; 27 | [self.layer removeAllAnimations]; 28 | } 29 | } 30 | 31 | - (instancetype)initWithFrame:(CGRect)frame 32 | { 33 | self = [super initWithFrame:frame]; 34 | if (self) { 35 | _circleRadius = 0; 36 | self.layer.backgroundColor = [UIColor grayColor].CGColor; 37 | [self addShapeLayer]; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)addShapeLayer 43 | { 44 | self.layer.masksToBounds = YES; 45 | [self insertSubview:self.loadingImage atIndex:0]; 46 | [self.layer addSublayer:self.pathAnimationLayer]; 47 | [self.layer addSublayer:self.circleLayer]; 48 | } 49 | 50 | 51 | - (void)startLoading 52 | { 53 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 54 | 55 | animation.fromValue = [NSNumber numberWithFloat:0.0]; 56 | animation.toValue = [NSNumber numberWithFloat:1.0]; 57 | animation.duration = 2.0; 58 | animation.delegate = self; 59 | [_circleLayer addAnimation:animation forKey:@"end"]; 60 | } 61 | 62 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 63 | { 64 | [_circleLayer removeFromSuperlayer]; 65 | _loadingImage.layer.mask = _pathAnimationLayer; 66 | 67 | [self addMaskAnimation]; 68 | } 69 | 70 | - (void)addMaskAnimation 71 | { 72 | CGFloat radius = 0 != _circleRadius ? _circleRadius : 50; 73 | UIBezierPath *beginPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0) radius:radius startAngle:0 endAngle:2 * M_PI clockwise:YES]; 74 | CGFloat maxRadius = fmax(self.bounds.size.width, self.bounds.size.height); 75 | UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0) radius:maxRadius/2.0 startAngle:0 endAngle:2 * M_PI clockwise:YES]; 76 | 77 | _pathAnimationLayer.path = endPath.CGPath; 78 | _pathAnimationLayer.lineWidth = maxRadius; 79 | 80 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 81 | pathAnimation.fromValue = (__bridge id _Nullable)(beginPath.CGPath); 82 | pathAnimation.toValue = (__bridge id _Nullable)(endPath.CGPath); 83 | pathAnimation.duration = 2.0; 84 | 85 | CABasicAnimation *widthAnimation = [CABasicAnimation animationWithKeyPath:@"lineWidth"]; 86 | widthAnimation.fromValue = [NSNumber numberWithFloat:0]; 87 | widthAnimation.toValue = [NSNumber numberWithFloat:maxRadius]; 88 | widthAnimation.duration = 2.0; 89 | 90 | CAAnimationGroup *group = [CAAnimationGroup animation]; 91 | group.animations = @[pathAnimation, widthAnimation]; 92 | group.duration = 2.0; 93 | 94 | [_pathAnimationLayer addAnimation:pathAnimation forKey:@"pathAnimation"]; 95 | [_pathAnimationLayer addAnimation:widthAnimation forKey:@"widthAnimation"]; 96 | 97 | } 98 | 99 | - (CAShapeLayer *)circleLayer 100 | { 101 | if (!_circleLayer) { 102 | _circleLayer = [[CAShapeLayer alloc] init]; 103 | _circleLayer.position = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0); 104 | _circleLayer.bounds = self.bounds; 105 | _circleLayer.backgroundColor = [UIColor grayColor].CGColor; 106 | CGFloat radius = 0 != _circleRadius ? _circleRadius : 50; 107 | UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0) radius:radius startAngle:0 endAngle:2 * M_PI clockwise:YES]; 108 | _circleLayer.path = circlePath.CGPath; 109 | _circleLayer.fillColor = [UIColor clearColor].CGColor; 110 | _circleLayer.strokeColor = [UIColor greenColor].CGColor; 111 | _circleLayer.lineWidth = 3.0; 112 | _circleLayer.strokeStart = 0.0;// 113 | _circleLayer.strokeEnd = 1.0; 114 | } 115 | 116 | return _circleLayer; 117 | } 118 | 119 | - (CAShapeLayer *)pathAnimationLayer 120 | { 121 | if (!_pathAnimationLayer) { 122 | self.pathAnimationLayer = [[CAShapeLayer alloc] init]; 123 | _pathAnimationLayer.position = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0); 124 | _pathAnimationLayer.bounds = self.bounds; 125 | ///透明色 126 | _pathAnimationLayer.backgroundColor = [UIColor clearColor].CGColor; 127 | CGFloat radius = 0 != _circleRadius ? _circleRadius : 50; 128 | UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0) radius:radius startAngle:0 endAngle:2 * M_PI clockwise:YES]; 129 | _pathAnimationLayer.path = circlePath.CGPath; 130 | _pathAnimationLayer.fillColor = [UIColor clearColor].CGColor; 131 | _pathAnimationLayer.strokeColor = [UIColor greenColor].CGColor; 132 | _pathAnimationLayer.lineWidth = 3.0; 133 | } 134 | 135 | return _pathAnimationLayer; 136 | } 137 | 138 | - (UIImageView *)loadingImage 139 | { 140 | if (!_loadingImage) { 141 | self.loadingImage = [[UIImageView alloc] initWithFrame:self.bounds]; 142 | } 143 | 144 | return _loadingImage; 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /YSCAnimation/commonWave/YSCWaveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaveView.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/2/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, YSCWaveType) { 12 | YSCWaveTypePulse, 13 | YSCWaveTypeMovedVoice, 14 | YSCWaveTypeVoice, 15 | }; 16 | 17 | @interface YSCWaveView : UIView 18 | 19 | - (void)removeFromParentView; 20 | 21 | - (void)showWaveViewWithType:(YSCWaveType)type; 22 | 23 | - (void)setWaveWidth:(CGFloat)width; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /YSCAnimation/fanshaped/YSCFanShapedView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCFanShapedView.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/2/18. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, YSCFanShapedShowType) { 12 | YSCFanShapedShowTypeExpand, 13 | YSCFanShapedShowTypeShrink, 14 | }; 15 | 16 | @interface YSCFanShapedView : UIView 17 | 18 | - (void)showInParentView:(UIView *)parentView WithType:(YSCFanShapedShowType)type; 19 | - (void)setShowType:(YSCFanShapedShowType)type; 20 | 21 | - (void)removeFromParentView; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /YSCAnimation/fanshaped/YSCFanShapedView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCFanShapedView.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/2/18. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCFanShapedView.h" 10 | 11 | @interface YSCFanShapedView () 12 | 13 | @property (nonatomic, strong) CALayer *fanShapeLayer; 14 | @property (nonatomic, strong) CAShapeLayer *maskLayer; 15 | @property (nonatomic, strong) CAShapeLayer *buttonMaskLayer; 16 | 17 | @end 18 | 19 | @implementation YSCFanShapedView 20 | 21 | - (void)removeFromParentView 22 | { 23 | if (self.superview) { 24 | [self removeAllSubLayers]; 25 | [self removeFromSuperview]; 26 | [self.layer removeAllAnimations]; 27 | } 28 | } 29 | 30 | - (void)removeAllSubLayers 31 | { 32 | for (NSInteger i = 0; [self.layer sublayers].count > 0; i++) { 33 | [[[self.layer sublayers] firstObject] removeFromSuperlayer]; 34 | } 35 | } 36 | 37 | - (void)showInParentView:(UIView *)parentView WithType:(YSCFanShapedShowType)type 38 | { 39 | if (![self.superview isKindOfClass:[parentView class]]) { 40 | [parentView addSubview:self]; 41 | } else { 42 | return; 43 | } 44 | 45 | _fanShapeLayer = [CALayer layer]; 46 | _fanShapeLayer.position = CGPointMake(150, 75); 47 | _fanShapeLayer.bounds = CGRectMake(0, 0, 300, 150); 48 | _fanShapeLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"fanshaped.jpg"].CGImage); 49 | [self.layer addSublayer:_fanShapeLayer]; 50 | 51 | //addMask 52 | _maskLayer = [CAShapeLayer layer]; 53 | _maskLayer.position = CGPointMake(150, 150); 54 | _maskLayer.bounds = CGRectMake(0, 0, 300, 150); 55 | _maskLayer.backgroundColor = [UIColor clearColor].CGColor; 56 | _maskLayer.fillColor = [UIColor greenColor].CGColor; 57 | _maskLayer.strokeColor = [UIColor redColor].CGColor; 58 | _maskLayer.lineWidth = 2; 59 | _maskLayer.anchorPoint = CGPointMake(0.5, 1.0); 60 | 61 | UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:160 startAngle:M_PI endAngle:2 * M_PI clockwise:YES]; 62 | [path closePath]; 63 | _maskLayer.path = path.CGPath; 64 | 65 | _fanShapeLayer.mask = _maskLayer; 66 | 67 | [self setShowType:type]; 68 | } 69 | 70 | - (void)setShowType:(YSCFanShapedShowType)type 71 | { 72 | CATransform3D beginTransform = CATransform3DIdentity; 73 | CATransform3D endTransform = CATransform3DIdentity; 74 | if (YSCFanShapedShowTypeExpand == type) {//打开扇形 75 | beginTransform = CATransform3DMakeRotation(M_PI, 0, 0, 1); 76 | endTransform = CATransform3DMakeRotation(0, 0, 0, 1); 77 | } else if (YSCFanShapedShowTypeShrink == type) {//关闭扇形 78 | beginTransform = CATransform3DMakeRotation(0, 0, 0, 1); 79 | endTransform = CATransform3DMakeRotation(M_PI, 0, 0, 1); 80 | } 81 | 82 | _maskLayer.transform = endTransform; 83 | 84 | CABasicAnimation *aniamtion = [CABasicAnimation animationWithKeyPath:@"transform"]; 85 | aniamtion.fromValue = [NSValue valueWithCATransform3D:beginTransform]; 86 | aniamtion.toValue = [NSValue valueWithCATransform3D:endTransform]; 87 | aniamtion.duration = 3.0; 88 | 89 | [_maskLayer addAnimation:aniamtion forKey:@"fanShape"]; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /YSCAnimation/fanshaped/fanshaped.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimation/fanshaped/fanshaped.jpg -------------------------------------------------------------------------------- /YSCAnimation/heartBeatPulse/YSCHeartBeatPulseView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCHeartBeatPulseView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/11/8. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCHeartBeatPulseView : UIView 12 | 13 | - (void)startHeartBeat; 14 | 15 | - (void)pauseHeartBeat; 16 | 17 | - (void)stopHeartBeat; 18 | 19 | - (void)setHeartBeatSpeed:(NSInteger)speed; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /YSCAnimation/heartBeatPulse/YSCHeartBeatPulseView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCHeartBeatPulseView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/11/8. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCHeartBeatPulseView.h" 10 | 11 | @interface YSCHeartBeatPulseView () 12 | 13 | @property (nonatomic, strong) CAShapeLayer *pulseShapeLayer; 14 | @property (nonatomic, strong) CADisplayLink *displayLink; 15 | @end 16 | 17 | @implementation YSCHeartBeatPulseView { 18 | CGFloat _density;//x轴绘制粒度,也可用其控制速度 19 | CGFloat _halfWaveLength;//半波长 20 | CGFloat _waveHeight; 21 | CGFloat _beginX; 22 | NSInteger _currentTimeCount;//当前时间计数 23 | NSInteger _maxTimeCount;//最大时间计数,超过后wave开始移动 24 | CGFloat _currentWaveWidth; 25 | } 26 | 27 | 28 | - (instancetype)initWithFrame:(CGRect)frame 29 | { 30 | self = [super initWithFrame:frame]; 31 | if (self) { 32 | [self initData]; 33 | [self setupSubviews]; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)initData 39 | { 40 | _density = 1.0; 41 | _halfWaveLength = 40; 42 | _waveHeight = self.frame.size.height; 43 | _beginX = 0.0; 44 | _currentTimeCount = 0; 45 | _maxTimeCount = (self.frame.size.width - 50) / _density; 46 | _currentWaveWidth = 0.0; 47 | } 48 | 49 | - (void)setupSubviews 50 | { 51 | [self.layer addSublayer:self.pulseShapeLayer]; 52 | } 53 | 54 | - (void)startHeartBeat 55 | { 56 | self.displayLink.paused = NO; 57 | } 58 | 59 | - (void)pauseHeartBeat 60 | { 61 | self.displayLink.paused = YES; 62 | } 63 | 64 | - (void)stopHeartBeat 65 | { 66 | [self.displayLink invalidate]; 67 | self.displayLink = nil; 68 | } 69 | 70 | - (void)setHeartBeatSpeed:(NSInteger)speed 71 | { 72 | _density = _density * speed; 73 | _maxTimeCount = (self.frame.size.width - 50) / _density; 74 | } 75 | 76 | - (void)invokeDisplayLinkCallback 77 | { 78 | _currentTimeCount++; 79 | self.pulseShapeLayer.path = [self generateBezierPathWithTimeCount:_currentTimeCount].CGPath; 80 | 81 | //移动wave 82 | if (_currentTimeCount > _maxTimeCount) { 83 | self.bounds = CGRectMake(_density * (_currentTimeCount - _maxTimeCount), 0, self.frame.size.width, self.frame.size.height); 84 | } 85 | } 86 | 87 | #pragma mark - getters 88 | 89 | - (UIBezierPath *)generateBezierPathWithTimeCount:(NSInteger)timeCount 90 | { 91 | if (timeCount <= 0) { 92 | return nil; 93 | } 94 | UIBezierPath *pulselinePath = [UIBezierPath bezierPath]; 95 | 96 | // sin (2pi * x) 97 | 98 | CGFloat amplitude = 0.0; 99 | CGFloat x = _beginX; 100 | for(NSInteger i = 0; i < timeCount; i++) { 101 | x += _density; 102 | amplitude = [self getAmplitude:x]; 103 | CGFloat y = amplitude * sinf(2 * M_PI * (0.5 * x / _halfWaveLength)) + (_waveHeight * 0.5); 104 | y = _waveHeight - y; 105 | if (0 == i) { 106 | [pulselinePath moveToPoint:CGPointMake(x, y)]; 107 | } 108 | else { 109 | [pulselinePath addLineToPoint:CGPointMake(x, y)]; 110 | } 111 | } 112 | 113 | return pulselinePath; 114 | } 115 | 116 | // 获取正弦波振幅,第一个半波振幅最大,第二个比较小,其余的很小,每6个半波数循环一次 117 | - (CGFloat)getAmplitude:(CGFloat)x 118 | { 119 | CGFloat amplitude = 0.0; 120 | NSInteger XOffset = (x - _beginX) / _halfWaveLength; 121 | XOffset = XOffset % 6; 122 | if (0 == XOffset) { 123 | amplitude = arc4random() % 50 + _waveHeight * 0.5 - 50; 124 | } else if (1 == XOffset) { 125 | amplitude = arc4random() % 10 + 10; 126 | } else { 127 | amplitude = arc4random() % 5; 128 | } 129 | 130 | return amplitude; 131 | } 132 | 133 | - (CADisplayLink *)displayLink 134 | { 135 | if (!_displayLink) { 136 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(invokeDisplayLinkCallback)]; 137 | [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 138 | _displayLink.paused = YES; 139 | } 140 | 141 | return _displayLink; 142 | } 143 | 144 | - (CAShapeLayer *)pulseShapeLayer 145 | { 146 | if (!_pulseShapeLayer) { 147 | self.pulseShapeLayer = [CAShapeLayer layer]; 148 | _pulseShapeLayer.lineCap = kCALineCapButt; 149 | _pulseShapeLayer.lineJoin = kCALineJoinRound; 150 | _pulseShapeLayer.strokeColor = [UIColor blackColor].CGColor; 151 | _pulseShapeLayer.fillColor = [UIColor clearColor].CGColor; 152 | _pulseShapeLayer.fillRule = @"even-odd"; 153 | _pulseShapeLayer.lineWidth = 2; 154 | _pulseShapeLayer.backgroundColor = [UIColor clearColor].CGColor; 155 | _pulseShapeLayer.position = CGPointMake(CGRectGetWidth(self.bounds) / 2.0, CGRectGetHeight(self.bounds) / 2.0); 156 | _pulseShapeLayer.bounds = self.bounds; 157 | } 158 | 159 | return _pulseShapeLayer; 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /YSCAnimation/microPhoneWave/YSCMicrophoneWaveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMicrophoneWaveView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/23. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCMicrophoneWaveView : UIView 12 | 13 | - (void)showMicrophoneWaveInParentView:(UIView *)parentView withFrame:(CGRect)frame; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /YSCAnimation/microPhoneWave/YSCMicrophoneWaveView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMicrophoneWaveView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/23. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCMicrophoneWaveView.h" 10 | 11 | @interface YSCMicrophoneWaveView () 12 | 13 | @property (nonatomic, strong) NSTimer *microphoneTimer; 14 | @property (nonatomic, strong) CAShapeLayer *microPhoneMaskLayer; 15 | 16 | @end 17 | 18 | @implementation YSCMicrophoneWaveView 19 | 20 | - (void)showMicrophoneWaveInParentView:(UIView *)parentView withFrame:(CGRect)frame 21 | { 22 | if (![self.superview isKindOfClass:[parentView class]]) { 23 | [parentView addSubview:self]; 24 | } else { 25 | return; 26 | } 27 | self.frame = frame; 28 | 29 | UIImageView *bottomImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 43, 48)]; 30 | bottomImageView.image = [UIImage imageNamed:@"microPhone_bottom"]; 31 | bottomImageView.center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); 32 | [self addSubview:bottomImageView]; 33 | UIImageView *topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 43, 48)]; 34 | topImageView.image = [UIImage imageNamed:@"microPhone_top"]; 35 | topImageView.center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); 36 | [self addSubview:topImageView]; 37 | 38 | _microPhoneMaskLayer = [CAShapeLayer layer]; 39 | _microPhoneMaskLayer.position = CGPointMake(topImageView.bounds.size.width / 2, 3 * topImageView.bounds.size.height / 2); 40 | _microPhoneMaskLayer.bounds = topImageView.bounds; 41 | _microPhoneMaskLayer.backgroundColor = [UIColor yellowColor].CGColor; 42 | 43 | topImageView.layer.mask = _microPhoneMaskLayer; 44 | 45 | [self setMicroPhoneTimer]; 46 | } 47 | 48 | ///定时 49 | - (void)setMicroPhoneTimer 50 | { 51 | [self closeMicroPhoneTimer]; 52 | 53 | NSInteger mode = 1; 54 | NSDictionary *dic = @{@"mode":[NSNumber numberWithInteger:mode]}; 55 | _microphoneTimer = [NSTimer timerWithTimeInterval:0.3 target:self selector:@selector(addMicroPhoneAnimation) userInfo:dic repeats:YES]; 56 | [[NSRunLoop currentRunLoop] addTimer:_microphoneTimer forMode:NSDefaultRunLoopMode]; 57 | } 58 | 59 | ///模拟声音改变效果 60 | - (void)addMicroPhoneAnimation 61 | { 62 | CGPoint beginPoint = _microPhoneMaskLayer.position; 63 | CGFloat layerHeight = _microPhoneMaskLayer.bounds.size.height; 64 | CGFloat height = arc4random() % (NSUInteger)layerHeight; 65 | CGPoint endPoint = CGPointMake(_microPhoneMaskLayer.position.x, 3 * layerHeight / 2 - height); 66 | _microPhoneMaskLayer.position = endPoint; 67 | 68 | CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; 69 | positionAnimation.fromValue = [NSValue valueWithCGPoint:beginPoint]; 70 | positionAnimation.toValue = [NSValue valueWithCGPoint:endPoint]; 71 | positionAnimation.duration = 0.3; 72 | 73 | [_microPhoneMaskLayer addAnimation:positionAnimation forKey:@""]; 74 | } 75 | 76 | - (void)closeMicroPhoneTimer 77 | { 78 | if (_microphoneTimer) { 79 | if ([_microphoneTimer isValid]) { 80 | [_microphoneTimer invalidate]; 81 | } 82 | _microphoneTimer = nil; 83 | } 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /YSCAnimation/replicator/YSCCircleRippleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleRippleView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCCircleRippleView : UIView 12 | 13 | - (void)startAnimation; 14 | 15 | - (void)stopAnimation; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /YSCAnimation/replicator/YSCCircleRippleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleRippleView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCCircleRippleView.h" 10 | 11 | @interface YSCCircleRippleView () 12 | 13 | @property (nonatomic, strong) CAShapeLayer *circleShapeLayer; 14 | 15 | @end 16 | 17 | @implementation YSCCircleRippleView 18 | 19 | - (instancetype)initWithFrame:(CGRect)frame 20 | { 21 | self = [super initWithFrame:frame]; 22 | if (self) { 23 | [self setUpLayers]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)setUpLayers 29 | { 30 | CGFloat width = self.bounds.size.width; 31 | 32 | self.circleShapeLayer = [CAShapeLayer layer]; 33 | _circleShapeLayer.bounds = CGRectMake(0, 0, width, width); 34 | _circleShapeLayer.position = CGPointMake(width / 2.0, width / 2.0); 35 | _circleShapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, width, width)].CGPath; 36 | _circleShapeLayer.fillColor = [UIColor redColor].CGColor; 37 | _circleShapeLayer.opacity = 0.0; 38 | 39 | CAReplicatorLayer *replicator = [CAReplicatorLayer layer]; 40 | replicator.bounds = CGRectMake(0, 0, width, width); 41 | replicator.position = CGPointMake(width / 2.0, width / 2.0); 42 | replicator.instanceDelay = 0.5; 43 | replicator.instanceCount = 8; 44 | 45 | [replicator addSublayer:_circleShapeLayer]; 46 | [self.layer addSublayer:replicator]; 47 | } 48 | 49 | - (void)startAnimation 50 | { 51 | CABasicAnimation *alphaAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 52 | alphaAnim.fromValue = [NSNumber numberWithFloat:0.6]; 53 | alphaAnim.toValue = [NSNumber numberWithFloat:0.0]; 54 | 55 | CABasicAnimation *scaleAnim =[CABasicAnimation animationWithKeyPath:@"transform"]; 56 | CATransform3D t = CATransform3DIdentity; 57 | CATransform3D t2 = CATransform3DScale(t, 0.0, 0.0, 0.0); 58 | scaleAnim.fromValue = [NSValue valueWithCATransform3D:t2]; 59 | CATransform3D t3 = CATransform3DScale(t, 1.0, 1.0, 0.0); 60 | scaleAnim.toValue = [NSValue valueWithCATransform3D:t3]; 61 | 62 | CAAnimationGroup *groupAnimation = [CAAnimationGroup animation]; 63 | groupAnimation.animations = @[alphaAnim, scaleAnim]; 64 | groupAnimation.duration = 4.0; 65 | groupAnimation.autoreverses = NO; 66 | groupAnimation.repeatCount = HUGE; 67 | 68 | [_circleShapeLayer addAnimation:groupAnimation forKey:nil]; 69 | } 70 | 71 | - (void)stopAnimation 72 | { 73 | [_circleShapeLayer removeAllAnimations]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /YSCAnimation/replicator/YSCMatrixCircleAnimationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMatrixCircleAnimationView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCMatrixCircleAnimationView : UIView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame xNum:(NSInteger)xNum yNum:(NSInteger)yNum; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /YSCAnimation/replicator/YSCMatrixCircleAnimationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMatrixCircleAnimationView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCMatrixCircleAnimationView.h" 10 | 11 | @implementation YSCMatrixCircleAnimationView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame xNum:(NSInteger)xNum yNum:(NSInteger)yNum 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | [self setUpLayersWithXNum:xNum yNum:yNum]; 18 | } 19 | return self; 20 | } 21 | 22 | - (void)setUpLayersWithXNum:(NSInteger)xNum yNum:(NSInteger)yNum 23 | { 24 | CGFloat circleMargin = 5.0; 25 | CGFloat width = self.bounds.size.width; 26 | CGFloat circleWidth = (width - circleMargin * (xNum - 1)) / xNum;//(width - (circleMargin * xNum - 1)) / xNum; 27 | 28 | CAShapeLayer *circleShapeLayer = [CAShapeLayer layer]; 29 | circleShapeLayer.bounds = CGRectMake(0, 0, circleWidth, circleWidth); 30 | circleShapeLayer.position = CGPointMake(circleWidth / 2.0, circleWidth / 2.0); 31 | circleShapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleWidth, circleWidth)].CGPath; 32 | circleShapeLayer.fillColor = [UIColor redColor].CGColor; 33 | 34 | CAReplicatorLayer *xReplicator = [CAReplicatorLayer layer]; 35 | xReplicator.bounds = CGRectMake(0, 0, width, circleWidth); 36 | xReplicator.position = CGPointMake(width / 2.0, circleWidth / 2.0); 37 | xReplicator.instanceDelay = 0.3;//延时0.3秒执行动画放大缩小,颜色渐变动画 38 | xReplicator.instanceCount = xNum;//拷贝xNum个副本 39 | 40 | CATransform3D transform = CATransform3DIdentity; 41 | //每个副本向右平移circleWidth + circleMargin 42 | transform = CATransform3DTranslate(transform, circleWidth + circleMargin, 0, 0.0); 43 | xReplicator.instanceTransform = transform; 44 | //transform = CATransform3DScale(transform, 1, -1, 0); 45 | 46 | CAReplicatorLayer *yReplicator = [CAReplicatorLayer layer]; 47 | yReplicator.bounds = CGRectMake(0, 0, width, width); 48 | yReplicator.position = CGPointMake(width / 2.0, width / 2.0); 49 | yReplicator.instanceDelay = 0.3;//延时0.3秒执行动画放大缩小,颜色渐变动画 50 | yReplicator.instanceCount = yNum;//拷贝yNum个副本 51 | 52 | CATransform3D transformY = CATransform3DIdentity; 53 | //每个副本向下平移circleWidth + circleMargin 54 | transformY = CATransform3DTranslate(transformY, 0, circleWidth + circleMargin, 0.0); 55 | yReplicator.instanceTransform = transformY; 56 | 57 | [xReplicator addSublayer:circleShapeLayer]; 58 | //将xReplicator添加到yReplicator上各自的副本偏移后就形成了一个矩阵 59 | [yReplicator addSublayer:xReplicator]; 60 | [self.layer addSublayer:yReplicator]; 61 | /* 62 | //或者 63 | [yReplicator addSublayer:circleShapeLayer]; 64 | [xReplicator addSublayer:yReplicator]; 65 | [self.layer addSublayer:xReplicator]; 66 | */ 67 | 68 | [self addAnimationToLayer:circleShapeLayer]; 69 | } 70 | 71 | - (void)addAnimationToLayer:(CAShapeLayer *)layer 72 | { 73 | CABasicAnimation *alphaAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; 74 | alphaAnim.fromValue = [NSNumber numberWithFloat:1.0]; 75 | alphaAnim.toValue = [NSNumber numberWithFloat:0.3]; 76 | 77 | CABasicAnimation *scaleAnim =[CABasicAnimation animationWithKeyPath:@"transform"]; 78 | CATransform3D t = CATransform3DIdentity; 79 | CATransform3D t2 = CATransform3DScale(t, 1.0, 1.0, 0.0); 80 | scaleAnim.fromValue = [NSValue valueWithCATransform3D:t2]; 81 | CATransform3D t3 = CATransform3DScale(t, 0.2, 0.2, 0.0); 82 | scaleAnim.toValue = [NSValue valueWithCATransform3D:t3]; 83 | 84 | CAAnimationGroup *groupAnimation = [CAAnimationGroup animation]; 85 | groupAnimation.animations = @[alphaAnim, scaleAnim]; 86 | groupAnimation.duration = 1.0; 87 | groupAnimation.autoreverses = YES; 88 | groupAnimation.repeatCount = HUGE; 89 | 90 | [layer addAnimation:groupAnimation forKey:nil]; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /YSCAnimation/ripple/YSCRippleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCRippleView.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/2/17. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, YSCRippleType) { 12 | YSCRippleTypeLine, 13 | YSCRippleTypeRing, 14 | YSCRippleTypeCircle, 15 | YSCRippleTypeMixed, 16 | }; 17 | 18 | @interface YSCRippleView : UIView 19 | 20 | /** 21 | * show ripple view 22 | * 23 | * @param type ripple type 24 | */ 25 | - (void)showWithRippleType:(YSCRippleType)type; 26 | 27 | /** 28 | * remove ripple view 29 | */ 30 | - (void)removeFromParentView; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /YSCAnimation/ripple/YSCRippleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCRippleView.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/2/17. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCRippleView.h" 10 | 11 | @interface YSCRippleView () 12 | 13 | @property (nonatomic, strong) UIButton *rippleButton; 14 | @property (nonatomic, strong) NSTimer *rippleTimer; 15 | @property (nonatomic, assign) NSInteger mode; 16 | @property (nonatomic, assign) YSCRippleType type; 17 | 18 | @end 19 | 20 | @implementation YSCRippleView 21 | 22 | - (void)removeFromParentView 23 | { 24 | if (self.superview) { 25 | [_rippleButton removeFromSuperview]; 26 | [self closeRippleTimer]; 27 | [self removeAllSubLayers]; 28 | [self removeFromSuperview]; 29 | [self.layer removeAllAnimations]; 30 | } 31 | } 32 | 33 | - (void)removeAllSubLayers 34 | { 35 | for (NSInteger i = 0; [self.layer sublayers].count > 0; i++) { 36 | [[[self.layer sublayers] firstObject] removeFromSuperlayer]; 37 | } 38 | } 39 | 40 | - (void)showWithRippleType:(YSCRippleType)type 41 | { 42 | _type = type; 43 | [self setUpRippleButton]; 44 | 45 | self.rippleTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(addRippleLayer) userInfo:nil repeats:YES]; 46 | [[NSRunLoop currentRunLoop] addTimer:_rippleTimer forMode:NSRunLoopCommonModes]; 47 | } 48 | 49 | - (void)setUpRippleButton 50 | { 51 | _rippleButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; 52 | _rippleButton.center = self.center; 53 | _rippleButton.layer.backgroundColor = [UIColor blueColor].CGColor; 54 | _rippleButton.layer.cornerRadius = 25; 55 | _rippleButton.layer.masksToBounds = YES; 56 | _rippleButton.layer.borderColor = [UIColor yellowColor].CGColor; 57 | _rippleButton.layer.borderWidth = 2; 58 | [_rippleButton addTarget:self action:@selector(rippleButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 59 | 60 | [self addSubview:_rippleButton]; 61 | } 62 | 63 | - (void)rippleButtonTouched:(id)sender 64 | { 65 | [self closeRippleTimer]; 66 | [self addRippleLayer]; 67 | } 68 | 69 | - (CGRect)makeEndRect 70 | { 71 | CGRect endRect = CGRectMake(_rippleButton.frame.origin.x, _rippleButton.frame.origin.y, 50, 50); 72 | endRect = CGRectInset(endRect, -300, -300); 73 | return endRect; 74 | } 75 | 76 | - (void)addRippleLayer 77 | { 78 | CAShapeLayer *rippleLayer = [[CAShapeLayer alloc] init]; 79 | rippleLayer.position = CGPointMake(200, 200); 80 | rippleLayer.bounds = CGRectMake(0, 0, 400, 400); 81 | rippleLayer.backgroundColor = [UIColor clearColor].CGColor; 82 | 83 | UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_rippleButton.frame.origin.x, _rippleButton.frame.origin.y, 50, 50)]; 84 | rippleLayer.path = path.CGPath; 85 | rippleLayer.strokeColor = [UIColor greenColor].CGColor; 86 | if (YSCRippleTypeRing == _type) { 87 | rippleLayer.lineWidth = 5; 88 | } else { 89 | rippleLayer.lineWidth = 1.5; 90 | } 91 | 92 | if (YSCRippleTypeLine == _type || YSCRippleTypeRing == _type) { 93 | rippleLayer.fillColor = [UIColor clearColor].CGColor; 94 | } else if (YSCRippleTypeCircle == _type) { 95 | rippleLayer.fillColor = [UIColor greenColor].CGColor; 96 | } else if (YSCRippleTypeMixed == _type) { 97 | rippleLayer.fillColor = [UIColor grayColor].CGColor; 98 | } 99 | 100 | [self.layer insertSublayer:rippleLayer below:_rippleButton.layer]; 101 | 102 | //addRippleAnimation 103 | UIBezierPath *beginPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_rippleButton.frame.origin.x, _rippleButton.frame.origin.y, 50, 50)]; 104 | CGRect endRect = CGRectInset([self makeEndRect], -100, -100); 105 | UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:endRect]; 106 | 107 | rippleLayer.path = endPath.CGPath; 108 | rippleLayer.opacity = 0.0; 109 | 110 | CABasicAnimation *rippleAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 111 | rippleAnimation.fromValue = (__bridge id _Nullable)(beginPath.CGPath); 112 | rippleAnimation.toValue = (__bridge id _Nullable)(endPath.CGPath); 113 | rippleAnimation.duration = 5.0; 114 | 115 | CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 116 | opacityAnimation.fromValue = [NSNumber numberWithFloat:0.6]; 117 | opacityAnimation.toValue = [NSNumber numberWithFloat:0.0]; 118 | opacityAnimation.duration = 5.0; 119 | 120 | [rippleLayer addAnimation:opacityAnimation forKey:@""]; 121 | [rippleLayer addAnimation:rippleAnimation forKey:@""]; 122 | 123 | [self performSelector:@selector(removeRippleLayer:) withObject:rippleLayer afterDelay:5.0]; 124 | } 125 | 126 | - (void)removeRippleLayer:(CAShapeLayer *)rippleLayer 127 | { 128 | [rippleLayer removeFromSuperlayer]; 129 | rippleLayer = nil; 130 | } 131 | 132 | - (void)closeRippleTimer 133 | { 134 | if (_rippleTimer) { 135 | if ([_rippleTimer isValid]) { 136 | [_rippleTimer invalidate]; 137 | } 138 | _rippleTimer = nil; 139 | } 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /YSCAnimation/seaWaterWave/Shaders/SeaFragment.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform highp vec2 iResolution; 3 | uniform highp float iGlobalTime; 4 | 5 | const int NUM_STEPS = 8; 6 | const float PI = 3.1415; 7 | const float EPSILON = 1e-3; 8 | float EPSILON_NRM = 0.1 / iResolution.x; 9 | 10 | // sea 11 | const int ITER_GEOMETRY = 3; 12 | const int ITER_FRAGMENT = 5; 13 | const float SEA_HEIGHT = 0.6; 14 | const float SEA_CHOPPY = 4.0; 15 | const float SEA_SPEED = 0.8; 16 | const float SEA_FREQ = 0.16; 17 | const vec3 SEA_BASE = vec3(0.1,0.19,0.22); 18 | const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6); 19 | float SEA_TIME = iGlobalTime * SEA_SPEED; 20 | mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); 21 | 22 | // math 23 | mat3 fromEuler(vec3 ang) { 24 | vec2 a1 = vec2(sin(ang.x),cos(ang.x)); 25 | vec2 a2 = vec2(sin(ang.y),cos(ang.y)); 26 | vec2 a3 = vec2(sin(ang.z),cos(ang.z)); 27 | mat3 m; 28 | m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x); 29 | m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x); 30 | m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y); 31 | return m; 32 | } 33 | float hash( vec2 p ) { 34 | float h = dot(p,vec2(127.1,311.7)); 35 | return fract(sin(h)*43758.5453123); 36 | } 37 | float noise( in vec2 p ) { 38 | vec2 i = floor( p ); 39 | vec2 f = fract( p ); 40 | vec2 u = f*f*(3.0-2.0*f); 41 | return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), 42 | hash( i + vec2(1.0,0.0) ), u.x), 43 | mix( hash( i + vec2(0.0,1.0) ), 44 | hash( i + vec2(1.0,1.0) ), u.x), u.y); 45 | } 46 | 47 | // lighting 48 | float diffuse(vec3 n,vec3 l,float p) { 49 | return pow(dot(n,l) * 0.4 + 0.6,p); 50 | } 51 | float specular(vec3 n,vec3 l,vec3 e,float s) { 52 | float nrm = (s + 8.0) / (3.1415 * 8.0); 53 | return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; 54 | } 55 | 56 | // sky 57 | vec3 getSkyColor(vec3 e) { 58 | e.y = max(e.y,0.0); 59 | vec3 ret; 60 | ret.x = pow(1.0-e.y,2.0); 61 | ret.y = 1.0-e.y; 62 | ret.z = 0.6+(1.0-e.y)*0.4; 63 | return ret; 64 | } 65 | 66 | // sea 67 | float sea_octave(vec2 uv, float choppy) { 68 | uv += noise(uv); 69 | vec2 wv = 1.0-abs(sin(uv)); 70 | vec2 swv = abs(cos(uv)); 71 | wv = mix(wv,swv,wv); 72 | return pow(1.0-pow(wv.x * wv.y,0.65),choppy); 73 | } 74 | 75 | float map(vec3 p) { 76 | float freq = SEA_FREQ; 77 | float amp = SEA_HEIGHT; 78 | float choppy = SEA_CHOPPY; 79 | vec2 uv = p.xz; uv.x *= 0.75; 80 | 81 | float d = 0.0; 82 | float h = 0.0; 83 | for(int i = 0; i < ITER_GEOMETRY; i++) { 84 | d = sea_octave((uv+SEA_TIME)*freq,choppy); 85 | d += sea_octave((uv-SEA_TIME)*freq,choppy); 86 | h += d * amp; 87 | uv *= octave_m; freq *= 1.9; amp *= 0.22; 88 | choppy = mix(choppy,1.0,0.2); 89 | } 90 | return p.y - h; 91 | } 92 | 93 | float map_detailed(vec3 p) { 94 | float freq = SEA_FREQ; 95 | float amp = SEA_HEIGHT; 96 | float choppy = SEA_CHOPPY; 97 | vec2 uv = p.xz; uv.x *= 0.75; 98 | 99 | float d = 0.0; 100 | float h = 0.0; 101 | for(int i = 0; i < ITER_FRAGMENT; i++) { 102 | d = sea_octave((uv+SEA_TIME)*freq,choppy); 103 | d += sea_octave((uv-SEA_TIME)*freq,choppy); 104 | h += d * amp; 105 | uv *= octave_m; freq *= 1.9; amp *= 0.22; 106 | choppy = mix(choppy,1.0,0.2); 107 | } 108 | return p.y - h; 109 | } 110 | 111 | vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) { 112 | float fresnel = 1.0 - max(dot(n,-eye),0.0); 113 | fresnel = pow(fresnel,3.0) * 0.65; 114 | 115 | vec3 reflected = getSkyColor(reflect(eye,n)); 116 | vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; 117 | 118 | vec3 color = mix(refracted,reflected,fresnel); 119 | 120 | float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); 121 | color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten; 122 | 123 | color += vec3(specular(n,l,eye,60.0)); 124 | 125 | return color; 126 | } 127 | 128 | // tracing 129 | vec3 getNormal(vec3 p, float eps) { 130 | vec3 n; 131 | n.y = map_detailed(p); 132 | n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y; 133 | n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y; 134 | n.y = eps; 135 | return normalize(n); 136 | } 137 | 138 | float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) { 139 | float tm = 0.0; 140 | float tx = 1000.0; 141 | float hx = map(ori + dir * tx); 142 | if(hx > 0.0) return tx; 143 | float hm = map(ori + dir * tm); 144 | float tmid = 0.0; 145 | for(int i = 0; i < NUM_STEPS; i++) { 146 | tmid = mix(tm,tx, hm/(hm-hx)); 147 | p = ori + dir * tmid; 148 | float hmid = map(p); 149 | if(hmid < 0.0) { 150 | tx = tmid; 151 | hx = hmid; 152 | } else { 153 | tm = tmid; 154 | hm = hmid; 155 | } 156 | } 157 | return tmid; 158 | } 159 | 160 | // main 161 | void main() { 162 | vec2 uv = gl_FragCoord.xy / iResolution.xy; 163 | uv = uv * 2.0 - 1.0; 164 | uv.x *= iResolution.x / iResolution.y; 165 | //float time = iGlobalTime * 0.3 + iMouse.x*0.01; 166 | float time = iGlobalTime * 0.3 + 1.0; 167 | // ray 168 | vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time); 169 | vec3 ori = vec3(0.0,3.5,time*5.0); 170 | vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.15; 171 | dir = normalize(dir) * fromEuler(ang); 172 | 173 | // tracing 174 | vec3 p; 175 | heightMapTracing(ori,dir,p); 176 | vec3 dist = p - ori; 177 | vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM); 178 | vec3 light = normalize(vec3(0.0,1.0,0.8)); 179 | 180 | // color 181 | vec3 color = mix( 182 | getSkyColor(dir), 183 | getSeaColor(p,n,light,dir,dist), 184 | pow(smoothstep(0.0,-0.05,dir.y),0.3)); 185 | 186 | // post 187 | gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0); 188 | } 189 | -------------------------------------------------------------------------------- /YSCAnimation/seaWaterWave/Shaders/SeaVertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec4 Position; 2 | 3 | void main() 4 | { 5 | gl_Position = Position; 6 | } -------------------------------------------------------------------------------- /YSCAnimation/seaWaterWave/YSCSeaGLView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCSeaGLView.h 3 | // 4 | // 5 | // Created by yushichao on 16/3/18. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #include 12 | #include 13 | 14 | @interface YSCSeaGLView : UIView { 15 | CAEAGLLayer* _eaglLayer; 16 | EAGLContext* _context; 17 | GLuint _colorRenderBuffer; 18 | GLuint _positionSlot; 19 | GLuint _colorSlot; 20 | GLuint _projectionUniform; 21 | GLuint _modelViewUniform; 22 | float _currentRotation; 23 | GLuint _depthRenderBuffer; 24 | 25 | GLuint _floorTexture; 26 | GLuint _fishTexture; 27 | GLuint _texCoordSlot; 28 | GLuint _textureUniform; 29 | GLuint _textureUniform2; 30 | GLuint _vertexBuffer; 31 | GLuint _indexBuffer; 32 | 33 | GLuint _iResolutionUniform; 34 | GLuint _iGlobalTimeUniform; 35 | } 36 | 37 | - (void)removeFromParent; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /YSCAnimation/seaWaterWave/YSCSeaGLView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCSeaGLView.m 3 | // 4 | // 5 | // Created by yushichao on 16/3/18. 6 | // 7 | // 8 | 9 | #import "YSCSeaGLView.h" 10 | 11 | @interface YSCSeaGLView () 12 | 13 | @property (nonatomic, strong) CADisplayLink *displayLink; 14 | 15 | @end 16 | 17 | @implementation YSCSeaGLView 18 | 19 | typedef struct { 20 | float Position[3]; 21 | float Color[4]; 22 | float TexCoord[2]; // New 23 | } Vertex; 24 | 25 | #define TEX_COORD_MAX 1 26 | 27 | const Vertex Vertices__[] = { 28 | // Front 29 | {{1, -1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}}, 30 | {{1, 1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}}, 31 | {{-1, 1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}}, 32 | {{-1, -1, 0}, {0, 0, 0, 1}, {0, 0}}, 33 | }; 34 | 35 | const GLubyte Indices__[] = { 36 | // Front 37 | 0, 1, 2, 38 | 2, 3, 0, 39 | }; 40 | 41 | + (Class)layerClass 42 | { 43 | return [CAEAGLLayer class]; 44 | } 45 | 46 | - (void)setupLayer 47 | { 48 | _eaglLayer = (CAEAGLLayer*) self.layer; 49 | _eaglLayer.opaque = YES; 50 | } 51 | 52 | - (void)setupContext 53 | { 54 | EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2; 55 | _context = [[EAGLContext alloc] initWithAPI:api]; 56 | if (!_context) { 57 | NSLog(@"Failed to initialize OpenGLES 2.0 context"); 58 | exit(1); 59 | } 60 | 61 | if (![EAGLContext setCurrentContext:_context]) { 62 | NSLog(@"Failed to set current OpenGL context"); 63 | exit(1); 64 | } 65 | } 66 | 67 | - (void)setupRenderBuffer 68 | { 69 | glGenRenderbuffers(1, &_colorRenderBuffer); 70 | glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer); 71 | [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer]; 72 | } 73 | 74 | - (void)setupDepthBuffer 75 | { 76 | glGenRenderbuffers(1, &_depthRenderBuffer); 77 | glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer); 78 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.frame.size.width, self.frame.size.height); 79 | } 80 | 81 | - (void)setupFrameBuffer 82 | { 83 | GLuint framebuffer; 84 | glGenFramebuffers(1, &framebuffer); 85 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); 86 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer); 87 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer); 88 | } 89 | 90 | - (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType 91 | { 92 | // 1 93 | NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:@"glsl"]; 94 | NSError* error; 95 | NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error]; 96 | if (!shaderString) { 97 | NSLog(@"Error loading shader: %@", error.localizedDescription); 98 | exit(1); 99 | } 100 | 101 | // 2 102 | GLuint shaderHandle = glCreateShader(shaderType); 103 | 104 | // 3 105 | const char * shaderStringUTF8 = [shaderString UTF8String]; 106 | int shaderStringLength = [shaderString length]; 107 | glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength); 108 | 109 | // 4 110 | glCompileShader(shaderHandle); 111 | 112 | // 5 113 | GLint compileSuccess; 114 | glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess); 115 | if (compileSuccess == GL_FALSE) { 116 | GLchar messages[256]; 117 | glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]); 118 | NSString *messageString = [NSString stringWithUTF8String:messages]; 119 | NSLog(@"%@", messageString); 120 | exit(1); 121 | } 122 | 123 | return shaderHandle; 124 | } 125 | 126 | - (void)compileShaders 127 | { 128 | // 1 129 | GLuint vertexShader = [self compileShader:@"SeaVertex" withType:GL_VERTEX_SHADER]; 130 | GLuint fragmentShader = [self compileShader:@"SeaFragment" withType:GL_FRAGMENT_SHADER]; 131 | 132 | // 2 133 | GLuint programHandle = glCreateProgram(); 134 | glAttachShader(programHandle, vertexShader); 135 | glAttachShader(programHandle, fragmentShader); 136 | glLinkProgram(programHandle); 137 | 138 | // 3 139 | GLint linkSuccess; 140 | glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess); 141 | if (linkSuccess == GL_FALSE) { 142 | GLchar messages[256]; 143 | glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]); 144 | NSString *messageString = [NSString stringWithUTF8String:messages]; 145 | NSLog(@"%@", messageString); 146 | exit(1); 147 | } 148 | 149 | // 4 150 | glUseProgram(programHandle); 151 | 152 | // 5 153 | _positionSlot = glGetAttribLocation(programHandle, "Position"); 154 | glEnableVertexAttribArray(_positionSlot); 155 | 156 | _iResolutionUniform = glGetUniformLocation(programHandle, "iResolution"); 157 | _iGlobalTimeUniform = glGetUniformLocation(programHandle, "iGlobalTime"); 158 | } 159 | 160 | - (void)setupVBOs 161 | { 162 | glGenBuffers(1, &_vertexBuffer); 163 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 164 | glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices__), Vertices__, GL_STATIC_DRAW); 165 | 166 | glGenBuffers(1, &_indexBuffer); 167 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 168 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices__), Indices__, GL_STATIC_DRAW); 169 | } 170 | 171 | - (void)render:(CADisplayLink*)displayLink 172 | { 173 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 174 | glEnable(GL_BLEND); 175 | 176 | glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0); 177 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 178 | glEnable(GL_DEPTH_TEST); 179 | 180 | // 1 181 | glViewport(-[UIScreen mainScreen].bounds.size.width, -[UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width * 2, [UIScreen mainScreen].bounds.size.height * 2); 182 | 183 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); 184 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer); 185 | 186 | // 2 187 | glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0); 188 | // glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3)); 189 | 190 | glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 7)); 191 | 192 | GLfloat sizeArray[2]; 193 | sizeArray[0] = [UIScreen mainScreen].bounds.size.width; 194 | sizeArray[1] = [UIScreen mainScreen].bounds.size.height; 195 | glUniform2fv(_iResolutionUniform, 1, sizeArray); 196 | 197 | static float globleTime = 0.0; 198 | globleTime = globleTime + 0.1; 199 | glUniform1f(_iGlobalTimeUniform, globleTime); 200 | 201 | // glActiveTexture(GL_TEXTURE0); 202 | // glBindTexture(GL_TEXTURE_2D, _floorTexture); 203 | // glUniform1i(_textureUniform, 0); 204 | // 205 | // glActiveTexture(GL_TEXTURE1); // unneccc in practice 206 | // glBindTexture(GL_TEXTURE_2D, _fishTexture); 207 | // glUniform1i(_textureUniform2, 1); // unnecc in practice 208 | 209 | // 3 210 | glDrawElements(GL_TRIANGLES, sizeof(Indices__)/sizeof(Indices__[0]), GL_UNSIGNED_BYTE, 0); 211 | 212 | [_context presentRenderbuffer:GL_RENDERBUFFER]; 213 | } 214 | 215 | - (void)setupDisplayLink 216 | { 217 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)]; 218 | [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 219 | } 220 | 221 | - (GLuint)setupTexture:(NSString *)fileName 222 | { 223 | // 1 224 | CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage; 225 | if (!spriteImage) { 226 | NSLog(@"Failed to load image %@", fileName); 227 | exit(1); 228 | } 229 | 230 | // 2 231 | size_t width = CGImageGetWidth(spriteImage); 232 | size_t height = CGImageGetHeight(spriteImage); 233 | 234 | GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte)); 235 | 236 | CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); 237 | 238 | CGContextTranslateCTM(spriteContext, 0, height); 239 | CGContextScaleCTM(spriteContext, 1.0, -1.0); 240 | // 3 241 | CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage); 242 | 243 | CGContextRelease(spriteContext); 244 | 245 | GLubyte *imageData = NULL; 246 | CFDataRef dataFromImageDataProvider = NULL; 247 | dataFromImageDataProvider = CGDataProviderCopyData(CGImageGetDataProvider(spriteImage)); 248 | imageData = (GLubyte *)CFDataGetBytePtr(dataFromImageDataProvider); 249 | 250 | // 4 251 | GLuint texName; 252 | glGenTextures(1, &texName); 253 | glBindTexture(GL_TEXTURE_2D, texName); 254 | 255 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 256 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 257 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);//不加出现灰色斑点 258 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 259 | 260 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); 261 | 262 | free(spriteData); 263 | return texName; 264 | } 265 | 266 | - (id)initWithFrame:(CGRect)frame 267 | { 268 | self = [super initWithFrame:frame]; 269 | if (self) { 270 | [self setupLayer]; 271 | [self setupContext]; 272 | [self setupDepthBuffer]; 273 | [self setupRenderBuffer]; 274 | [self setupFrameBuffer]; 275 | [self compileShaders]; 276 | [self setupVBOs]; 277 | [self setupDisplayLink]; 278 | // _floorTexture = [self setupTexture:@"20140309qc01.jpg"]; 279 | // _fishTexture = [self setupTexture:@"kelvinMap.png"]; 280 | } 281 | return self; 282 | } 283 | 284 | - (void)removeFromParent 285 | { 286 | [_displayLink invalidate]; 287 | _displayLink = nil; 288 | _context = nil; 289 | } 290 | 291 | - (void)dealloc 292 | { 293 | [_displayLink invalidate]; 294 | _displayLink = nil; 295 | _context = nil; 296 | } 297 | @end 298 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/CurveView/YSCTableRefreshCurveLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshCurveLayer.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface YSCTableRefreshCurveLayer : CAShapeLayer 13 | 14 | /** 15 | * CurveLayer的进度 0~1 16 | */ 17 | @property(nonatomic,assign)CGFloat progress; 18 | 19 | - (void)showInRotateLinePath; 20 | - (void)resetData; 21 | @end 22 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/CurveView/YSCTableRefreshCurveLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshCurveLayer.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #define Radius 10 10 | #define Space 1 11 | #define LineLength 30 12 | #define CenterY self.frame.size.height/2 13 | 14 | #define Degree M_PI/3 15 | 16 | #import "YSCTableRefreshCurveLayer.h" 17 | 18 | @interface YSCTableRefreshCurveLayer () 19 | 20 | @property (nonatomic, strong) UIBezierPath *linePath; 21 | @property (nonatomic, strong) UIBezierPath *rotateLintPath; 22 | 23 | @end 24 | 25 | @implementation YSCTableRefreshCurveLayer 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | if (self) { 31 | self.backgroundColor = [UIColor clearColor].CGColor; 32 | self.strokeColor = [YSCGlobleMethod uiColorFromHexString:@"3c76ff"].CGColor; 33 | self.fillColor = [UIColor clearColor].CGColor; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)resetData 39 | { 40 | self.strokeStart = 0.0; 41 | self.strokeEnd = 0.0; 42 | } 43 | 44 | - (void)setProgress:(CGFloat)progress 45 | { 46 | self.path = self.linePath.CGPath; 47 | self.strokeStart = 0.0; 48 | self.strokeEnd = progress <= 1.0 ? progress : 1.0; 49 | } 50 | 51 | - (void)showInRotateLinePath 52 | { 53 | self.path = self.rotateLintPath.CGPath; 54 | self.strokeStart = 0.0; 55 | self.strokeEnd = 1.0; 56 | } 57 | 58 | - (UIBezierPath *)linePath 59 | { 60 | if (!_linePath) { 61 | UIBezierPath *path = [UIBezierPath bezierPath]; 62 | [path addArcWithCenter:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0) radius:9 startAngle:0.32*M_PI endAngle:2.18 * M_PI clockwise:YES]; 63 | CGPoint endPoint = CGPointMake(path.currentPoint.x + 4, path.currentPoint.y + 4); 64 | [path addLineToPoint:endPoint]; 65 | _linePath = path; 66 | } 67 | 68 | return _linePath; 69 | } 70 | 71 | - (UIBezierPath *)rotateLintPath 72 | { 73 | if (!_rotateLintPath) { 74 | UIBezierPath *path = [UIBezierPath bezierPath]; 75 | [path addArcWithCenter:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0) radius:9 startAngle:0.32*M_PI endAngle:2.18 * M_PI clockwise:YES]; 76 | [path addLineToPoint:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0)]; 77 | _rotateLintPath = path; 78 | } 79 | 80 | return _rotateLintPath; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/CurveView/YSCTableRefreshCurveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshCurveView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCTableRefreshCurveView : UIView 12 | 13 | /** 14 | * CurveView的进度 0~1 15 | */ 16 | @property(nonatomic,assign)CGFloat progress; 17 | 18 | - (void)showInRotateLinePath; 19 | - (void)resetData; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/CurveView/YSCTableRefreshCurveView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshCurveView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import "YSCTableRefreshCurveView.h" 10 | #import "YSCTableRefreshCurveLayer.h" 11 | 12 | @interface YSCTableRefreshCurveView() 13 | 14 | @property (nonatomic,strong)YSCTableRefreshCurveLayer *curveLayer; 15 | 16 | @end 17 | 18 | @implementation YSCTableRefreshCurveView 19 | 20 | 21 | + (Class)layerClass 22 | { 23 | return [YSCTableRefreshCurveLayer class]; 24 | } 25 | 26 | -(id)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | } 31 | return self; 32 | } 33 | 34 | - (void)resetData 35 | { 36 | [self.curveLayer resetData]; 37 | } 38 | 39 | -(void)setProgress:(CGFloat)progress 40 | { 41 | self.curveLayer.progress = progress; 42 | [self.curveLayer setNeedsDisplay]; 43 | } 44 | 45 | - (void)showInRotateLinePath 46 | { 47 | [self.curveLayer showInRotateLinePath]; 48 | } 49 | 50 | -(void)willMoveToSuperview:(UIView *)newSuperview 51 | { 52 | self.curveLayer = [YSCTableRefreshCurveLayer layer]; 53 | self.curveLayer.frame = self.bounds; 54 | self.curveLayer.contentsScale = [UIScreen mainScreen].scale; 55 | self.curveLayer.progress = 0.0f; 56 | [self.curveLayer setNeedsDisplay]; 57 | [self.layer addSublayer:self.curveLayer]; 58 | 59 | } 60 | 61 | 62 | @end 63 | 64 | 65 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/LabelView/YSCTableRefreshLabelView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshLabelView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | UP, 13 | DOWN, 14 | } PULLINGSTATE; 15 | 16 | @interface YSCTableRefreshLabelView : UIView 17 | 18 | 19 | /** 20 | * LabelView的进度 0~1 21 | */ 22 | @property(nonatomic,assign) CGFloat progress; 23 | 24 | 25 | /** 26 | * 是否正在刷新 27 | */ 28 | @property(nonatomic,assign) BOOL loading; 29 | 30 | 31 | /** 32 | * 上拉还是下拉 33 | */ 34 | @property(nonatomic,assign) PULLINGSTATE state; 35 | 36 | - (CGFloat)titleWidth; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/LabelView/YSCTableRefreshLabelView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshLabelView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import "YSCTableRefreshLabelView.h" 10 | 11 | #define kPullingDownString @"下拉加载更多" 12 | #define kPullingUpString @"上拉加载更多" 13 | #define kReleaseString @"松开加载" 14 | #define kIsLoadingString @"正在加载" 15 | 16 | #define kPullingString self.state == UP ? kPullingUpString : kPullingDownString 17 | 18 | 19 | //#define self.state 20 | 21 | #define LabelHeight 50 22 | 23 | @interface YSCTableRefreshLabelView() 24 | 25 | 26 | @end 27 | 28 | @implementation YSCTableRefreshLabelView { 29 | UILabel *titleLabel; 30 | } 31 | 32 | -(id)initWithFrame:(CGRect)frame{ 33 | 34 | self = [super initWithFrame:frame]; 35 | if (self) { 36 | [self setUp]; 37 | } 38 | return self; 39 | } 40 | 41 | 42 | -(void)setUp 43 | { 44 | self.state = DOWN; 45 | titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, self.bounds.size.height/2-LabelHeight/2, self.bounds.size.width, LabelHeight)]; 46 | titleLabel.text = kPullingString; 47 | titleLabel.textColor = [YSCGlobleMethod uiColorFromHexString:@"666666"]; 48 | titleLabel.font = [UIFont systemFontOfSize:12.0]; 49 | titleLabel.adjustsFontSizeToFitWidth = YES; 50 | [self addSubview:titleLabel]; 51 | } 52 | 53 | - (void)setLoading:(BOOL)loading 54 | { 55 | _loading = loading; 56 | if (loading) { 57 | titleLabel.text = kIsLoadingString; 58 | } 59 | } 60 | 61 | -(void)setProgress:(CGFloat)progress 62 | { 63 | titleLabel.alpha = progress; 64 | 65 | if (!self.loading) { 66 | if (progress >= 1.0) { 67 | titleLabel.text = kReleaseString; 68 | }else{ 69 | titleLabel.text = kPullingString; 70 | } 71 | }else{ 72 | if (progress >= 0.91) { 73 | titleLabel.text = kReleaseString; 74 | }else{ 75 | titleLabel.text = kPullingString; 76 | } 77 | } 78 | 79 | } 80 | 81 | - (CGFloat)titleWidth 82 | { 83 | if (!titleLabel.text || 0 == self.frame.size.width || 0 == self.frame.size.height) { 84 | return 0.0; 85 | } 86 | NSDictionary *attributes = @{NSFontAttributeName :[UIFont systemFontOfSize:12]}; 87 | CGFloat width = [YSCGlobleMethod getStringWidth:titleLabel.text withRect:self.frame attributes:attributes]; 88 | 89 | return width; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/UITableViewController+YSCRefresh.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITableViewController+YSCRefresh.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/5. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YSCTableRefreshHeaderView.h" 11 | #import "YSCTableRefreshFooterView.h" 12 | 13 | @interface UITableViewController (YSCRefresh) 14 | 15 | //上/下拉刷新 16 | @property (nonatomic, assign) BOOL hasRefreshHeader; 17 | @property (nonatomic, assign) BOOL hasRefreshFooter; 18 | 19 | @property (nonatomic, copy) void(^heederRefreshingBlock)(void); 20 | @property (nonatomic, copy) void(^footerRefreshingBlock)(void); 21 | 22 | - (void)headerRefreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount; 23 | - (void)footerRefreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount; 24 | 25 | - (void)removeRefreshHeaderViewObservers; 26 | - (void)removeRefreshFooterViewObservers; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/UITableViewController+YSCRefresh.m: -------------------------------------------------------------------------------- 1 | // 2 | // MMSCTListViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/5. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import "UITableViewController+YSCRefresh.h" 10 | #import 11 | 12 | static void *hasRefreshHeaderKey = &hasRefreshHeaderKey; 13 | static void *hasRefreshFooterKey = &hasRefreshFooterKey; 14 | static void *refreshHeaderViewKey = &refreshHeaderViewKey; 15 | static void *refreshFooterViewKey = &refreshFooterViewKey; 16 | 17 | //@interface UITableViewController (YSCRefresh) 18 | 19 | //@property (nonatomic, strong) YSCTableRefreshHeaderView *refreshHeaderView; 20 | //@property (nonatomic, strong) YSCTableRefreshFooterView *refreshFooterView; 21 | 22 | //@end 23 | 24 | @implementation UITableViewController (YSCRefresh) 25 | 26 | #pragma mark - 上/下拉刷新 27 | 28 | //- (BOOL)isNavigationBarHidden 29 | //{ 30 | // return YES; 31 | //} 32 | 33 | - (void)setHasRefreshHeader:(BOOL)hasRefreshHeader 34 | { 35 | objc_setAssociatedObject(self, hasRefreshHeaderKey, @(hasRefreshHeader), OBJC_ASSOCIATION_RETAIN); 36 | 37 | YSCTableRefreshHeaderView *refreshHeaderView = objc_getAssociatedObject(self, refreshHeaderViewKey); 38 | if (hasRefreshHeader && !refreshHeaderView) { 39 | refreshHeaderView = [[YSCTableRefreshHeaderView alloc] initWithAssociatedScrollView:self.tableView withNavigationBar:!self.navigationController.isNavigationBarHidden]; 40 | objc_setAssociatedObject(self, refreshHeaderViewKey, refreshHeaderView, OBJC_ASSOCIATION_RETAIN); 41 | } else if (!hasRefreshHeader && refreshHeaderView.superview) { 42 | [self removeRefreshHeaderViewObservers]; 43 | [refreshHeaderView removeFromSuperview]; 44 | refreshHeaderView = nil; 45 | } 46 | } 47 | 48 | - (BOOL)hasRefreshHeader 49 | { 50 | BOOL hasRefreshHeader = [objc_getAssociatedObject(self, hasRefreshHeaderKey) boolValue]; 51 | return hasRefreshHeader; 52 | } 53 | 54 | - (void)setHeederRefreshingBlock:(void (^)(void))heederRefreshingBlock 55 | { 56 | YSCTableRefreshHeaderView *refreshHeaderView = objc_getAssociatedObject(self, refreshHeaderViewKey); 57 | [refreshHeaderView addRefreshingBlock:heederRefreshingBlock]; 58 | } 59 | 60 | - (void)headerRefreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount 61 | { 62 | YSCTableRefreshHeaderView *refreshHeaderView = objc_getAssociatedObject(self, refreshHeaderViewKey); 63 | [refreshHeaderView refreshFinished:refreshStatus refreshItemsCount:itemsCount]; 64 | } 65 | 66 | - (void)removeRefreshHeaderViewObservers 67 | { 68 | YSCTableRefreshHeaderView *refreshHeaderView = objc_getAssociatedObject(self, refreshHeaderViewKey); 69 | [refreshHeaderView removeObservers]; 70 | } 71 | 72 | - (void)setHasRefreshFooter:(BOOL)hasRefreshFooter 73 | { 74 | objc_setAssociatedObject(self, hasRefreshFooterKey, @(hasRefreshFooter), OBJC_ASSOCIATION_RETAIN); 75 | 76 | YSCTableRefreshFooterView *refreshFooterView = objc_getAssociatedObject(self, refreshFooterViewKey); 77 | if (hasRefreshFooter && !refreshFooterView) { 78 | refreshFooterView = [[YSCTableRefreshFooterView alloc] initWithAssociatedScrollView:self.tableView withNavigationBar:!self.navigationController.isNavigationBarHidden]; 79 | objc_setAssociatedObject(self, refreshFooterViewKey, refreshFooterView, OBJC_ASSOCIATION_RETAIN); 80 | } else if (!hasRefreshFooter && refreshFooterView.superview) { 81 | [self removeRefreshFooterViewObservers]; 82 | [refreshFooterView removeFromSuperview]; 83 | refreshFooterView = nil; 84 | } 85 | } 86 | 87 | - (BOOL)hasRefreshFooter 88 | { 89 | BOOL hasRefreshFooter = [objc_getAssociatedObject(self, hasRefreshFooterKey) boolValue]; 90 | return hasRefreshFooter; 91 | } 92 | 93 | - (void)setFooterRefreshingBlock:(void (^)(void))footerRefreshingBlock 94 | { 95 | YSCTableRefreshFooterView *refreshFooterView = objc_getAssociatedObject(self, refreshFooterViewKey); 96 | [refreshFooterView addRefreshingBlock:footerRefreshingBlock]; 97 | } 98 | 99 | - (void)footerRefreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount 100 | { 101 | YSCTableRefreshFooterView *refreshFooterView = objc_getAssociatedObject(self, refreshFooterViewKey); 102 | [refreshFooterView refreshFinished:refreshStatus refreshItemsCount:itemsCount]; 103 | } 104 | 105 | - (void)removeRefreshFooterViewObservers 106 | { 107 | YSCTableRefreshFooterView *refreshFooterView = objc_getAssociatedObject(self, refreshFooterViewKey); 108 | [refreshFooterView removeObservers]; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/YSCTableRefreshFooterView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshFooterView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YSCTableRefreshHeaderView.h" 11 | 12 | @interface YSCTableRefreshFooterView : UIView 13 | 14 | /** 15 | * 需要滑动多大距离才能松开 16 | */ 17 | @property(nonatomic,assign)CGFloat pullDistance; 18 | 19 | 20 | /** 21 | * 初始化方法 22 | * 23 | * @param scrollView 关联的滚动视图 24 | * 25 | * @return self 26 | */ 27 | -(id)initWithAssociatedScrollView:(UIScrollView *)scrollView withNavigationBar:(BOOL)navBar; 28 | 29 | 30 | 31 | /** 32 | * 停止旋转,并且滚动视图回弹到底部 33 | */ 34 | -(void)stopRefreshing; 35 | 36 | 37 | /** 38 | * 刷新执行的具体操作 39 | * 40 | * @param block 操作 41 | */ 42 | 43 | -(void)addRefreshingBlock:(void (^)(void))block; 44 | 45 | - (void)refreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount; 46 | 47 | - (void)removeObservers; 48 | @end 49 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/YSCTableRefreshHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshHeaderView.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * MMSCTRefreshStatus 刷新状态 13 | */ 14 | typedef NS_ENUM(NSInteger, MMSCTRefreshStatus) { 15 | /** 16 | * 刷新成功 17 | */ 18 | MMSCTRefreshStatusSuccess, 19 | /** 20 | * 刷新失败 21 | */ 22 | MMSCTRefreshStatusFailed, 23 | /** 24 | * 取消刷新 25 | */ 26 | MMSCTRefreshStatusCancel, 27 | }; 28 | 29 | @interface YSCTableRefreshHeaderView : UIView 30 | 31 | /** 32 | * 需要滑动多大距离才能松开 33 | */ 34 | @property(nonatomic,assign)CGFloat pullDistance; 35 | 36 | 37 | /** 38 | * 初始化方法 39 | * 40 | * @param scrollView 关联的滚动视图 41 | * 42 | * @return self 43 | */ 44 | -(id)initWithAssociatedScrollView:(UIScrollView *)scrollView withNavigationBar:(BOOL)navBar; 45 | 46 | /** 47 | * 立即触发下拉刷新 48 | */ 49 | -(void)triggerPulling; 50 | 51 | /** 52 | * 停止旋转,并且滚动视图回弹到顶部 53 | */ 54 | -(void)stopRefreshing; 55 | 56 | 57 | /** 58 | * 刷新执行的具体操作 59 | * 60 | * @param block 操作 61 | */ 62 | 63 | -(void)addRefreshingBlock:(void (^)(void))block; 64 | 65 | - (void)refreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount; 66 | 67 | - (void)removeObservers; 68 | @end 69 | 70 | -------------------------------------------------------------------------------- /YSCAnimation/tableRefresh/YSCTableRefreshHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCTableRefreshHeaderView.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/1/18. 6 | // Copyright © 2017年 MMS. All rights reserved. 7 | // 8 | 9 | #import "YSCTableRefreshHeaderView.h" 10 | #import "YSCTableRefreshLabelView.h" 11 | #import "YSCTableRefreshCurveView.h" 12 | 13 | @interface YSCTableRefreshHeaderView() 14 | 15 | @property (nonatomic, assign) CGFloat progress; 16 | @property (nonatomic, weak) UIScrollView *associatedScrollView; 17 | @property (nonatomic, copy) void(^refreshingBlock)(void); 18 | @property (nonatomic, assign) MMSCTRefreshStatus refreshStatus; 19 | 20 | @end 21 | 22 | @implementation YSCTableRefreshHeaderView { 23 | YSCTableRefreshLabelView *labelView; 24 | YSCTableRefreshCurveView *curveView; 25 | 26 | CGFloat originOffset; 27 | BOOL willEnd; 28 | BOOL notTracking; 29 | BOOL loading; 30 | 31 | BOOL downDragEnable; 32 | BOOL hasAddObserver; 33 | CGFloat originContentInsetBottom;//记住刚进入下拉时,初始ContentInset的bottom值,下拉结束后置回 34 | } 35 | 36 | #pragma mark -- Public Method 37 | 38 | -(id)initWithAssociatedScrollView:(UIScrollView *)scrollView withNavigationBar:(BOOL)navBar{ 39 | 40 | self = [super initWithFrame:CGRectMake(scrollView.bounds.size.width/2-200/2, -53, 200, 53)]; 41 | if (self) { 42 | if (navBar) { 43 | originOffset = 64.0f; 44 | }else{ 45 | originOffset = 0.0f; 46 | } 47 | self.associatedScrollView = scrollView; 48 | [self setUp]; 49 | [self.associatedScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; 50 | [self.associatedScrollView insertSubview:self atIndex:0]; 51 | 52 | [labelView addObserver:self forKeyPath:@"loading" options:NSKeyValueObservingOptionNew context:nil]; 53 | [labelView addObserver:self forKeyPath:@"progress" options:NSKeyValueObservingOptionNew context:nil]; 54 | 55 | originContentInsetBottom = 0.0; 56 | downDragEnable = YES; 57 | hasAddObserver = YES; 58 | } 59 | 60 | return self; 61 | 62 | } 63 | 64 | 65 | -(void)setProgress:(CGFloat)progress{ 66 | 67 | if (!downDragEnable) { 68 | labelView.hidden = YES; 69 | curveView.hidden = YES; 70 | return; 71 | } else { 72 | labelView.hidden = NO; 73 | curveView.hidden = NO; 74 | } 75 | 76 | if (!self.associatedScrollView.tracking) { 77 | labelView.loading = YES; 78 | } 79 | 80 | if (!willEnd && !loading ) { 81 | NSLog(@"progress:%f",progress); 82 | curveView.progress = labelView.progress = progress; 83 | } 84 | 85 | CGFloat diff = fabs(self.associatedScrollView.contentOffset.y+originOffset) - self.pullDistance + 10; 86 | NSLog(@"diff:%f",diff); 87 | 88 | if (diff > 0) { 89 | 90 | if (!self.associatedScrollView.tracking) { 91 | if (!notTracking) { 92 | notTracking = YES; 93 | loading = YES; 94 | // labelView.loading = YES; 95 | 96 | NSLog(@"旋转"); 97 | 98 | //旋转... 99 | [curveView showInRotateLinePath]; 100 | [self startLoading:curveView]; 101 | 102 | originContentInsetBottom = self.associatedScrollView.contentInset.bottom; 103 | [UIView animateWithDuration:0.3 animations:^{ 104 | 105 | self.associatedScrollView.contentInset = UIEdgeInsetsMake(self.pullDistance + originOffset, 0, originContentInsetBottom, 0); 106 | 107 | } completion:^(BOOL finished) { 108 | 109 | if (_refreshingBlock) { 110 | _refreshingBlock(); 111 | } 112 | 113 | }]; 114 | } 115 | } 116 | 117 | // if (!loading) { 118 | // 119 | // curveView.transform = CGAffineTransformMakeRotation(M_PI * (diff*2/180)); 120 | // } 121 | 122 | }else{ 123 | 124 | labelView.loading = NO; 125 | curveView.transform = CGAffineTransformIdentity; 126 | 127 | } 128 | 129 | } 130 | 131 | 132 | -(void)addRefreshingBlock:(void (^)(void))block 133 | { 134 | self.refreshingBlock = block; 135 | } 136 | 137 | - (void)refreshFinished:(MMSCTRefreshStatus)refreshStatus refreshItemsCount:(NSInteger)itemsCount 138 | { 139 | if (!loading) { 140 | return; 141 | } 142 | _refreshStatus = refreshStatus; 143 | 144 | if (MMSCTRefreshStatusSuccess == refreshStatus && itemsCount == 0) { 145 | downDragEnable = NO; 146 | } else { 147 | downDragEnable = YES; 148 | } 149 | [self stopRefreshing]; 150 | } 151 | 152 | -(void)triggerPulling 153 | { 154 | [self.associatedScrollView setContentOffset:CGPointMake(0, -self.pullDistance-originOffset) animated:YES]; 155 | } 156 | 157 | -(void)stopRefreshing{ 158 | 159 | willEnd = YES; 160 | 161 | self.progress = 1.0; 162 | [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 163 | self.alpha = 0.0f; 164 | self.associatedScrollView.contentInset = UIEdgeInsetsMake(originOffset+0.1, 0, originContentInsetBottom, 0); 165 | } completion:^(BOOL finished) { 166 | self.alpha = 1.0f; 167 | willEnd = NO; 168 | notTracking = NO; 169 | loading = NO; 170 | labelView.loading = NO; 171 | [self stopLoading:curveView]; 172 | [curveView resetData]; 173 | }]; 174 | 175 | 176 | } 177 | 178 | #pragma mark -- Helper Method 179 | 180 | -(void)setUp{ 181 | 182 | //一些默认参数 183 | self.pullDistance = 53; 184 | 185 | curveView = [[YSCTableRefreshCurveView alloc] initWithFrame:CGRectMake(20, 0, 30, self.bounds.size.height)]; 186 | [self insertSubview:curveView atIndex:0]; 187 | 188 | 189 | labelView = [[YSCTableRefreshLabelView alloc] initWithFrame:CGRectMake(curveView.frame.origin.x + curveView.frame.size.width + 10, curveView.frame.origin.y, 150, curveView.frame.size.height)]; 190 | [self insertSubview:labelView aboveSubview:curveView]; 191 | } 192 | 193 | - (void)layoutSubviews 194 | { 195 | CGFloat titleWidth = [labelView titleWidth]; 196 | CGFloat curveViewX = (200 - 30 - titleWidth - 2) / 2.0; 197 | 198 | curveView.frame = CGRectMake(curveViewX, 0, 30, self.bounds.size.height); 199 | labelView.frame = CGRectMake(curveView.frame.origin.x + curveView.frame.size.width + 2, curveView.frame.origin.y, titleWidth + 2, curveView.frame.size.height); 200 | } 201 | 202 | - (void)startLoading:(UIView *)rotateView 203 | { 204 | rotateView.transform = CGAffineTransformIdentity; 205 | 206 | CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; 207 | rotationAnimation.toValue = @(M_PI * 2.0); 208 | rotationAnimation.duration = 0.5f; 209 | rotationAnimation.autoreverses = NO; 210 | rotationAnimation.repeatCount = HUGE_VALF; 211 | rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 212 | [rotateView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"]; 213 | 214 | } 215 | 216 | - (void)stopLoading:(UIView *)rotateView{ 217 | 218 | [rotateView.layer removeAllAnimations]; 219 | } 220 | 221 | #pragma mark -- KVO 222 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ 223 | 224 | if ([keyPath isEqualToString:@"contentOffset"]) { 225 | 226 | CGPoint contentOffset = [[change valueForKey:NSKeyValueChangeNewKey] CGPointValue]; 227 | 228 | if (contentOffset.y + originOffset <= 0) { 229 | 230 | self.progress = MAX(0.0, MIN(fabs(contentOffset.y+originOffset)/self.pullDistance, 1.0)); 231 | 232 | } 233 | } 234 | 235 | if ([keyPath isEqualToString:@"loading"] || [keyPath isEqualToString:@"progress"]) { 236 | [self layoutIfNeeded]; 237 | } 238 | } 239 | 240 | - (void)removeObservers 241 | { 242 | if (hasAddObserver) { 243 | [self.associatedScrollView removeObserver:self forKeyPath:@"contentOffset"]; 244 | [labelView removeObserver:self forKeyPath:@"loading"]; 245 | [labelView removeObserver:self forKeyPath:@"progress"]; 246 | hasAddObserver = NO; 247 | } 248 | } 249 | 250 | #pragma dealloc 251 | -(void)dealloc 252 | { 253 | NSLog(@""); 254 | } 255 | 256 | @end 257 | -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/YSCNewVoiceWaveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCNewVoiceWaveView.h 3 | // Waver 4 | // 5 | // Created by yushichao on 16/8/9. 6 | // Copyright © 2016年 YSC Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^YSCShowLoadingCircleCallback)(void); 12 | 13 | @interface YSCNewVoiceWaveView : UIView 14 | 15 | /** 16 | * 添加并初始化波纹视图 17 | * 18 | * @param parentView 父视图 19 | * @param voiceWaveDisappearCallback 波纹消失时回调 20 | */ 21 | - (void)showInParentView:(UIView *)parentView; 22 | 23 | /** 24 | * 设置波纹个数,默认两个 25 | * 26 | * @param waveNumber 波纹个数 27 | */ 28 | - (void)setVoiceWaveNumber:(NSInteger)waveNumber; 29 | 30 | /** 31 | * 开始声波动画 32 | */ 33 | - (void)startVoiceWave; 34 | 35 | /** 36 | * 改变音量来改变声波振动幅度 37 | * 38 | * @param stopVoiceWave 音量大小 大小为0~1 39 | */ 40 | - (void)changeVolume:(CGFloat)volume; 41 | 42 | /** 43 | * 停止声波动画 44 | */ 45 | - (void)stopVoiceWaveWithShowLoadingViewCallback:(YSCShowLoadingCircleCallback)showLoadingCircleCallback; 46 | 47 | /** 48 | * 移掉声波 49 | */ 50 | - (void)removeFromParent; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/YSCVoiceLoadingCircleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCVoiceLoadingCircleView.h 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/8/15. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCVoiceLoadingCircleView : UIView 12 | 13 | /** 14 | * 初始化 15 | * 16 | * @param radius 圆圈半径 17 | * 18 | * @param center 本视图中心坐标 19 | * 20 | * @return 实例对象 21 | */ 22 | - (instancetype)initWithCircleRadius:(CGFloat)radius center:(CGPoint)center; 23 | 24 | /** 25 | * 开始并添加语音搜索loading动画 26 | */ 27 | - (void)startLoadingInParentView:(UIView *)parentView; 28 | 29 | /** 30 | * 停止语音搜搜loading动画 31 | */ 32 | - (void)stopLoading; 33 | @end 34 | -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/YSCVoiceWaveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCVoiceWaveView.h 3 | // Waver 4 | // 5 | // Created by yushichao on 16/8/9. 6 | // Copyright © 2016年 YSC Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^YSCShowLoadingCircleCallback)(void); 12 | 13 | @interface YSCVoiceWaveView : UIView 14 | 15 | /** 16 | * 添加并初始化波纹视图 17 | * 18 | * @param parentView 父视图 19 | * @param voiceWaveDisappearCallback 波纹消失时回调 20 | */ 21 | - (void)showInParentView:(UIView *)parentView; 22 | 23 | /** 24 | * 开始声波动画 25 | */ 26 | - (void)startVoiceWave; 27 | 28 | /** 29 | * 改变音量来改变声波振动幅度 30 | * 31 | * @param stopVoiceWave 音量大小 大小为0~1 32 | */ 33 | - (void)changeVolume:(CGFloat)volume; 34 | 35 | /** 36 | * 停止声波动画 37 | */ 38 | - (void)stopVoiceWaveWithShowLoadingViewCallback:(YSCShowLoadingCircleCallback)showLoadingCircleCallback; 39 | 40 | /** 41 | * 移掉声波 42 | */ 43 | - (void)removeFromParent; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/YSCVolumeQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCVolumeQueue.h 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/8/17. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCVolumeQueue : NSObject 12 | 13 | - (void)pushVolume:(CGFloat)volume; 14 | - (void)pushVolumeWithArray:(NSArray *)array; 15 | - (CGFloat)popVolume; 16 | - (void)cleanQueue; 17 | 18 | @end 19 | 20 | -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/YSCVolumeQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCVolumeQueue.m 3 | // MISVoiceSearchLib 4 | // 5 | // Created by yushichao on 16/8/17. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCVolumeQueue.h" 10 | 11 | #define minVolume 0.05 12 | 13 | @interface YSCVolumeQueue() 14 | 15 | @property (nonatomic, strong) NSMutableArray *volumeArray; 16 | 17 | @end 18 | 19 | @implementation YSCVolumeQueue 20 | 21 | - (instancetype)init 22 | { 23 | self = [super init]; 24 | if (self) { 25 | self.volumeArray = [NSMutableArray array]; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (void)pushVolume:(CGFloat)volume 32 | { 33 | if (volume >= minVolume) { 34 | [_volumeArray addObject:[NSNumber numberWithFloat:volume]]; 35 | } 36 | } 37 | 38 | - (void)pushVolumeWithArray:(NSArray *)array 39 | { 40 | if (array.count > 0) { 41 | for (NSInteger i = 0; i < array.count; i++) { 42 | CGFloat volume = [array[i] floatValue]; 43 | [self pushVolume:volume]; 44 | } 45 | } 46 | } 47 | 48 | - (CGFloat)popVolume 49 | { 50 | CGFloat volume = -10; 51 | if (_volumeArray.count > 0) { 52 | volume = [[_volumeArray firstObject] floatValue]; 53 | [_volumeArray removeObjectAtIndex:0]; 54 | } 55 | 56 | return volume; 57 | } 58 | 59 | - (void)cleanQueue 60 | { 61 | if (_volumeArray) { 62 | [_volumeArray removeAllObjects]; 63 | } 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/circleBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimation/voiceWave/circleBackground.png -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimation/voiceWave/fill.png -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/firstLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimation/voiceWave/firstLine.png -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/maskImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimation/voiceWave/maskImage.png -------------------------------------------------------------------------------- /YSCAnimation/voiceWave/secondLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimation/voiceWave/secondLine.png -------------------------------------------------------------------------------- /YSCAnimation/waterWave/YSCWaterWaveView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaterWaveView.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/3/2. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, YSCWaterWaveAnimateType) { 12 | YSCWaterWaveAnimateTypeShow, 13 | YSCWaterWaveAnimateTypeHide, 14 | }; 15 | 16 | @interface YSCWaterWaveView : UIView 17 | 18 | @property (nonatomic, strong) UIColor *firstWaveColor; // 第一个波浪颜色 19 | @property (nonatomic, strong) UIColor *secondWaveColor; // 第二个波浪颜色 20 | @property (nonatomic, assign) CGFloat percent; // 上升高度最大百分比 21 | 22 | -(void) startWave; 23 | -(void) stopWave; 24 | -(void) reset; 25 | - (void)removeFromParentView; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /YSCAnimation/waterWave/YSCWaterWaveView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaterWaveView.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/3/2. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCWaterWaveView.h" 10 | 11 | @interface YSCWaterWaveView () 12 | 13 | @property (nonatomic, strong) CADisplayLink *waveDisplaylink; 14 | 15 | @property (nonatomic, strong) CAShapeLayer *firstWaveLayer; 16 | @property (nonatomic, strong) CAShapeLayer *secondWaveLayer; 17 | @end 18 | 19 | @implementation YSCWaterWaveView { 20 | CGFloat waveAmplitude; // 波纹振幅 21 | CGFloat waveCycle; // 波纹周期 22 | CGFloat waveSpeed; // 波纹速度 23 | CGFloat waveGrowth; // 波纹上升速度 24 | 25 | CGFloat waterWaveHeight; 26 | CGFloat waterWaveWidth; 27 | CGFloat offsetX; // 波浪x位移 28 | CGFloat currentWavePointY; // 当前波浪上市高度Y(高度从大到小 坐标系向下增长) 29 | 30 | float variable; //可变参数 更加真实 模拟波纹 31 | BOOL increase; // 增减变化 32 | YSCWaterWaveAnimateType _animateType;//展现时先上升,隐藏时先下降 33 | } 34 | 35 | /* 36 | // Only override drawRect: if you perform custom drawing. 37 | // An empty implementation adversely affects performance during animation. 38 | - (void)drawRect:(CGRect)rect { 39 | // Drawing code 40 | } 41 | */ 42 | 43 | - (void)removeFromParentView 44 | { 45 | [self reset]; 46 | [self removeFromSuperview]; 47 | } 48 | 49 | -(id)initWithFrame:(CGRect)frame 50 | { 51 | self = [super initWithFrame:frame]; 52 | if (self) { 53 | self.backgroundColor = [UIColor clearColor]; 54 | self.layer.masksToBounds = YES; 55 | [self setUp]; 56 | } 57 | 58 | return self; 59 | } 60 | 61 | -(id)initWithCoder:(NSCoder *)aDecoder 62 | { 63 | self = [super initWithCoder:aDecoder]; 64 | if (self) { 65 | self.backgroundColor = [UIColor clearColor]; 66 | self.layer.masksToBounds = YES; 67 | [self setUp]; 68 | } 69 | return self; 70 | } 71 | 72 | - (void)setFrame:(CGRect)frame 73 | { 74 | [super setFrame:frame]; 75 | waterWaveHeight = self.frame.size.height/2; 76 | waterWaveWidth = self.frame.size.width; 77 | if (waterWaveWidth > 0) { 78 | waveCycle = 1.29 * M_PI / waterWaveWidth; 79 | } 80 | 81 | if (currentWavePointY <= 0) { 82 | currentWavePointY = self.frame.size.height; 83 | } 84 | } 85 | 86 | - (void)setUp 87 | { 88 | waterWaveHeight = self.frame.size.height/2; 89 | waterWaveWidth = self.frame.size.width; 90 | _firstWaveColor = [UIColor colorWithRed:223/255.0 green:83/255.0 blue:64/255.0 alpha:1]; 91 | _secondWaveColor = [UIColor colorWithRed:236/255.0f green:90/255.0f blue:66/255.0f alpha:1]; 92 | 93 | waveGrowth = 0.85; 94 | waveSpeed = 0.4/M_PI; 95 | 96 | [self resetProperty]; 97 | } 98 | 99 | - (void)resetProperty 100 | { 101 | currentWavePointY = self.frame.size.height; 102 | 103 | variable = 1.6; 104 | increase = NO; 105 | 106 | offsetX = 0; 107 | } 108 | 109 | - (void)setFirstWaveColor:(UIColor *)firstWaveColor 110 | { 111 | _firstWaveColor = firstWaveColor; 112 | _firstWaveLayer.fillColor = firstWaveColor.CGColor; 113 | } 114 | 115 | - (void)setSecondWaveColor:(UIColor *)secondWaveColor 116 | { 117 | _secondWaveColor = secondWaveColor; 118 | _secondWaveLayer.fillColor = secondWaveColor.CGColor; 119 | } 120 | 121 | - (void)setPercent:(CGFloat)percent 122 | { 123 | _percent = percent; 124 | [self resetProperty]; 125 | } 126 | 127 | -(void)startWave 128 | { 129 | _animateType = YSCWaterWaveAnimateTypeShow; 130 | [self resetProperty]; 131 | 132 | if (_firstWaveLayer == nil) { 133 | // 创建第一个波浪Layer 134 | _firstWaveLayer = [CAShapeLayer layer]; 135 | _firstWaveLayer.fillColor = _firstWaveColor.CGColor; 136 | [self.layer addSublayer:_firstWaveLayer]; 137 | } 138 | 139 | if (_secondWaveLayer == nil) { 140 | // 创建第二个波浪Layer 141 | _secondWaveLayer = [CAShapeLayer layer]; 142 | _secondWaveLayer.fillColor = _secondWaveColor.CGColor; 143 | [self.layer addSublayer:_secondWaveLayer]; 144 | } 145 | 146 | if (_waveDisplaylink) { 147 | [self stopWave]; 148 | } 149 | 150 | // 启动定时调用 151 | _waveDisplaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(getCurrentWave:)]; 152 | [_waveDisplaylink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 153 | 154 | } 155 | 156 | -(void) stopWave 157 | { 158 | _animateType = YSCWaterWaveAnimateTypeHide; 159 | 160 | // [_waveDisplaylink invalidate]; 161 | // _waveDisplaylink = nil; 162 | } 163 | 164 | - (void)reset 165 | { 166 | [_waveDisplaylink invalidate]; 167 | _waveDisplaylink = nil; 168 | 169 | [self resetProperty]; 170 | 171 | [_firstWaveLayer removeFromSuperlayer]; 172 | _firstWaveLayer = nil; 173 | [_secondWaveLayer removeFromSuperlayer]; 174 | _secondWaveLayer = nil; 175 | } 176 | 177 | -(void)animateWave 178 | { 179 | if (increase) { 180 | variable += 0.01; 181 | }else{ 182 | variable -= 0.01; 183 | } 184 | 185 | if (variable<=1) { 186 | increase = YES; 187 | } 188 | 189 | if (variable>=1.6) { 190 | increase = NO; 191 | } 192 | 193 | waveAmplitude = variable*5; 194 | } 195 | 196 | -(void)getCurrentWave:(CADisplayLink *)displayLink 197 | { 198 | [self animateWave]; 199 | 200 | if (YSCWaterWaveAnimateTypeShow == _animateType && currentWavePointY > 2 * waterWaveHeight *(1-_percent)) { 201 | // 波浪高度未到指定高度 继续上涨 202 | currentWavePointY -= waveGrowth; 203 | } else if (YSCWaterWaveAnimateTypeHide == _animateType && currentWavePointY < 2 * waterWaveHeight) { 204 | currentWavePointY += waveGrowth; 205 | } else if (YSCWaterWaveAnimateTypeHide == _animateType && currentWavePointY == 2 * waterWaveHeight) { 206 | [_waveDisplaylink invalidate]; 207 | _waveDisplaylink = nil; 208 | [self removeFromParentView]; 209 | } 210 | 211 | // 波浪位移 212 | offsetX += waveSpeed; 213 | 214 | [self setCurrentFirstWaveLayerPath]; 215 | [self setCurrentSecondWaveLayerPath]; 216 | } 217 | 218 | -(void)setCurrentFirstWaveLayerPath 219 | { 220 | CGMutablePathRef path = CGPathCreateMutable(); 221 | CGFloat y = currentWavePointY; 222 | CGPathMoveToPoint(path, nil, 0, y); 223 | for (float x = 0.0f; x <= waterWaveWidth ; x++) { 224 | // 正弦波浪公式 225 | y = waveAmplitude * sin(waveCycle * x + offsetX) + currentWavePointY; 226 | CGPathAddLineToPoint(path, nil, x, y); 227 | } 228 | 229 | CGPathAddLineToPoint(path, nil, waterWaveWidth, self.frame.size.height); 230 | CGPathAddLineToPoint(path, nil, 0, self.frame.size.height); 231 | CGPathCloseSubpath(path); 232 | 233 | _firstWaveLayer.path = path; 234 | CGPathRelease(path); 235 | } 236 | 237 | -(void)setCurrentSecondWaveLayerPath 238 | { 239 | CGMutablePathRef path = CGPathCreateMutable(); 240 | CGFloat y = currentWavePointY; 241 | CGPathMoveToPoint(path, nil, 0, y); 242 | for (float x = 0.0f; x <= waterWaveWidth ; x++) { 243 | // 余弦波浪公式 244 | y = waveAmplitude * cos(waveCycle * x + offsetX) + currentWavePointY; 245 | CGPathAddLineToPoint(path, nil, x, y); 246 | } 247 | 248 | CGPathAddLineToPoint(path, nil, waterWaveWidth, self.frame.size.height); 249 | CGPathAddLineToPoint(path, nil, 0, self.frame.size.height); 250 | CGPathCloseSubpath(path); 251 | 252 | _secondWaveLayer.path = path; 253 | CGPathRelease(path); 254 | } 255 | 256 | - (void)dealloc 257 | { 258 | [self reset]; 259 | } 260 | 261 | @end 262 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_bottom.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "microPhone_bottom@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "microPhone_bottom@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_bottom.imageset/microPhone_bottom@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_bottom.imageset/microPhone_bottom@2x.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_bottom.imageset/microPhone_bottom@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_bottom.imageset/microPhone_bottom@3x.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_top.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "microPhone_top@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "microPhone_top@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_top.imageset/microPhone_top@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_top.imageset/microPhone_top@2x.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_top.imageset/microPhone_top@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Assets.xcassets/microPhone_top.imageset/microPhone_top@3x.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/LoadGif/YSCloadGifViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCloadGifViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/11/8. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCloadGifViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/LoadGif/YSCloadGifViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCloadGifViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/11/8. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCloadGifViewController.h" 10 | #import "YSCGifLoadImage.h" 11 | #import "YSCGifLoadImageView.h" 12 | 13 | @interface YSCloadGifViewController () 14 | 15 | @property (nonatomic, strong) YSCGifLoadImageView *gifImageView; 16 | 17 | @end 18 | 19 | @implementation YSCloadGifViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | YSCGifLoadImage *image = (YSCGifLoadImage *)[YSCGifLoadImage imageNamed:@"sw3.gif"]; 27 | [self.gifImageView setImage:image]; 28 | [self.view addSubview:_gifImageView]; 29 | [_gifImageView startGifAnimatingWithGifRepeatCount:INT32_MAX gifRepeatDelayOffset:0 animatingCompleteCallBack:^{ 30 | 31 | }]; 32 | } 33 | 34 | - (void)didReceiveMemoryWarning { 35 | [super didReceiveMemoryWarning]; 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | - (void)viewWillDisappear:(BOOL)animated 40 | { 41 | [_gifImageView stopGifAnimation]; 42 | } 43 | 44 | - (YSCGifLoadImageView *)gifImageView 45 | { 46 | if (!_gifImageView) { 47 | self.gifImageView = [[YSCGifLoadImageView alloc] initWithFrame:CGRectMake(0, 0, 300, 225)]; 48 | _gifImageView.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0); 49 | } 50 | return _gifImageView; 51 | } 52 | 53 | /* 54 | #pragma mark - Navigation 55 | 56 | // In a storyboard-based application, you will often want to do a little preparation before navigation 57 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 58 | // Get the new view controller using [segue destinationViewController]. 59 | // Pass the selected object to the new view controller. 60 | } 61 | */ 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/carouselTitle/YSCCarouselTitleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCarouselTitleViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/2/28. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCCarouselTitleViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/carouselTitle/YSCCarouselTitleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCarouselTitleViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/2/28. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCCarouselTitleViewController.h" 10 | #import "YSCCarouselTitleView.h" 11 | 12 | @interface YSCCarouselTitleViewController () 13 | 14 | @property (nonatomic, strong) YSCCarouselTitleView *shortTitleView; 15 | @property (nonatomic, strong) YSCCarouselTitleView *longTitleView; 16 | 17 | @end 18 | 19 | @implementation YSCCarouselTitleViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | self.shortTitleView = [[YSCCarouselTitleView alloc] initWithFrame:CGRectMake(100, 100, self.view.bounds.size.width - 100 * 2, 40)]; 27 | _shortTitleView.title = @"静态--标题比较短"; 28 | [self.view addSubview:_shortTitleView]; 29 | 30 | self.longTitleView = [[YSCCarouselTitleView alloc] initWithFrame:CGRectMake(100, 200, self.view.bounds.size.width - 100 * 2, 40)]; 31 | _longTitleView.title = @"轮播--标题要长长长长长长长长长长长长长长长长长长长长长"; 32 | [self.view addSubview:_longTitleView]; 33 | } 34 | 35 | 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/custom wave/YSCWaveAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaveAnimationViewController.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCWaveAnimationViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/custom wave/YSCWaveAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaveAnimationViewController.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCWaveAnimationViewController.h" 10 | #import "YSCWaveView.h" 11 | 12 | @interface YSCWaveAnimationViewController () 13 | 14 | @property (nonatomic, strong) YSCWaveView *waveView; 15 | 16 | @end 17 | 18 | @implementation YSCWaveAnimationViewController 19 | 20 | - (void)viewDidLoad 21 | { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | UIButton *pusleButton = [[UIButton alloc] initWithFrame:CGRectMake(20 , 320, 150, 50)]; 27 | [pusleButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 28 | [pusleButton setTitle:@"pusle" forState:UIControlStateNormal]; 29 | [pusleButton addTarget:self action:@selector(pusleButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 30 | [self.view addSubview:pusleButton]; 31 | 32 | UIButton *waveButton = [[UIButton alloc] initWithFrame:CGRectMake(200 , 320, 150, 50)]; 33 | [waveButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 34 | [waveButton setTitle:@"wave" forState:UIControlStateNormal]; 35 | [waveButton addTarget:self action:@selector(waveButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 36 | [self.view addSubview:waveButton]; 37 | 38 | UIButton *movedWaveButton = [[UIButton alloc] initWithFrame:CGRectMake(20 , 400, 150, 50)]; 39 | [movedWaveButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 40 | [movedWaveButton setTitle:@"movedWave" forState:UIControlStateNormal]; 41 | [movedWaveButton addTarget:self action:@selector(movedWaveButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 42 | [self.view addSubview:movedWaveButton]; 43 | } 44 | 45 | - (void)didReceiveMemoryWarning 46 | { 47 | [super didReceiveMemoryWarning]; 48 | // Dispose of any resources that can be recreated. 49 | } 50 | 51 | - (void)pusleButtonTouched:(id)sender 52 | { 53 | [self removeSubViews]; 54 | [self.view addSubview:self.waveView]; 55 | [_waveView showWaveViewWithType:YSCWaveTypePulse]; 56 | } 57 | 58 | - (void)waveButtonTouched:(id)sender 59 | { 60 | [self removeSubViews]; 61 | [self.view addSubview:self.waveView]; 62 | [_waveView showWaveViewWithType:YSCWaveTypeVoice]; 63 | } 64 | 65 | - (void)movedWaveButtonTouched:(id)sender 66 | { 67 | [self removeSubViews]; 68 | [self.view addSubview:self.waveView]; 69 | [_waveView showWaveViewWithType:YSCWaveTypeMovedVoice]; 70 | } 71 | 72 | - (void)removeSubViews 73 | { 74 | if (_waveView.superview == self.view) { 75 | [_waveView removeFromParentView]; 76 | } 77 | } 78 | 79 | - (YSCWaveView *)waveView 80 | { 81 | if (!_waveView) { 82 | self.waveView = [[YSCWaveView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 300)]; 83 | _waveView.backgroundColor = [UIColor grayColor]; 84 | } 85 | 86 | return _waveView; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/custom/YSCLayerAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCLayerAnimationViewController.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, YSCAnimatinType) { 12 | YSCAnimatinTypeBasic, 13 | YSCAnimatinTypeKeyFrame, 14 | YSCAnimatinTypeKeyFramePath 15 | }; 16 | 17 | @interface YSCLayerAnimationViewController : UIViewController 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/custom/YSCLayerAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCLayerAnimationViewController.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCLayerAnimationViewController.h" 10 | 11 | @interface YSCLayerAnimationViewController () 12 | 13 | @end 14 | 15 | @implementation YSCLayerAnimationViewController 16 | 17 | - (void)dealloc 18 | { 19 | NSLog(@""); 20 | } 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view, typically from a nib. 25 | self.view.backgroundColor = [UIColor whiteColor]; 26 | [self addShadowLayer];//add shadow 27 | 28 | CALayer *subLayer = [[CALayer alloc] init]; 29 | subLayer.position = CGPointMake(50, 100); 30 | subLayer.bounds = CGRectMake(0, 0, 30, 30); 31 | // subLayer.delegate = self; 32 | subLayer.backgroundColor = [UIColor blueColor].CGColor; 33 | subLayer.masksToBounds = YES; 34 | subLayer.cornerRadius = 15; 35 | 36 | [self.view.layer addSublayer:subLayer]; 37 | [subLayer setNeedsDisplay];///需要调,不然不走drawLayer: inContext: 38 | } 39 | 40 | - (void)didReceiveMemoryWarning { 41 | [super didReceiveMemoryWarning]; 42 | // Dispose of any resources that can be recreated. 43 | } 44 | 45 | - (void)addShadowLayer 46 | { 47 | CALayer *shadowLayer = [[CALayer alloc] init]; 48 | shadowLayer.position = CGPointMake(50, 100); 49 | shadowLayer.bounds = CGRectMake(0, 0, 30, 30); 50 | shadowLayer.cornerRadius = 15; 51 | shadowLayer.shadowColor = [UIColor yellowColor].CGColor; 52 | shadowLayer.shadowOffset = CGSizeMake(3, 3); 53 | shadowLayer.shadowOpacity = 0.5; 54 | //layer要有内容才能显出阴影,如本身contents,backgroundColor,border 55 | shadowLayer.borderColor = [UIColor redColor].CGColor; 56 | shadowLayer.borderWidth = 2; 57 | // shadowLayer.backgroundColor = [UIColor redColor].CGColor; 58 | // shadowLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"D.jpg"].CGImage); 59 | 60 | [self.view.layer addSublayer:shadowLayer]; 61 | } 62 | 63 | - (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext 64 | { 65 | CGContextScaleCTM(theContext, 1, -1); 66 | CGContextTranslateCTM(theContext, 0, - 30); 67 | CGImageRef imageRef = [UIImage imageNamed:@"tree.jpg"].CGImage; 68 | CGContextDrawImage(theContext, CGRectMake(0, 0, 100, 100), imageRef); 69 | 70 | CGImageRelease(imageRef); 71 | } 72 | 73 | - (void)addAnimationToLayerWithMode:(YSCAnimatinType)mode endPoint:(CGPoint)endPoint 74 | { 75 | NSArray *subLayers = [self.view.layer sublayers]; 76 | CALayer *animationLayer = [subLayers lastObject]; 77 | 78 | if (YSCAnimatinTypeBasic == mode) { 79 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 80 | animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(50, 100)]; 81 | animation.toValue = [NSValue valueWithCGPoint:endPoint]; 82 | animation.duration = 3; 83 | [animationLayer addAnimation:animation forKey:@"animaton-position"]; 84 | } else if (YSCAnimatinTypeKeyFrame == mode) { 85 | CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 86 | animation.values = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:CGPointMake(50, 100)], [NSValue valueWithCGPoint:CGPointMake(150, 200)], [NSValue valueWithCGPoint:CGPointMake(50, 300)], [NSValue valueWithCGPoint:endPoint], nil]; 87 | animation.duration = 4; 88 | animation.calculationMode = kCAAnimationPaced;//kCAAnimationLinear:每个keyValue间匀速 kCAAnimationCubic:平滑移动 kCAAnimationDiscrete:跳动,没有中间的滑动 kCAAnimationPaced:整个动画匀速,动画时间相关参数默认设置好 kCAAnimationCubicPaced:整个动画平滑匀速,动画时间相关参数默认设置好 89 | animation.keyTimes = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.3], [NSNumber numberWithFloat:0.6], [NSNumber numberWithFloat:0.7], [NSNumber numberWithFloat:1.0], nil];//kCAAnimationDiscrete 需要多添一个time值,最后一个时间间隔用于动画结束后返回layer的初始状态或新设置的,kCAAnimationPaced和kCAAnimationCubicPaced对time属性不起作用,其他的keyValue数与time数相同,代表value的起始时间 90 | animation.delegate = self;//得放在addAnimation:之前,否则代理不会调用 91 | //存储当前位置在动画结束后使用 92 | [animation setValue:[NSValue valueWithCGPoint:endPoint] forKey:@"AnimationLocation"]; 93 | [animationLayer addAnimation:animation forKey:@"animation-point"]; 94 | } else if (YSCAnimatinTypeKeyFramePath == mode) { 95 | animationLayer.position = CGPointMake(350, 100);//加在添加动画前,先把layer的状态设为动画的最后状态,可避免动画完成后产生一个从初始位置到最终设置位置的快速隐式动画;若加在添加动画后,会产生一个从初始位置到最终设置位置的快速隐式动画,然后再开始添加的动画;采用代理的方式,在动画结束后,设置layer的最终状态(删除隐式动画),则可能出现一个从初始位置到最终设置位置的快速闪动。 96 | CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 97 | CGMutablePathRef pathRef = CGPathCreateMutable(); 98 | CGPathMoveToPoint(pathRef, NULL, 50, 100); 99 | CGPathAddCurveToPoint(pathRef, NULL, 70, 200, 230, 200, 250, 100); 100 | CGPathAddCurveToPoint(pathRef, NULL, 270, 200, 330, 200, 350, 100); 101 | animation.path = pathRef; 102 | CGPathRelease(pathRef); 103 | animation.duration = 3; 104 | // animation.beginTime = CACurrentMediaTime() + 2;//此beginTime为图层的本地时间 105 | animation.calculationMode = kCAAnimationLinear; 106 | animation.keyTimes = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.3], [NSNumber numberWithFloat:1.0], nil];//对于keyValues类型,标识各个value间的起始时间,个数为value数相同(kCAAnimationDiscrete会多一个);对于path则表示各个线段的起始时间,包括直线或曲线 107 | animation.delegate = self;//得放在addAnimation:之前,否则代理不会调用 108 | animation.timeOffset = 0.0;//动画开始播放的位置,但仍是完整的一个动画。只是开始和结束的位置变了 109 | animation.beginTime = CACurrentMediaTime() + 1; 110 | //存储当前位置在动画结束后使用 111 | [animation setValue:[NSValue valueWithCGPoint:CGPointMake(350, 100)] forKey:@"AnimationLocation"]; 112 | [animationLayer addAnimation:animation forKey:@"animation-point"]; 113 | } 114 | } 115 | 116 | #pragma mark - CAAnimationDelegate 117 | 118 | -(void)animationDidStart:(CAAnimation *)anim 119 | { 120 | 121 | } 122 | 123 | -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 124 | { 125 | NSArray *subLayers = [self.view.layer sublayers]; 126 | CALayer *animationLayer = [subLayers lastObject]; 127 | 128 | //animationLayer.position = [[anim valueForKey:@"AnimationLocation"] CGPointValue];//直接设置最终位置,会产生一个从初始位置到最终设置位置的快速隐式动画,解决这个问题有两种办法:关闭图层隐式动画、设置动画图层为根图层 129 | 130 | //关闭图层隐式动画 131 | //开启事务 132 | [CATransaction begin]; 133 | //禁用隐式动画 134 | [CATransaction setDisableActions:YES]; 135 | animationLayer.position=[[anim valueForKey:@"AnimationLocation"] CGPointValue];//设置最终位置,否则,layer会回到初始位置,动画并没有改变layer。 136 | //提交事务 137 | [CATransaction commit]; 138 | } 139 | 140 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 141 | { 142 | UITouch *touch = [touches anyObject]; 143 | CGPoint point = [touch locationInView:self.view]; 144 | NSArray *subLayers = [self.view.layer sublayers]; 145 | CALayer *animationLayer = [subLayers lastObject]; 146 | CAAnimation *animation = [animationLayer animationForKey:@"animation-point"]; 147 | 148 | CFTimeInterval timeInterval = [animationLayer convertTime:CACurrentMediaTime() fromLayer:nil]; 149 | NSLog(@"CurrentMediaTime:%f",timeInterval); 150 | 151 | if (animation) { 152 | if (0 == animationLayer.speed) { 153 | [self resumeAnimation]; 154 | } else { 155 | [self pauseAnimation]; 156 | } 157 | } else { 158 | NSInteger mode = YSCAnimatinTypeKeyFramePath; 159 | [self addAnimationToLayerWithMode:mode endPoint:point]; 160 | animationLayer.beginTime = -1; 161 | } 162 | } 163 | 164 | - (void)pauseAnimation 165 | { 166 | NSArray *subLayers = [self.view.layer sublayers]; 167 | CALayer *animationLayer = [subLayers lastObject]; 168 | CFTimeInterval timeInterval = [animationLayer convertTime:CACurrentMediaTime() fromLayer:nil]; 169 | animationLayer.timeOffset = timeInterval; 170 | animationLayer.speed = 0; 171 | 172 | NSLog(@"%f, %f", timeInterval, CACurrentMediaTime()); 173 | } 174 | 175 | - (void)resumeAnimation 176 | { 177 | NSArray *subLayers = [self.view.layer sublayers]; 178 | CALayer *animationLayer = [subLayers lastObject]; 179 | animationLayer.beginTime = CACurrentMediaTime() - animationLayer.timeOffset;//beginTime为本图层相对于父图层的时间,为相对时间;动画时间为当前图层时间 + beginTime,即为当前绝对时间;如在此基础上加1,则动画会往前倒1秒开始显示(此时本图层的开始时间已经超出父图层时间,即绝对时间,相当于动画暂停处的前面一秒),减一的话就是往后1秒开始显示 180 | animationLayer.timeOffset = 0;//在当前动画timeOffset后的动画开始显示 181 | animationLayer.speed = 1; 182 | 183 | NSLog(@"%f, %f", animationLayer.beginTime, CACurrentMediaTime()); 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/emitter/YSCButterflyViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCButterflyViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/25. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCButterflyViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/emitter/YSCButterflyViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCButterflyViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/25. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCButterflyViewController.h" 10 | 11 | @interface YSCButterflyViewController () { 12 | CAEmitterLayer * _butterflyEmitter;// 13 | } 14 | @end 15 | 16 | @implementation YSCButterflyViewController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | self.view.backgroundColor=[UIColor whiteColor]; 23 | //emitter 24 | _butterflyEmitter=[[CAEmitterLayer alloc] init]; 25 | _butterflyEmitter.emitterPosition=CGPointMake(self.view.frame.size.width/2,self.view.frame.size.height-20); 26 | _butterflyEmitter.emitterSize=CGSizeMake(self.view.frame.size.width-100, 20); 27 | _butterflyEmitter.renderMode = kCAEmitterLayerUnordered; 28 | _butterflyEmitter.emitterShape = kCAEmitterLayerCuboid; 29 | 30 | _butterflyEmitter.emitterDepth = 10; 31 | _butterflyEmitter.preservesDepth = YES; 32 | 33 | //cells 34 | //blue butterfly 35 | CAEmitterCell *blueButterfly = [CAEmitterCell emitterCell]; 36 | blueButterfly.birthRate=8; 37 | blueButterfly.lifetime=5.0; 38 | blueButterfly.lifetimeRange=1.5; 39 | blueButterfly.color=[[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1] CGColor]; 40 | blueButterfly.contents=(id)[[UIImage imageNamed:@"butterfly1"] CGImage]; 41 | [blueButterfly setName:@"blueButterfly"]; 42 | 43 | blueButterfly.velocity=160; 44 | blueButterfly.velocityRange=80; 45 | blueButterfly.emissionLongitude=M_PI+M_PI_2; 46 | blueButterfly.emissionLatitude = M_PI+M_PI_2; 47 | blueButterfly.emissionRange=M_PI_2; 48 | 49 | blueButterfly.scaleSpeed=0.3; 50 | blueButterfly.spin=0.2; 51 | blueButterfly.alphaSpeed = 0.2; 52 | 53 | //yellow butterfly 54 | CAEmitterCell *yellowButterfly = [CAEmitterCell emitterCell]; 55 | yellowButterfly.birthRate=4; 56 | yellowButterfly.lifetime=5.0; 57 | yellowButterfly.lifetimeRange=1.5; 58 | yellowButterfly.color=[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.05] CGColor]; 59 | yellowButterfly.contents=(id)[[UIImage imageNamed:@"butterfly2"] CGImage]; 60 | [yellowButterfly setName:@"yellowButterfly"]; 61 | 62 | yellowButterfly.velocity=250; 63 | yellowButterfly.velocityRange=100; 64 | yellowButterfly.emissionLongitude=M_PI+M_PI_2; 65 | yellowButterfly.emissionLatitude = M_PI+M_PI_2; 66 | yellowButterfly.emissionRange=M_PI_2; 67 | yellowButterfly.alphaSpeed = 0.2; 68 | yellowButterfly.scaleSpeed=0.3; 69 | yellowButterfly.spin=0.2; 70 | _butterflyEmitter.emitterCells=[NSArray arrayWithObjects:yellowButterfly, blueButterfly,nil]; 71 | [self.view.layer addSublayer:_butterflyEmitter]; 72 | 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/emitter/YSCEmitterAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCEmitterAnimationViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/25. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCEmitterAnimationViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/emitter/YSCEmitterAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCEmitterAnimationViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/25. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCEmitterAnimationViewController.h" 10 | 11 | @interface YSCEmitterAnimationViewController () 12 | 13 | @property (nonatomic, strong) NSMutableArray *cellDataArray; 14 | 15 | @end 16 | 17 | static NSString * const YSCCellDataName = @"YSCCellDataName"; 18 | static NSString * const YSCCellDataClass = @"YSCCellDataClass"; 19 | 20 | @implementation YSCEmitterAnimationViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | self.title = @"Emitter Animation Demo"; 26 | self.cellDataArray = [NSMutableArray array]; 27 | [self initCellDates]; 28 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 29 | } 30 | 31 | - (void)didReceiveMemoryWarning { 32 | [super didReceiveMemoryWarning]; 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | - (void)initCellDates 37 | { 38 | [_cellDataArray addObject:@{YSCCellDataName:@"fire animation", YSCCellDataClass:@"YSCFireViewController"}]; 39 | [_cellDataArray addObject:@{YSCCellDataName:@"butterfly animation", YSCCellDataClass:@"YSCButterflyViewController"}]; 40 | } 41 | 42 | #pragma mark - Table view data source 43 | 44 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 45 | 46 | return 1; 47 | } 48 | 49 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 50 | 51 | return _cellDataArray.count; 52 | } 53 | 54 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 55 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 56 | 57 | // Configure the cell... 58 | 59 | NSDictionary *cellData = nil; 60 | if (_cellDataArray.count > indexPath.row) { 61 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 62 | cell.textLabel.text = [cellData objectForKey:YSCCellDataName]; 63 | } 64 | 65 | return cell; 66 | } 67 | 68 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 69 | { 70 | NSDictionary *cellData = nil; 71 | if (_cellDataArray.count > indexPath.row) { 72 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 73 | NSString *className = [cellData objectForKey:YSCCellDataClass]; 74 | Class class = NSClassFromString(className); 75 | if ([class isSubclassOfClass:[UIViewController class]]) { 76 | UIViewController *entranceClassinStance = [[class alloc] init]; 77 | [self.navigationController pushViewController:entranceClassinStance animated:YES]; 78 | } 79 | } 80 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/emitter/YSCFireViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCFireViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/25. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCFireViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/emitter/YSCFireViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCFireViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/25. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCFireViewController.h" 10 | 11 | @interface YSCFireViewController () { 12 | CAEmitterLayer * _fireEmitter;//发射器对象 13 | } 14 | @end 15 | 16 | @implementation YSCFireViewController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | self.view.backgroundColor=[UIColor whiteColor]; 23 | //设置发射器 24 | _fireEmitter=[[CAEmitterLayer alloc] init]; 25 | _fireEmitter.emitterPosition=CGPointMake(self.view.frame.size.width/2,self.view.frame.size.height-20); 26 | _fireEmitter.emitterSize=CGSizeMake(self.view.frame.size.width-100, 20); 27 | _fireEmitter.renderMode = kCAEmitterLayerAdditive; 28 | //发射单元 29 | //火焰 30 | CAEmitterCell *fire = [CAEmitterCell emitterCell]; 31 | fire.birthRate=800; 32 | fire.lifetime=2.0; 33 | fire.lifetimeRange=1.5; 34 | fire.color=[[UIColor colorWithRed:0.8 green:0.4 blue:0.2 alpha:0.1] CGColor]; 35 | fire.contents=(id)[[UIImage imageNamed:@"fire"] CGImage]; 36 | [fire setName:@"fire"]; 37 | 38 | fire.velocity=160; 39 | fire.velocityRange=80; 40 | fire.emissionLongitude=M_PI+M_PI_2; 41 | fire.emissionRange=M_PI_2; 42 | 43 | fire.scaleSpeed=0.3; 44 | fire.spin=0.2; 45 | fire.alphaSpeed = -0.05; 46 | 47 | //烟雾 48 | CAEmitterCell *smoke = [CAEmitterCell emitterCell]; 49 | smoke.birthRate=400; 50 | smoke.lifetime=3.0; 51 | smoke.lifetimeRange=1.5; 52 | smoke.color=[[UIColor colorWithRed:1 green:1 blue:1 alpha:0.05] CGColor]; 53 | smoke.contents=(id)[[UIImage imageNamed:@"fire"] CGImage]; 54 | [smoke setName:@"smoke"]; 55 | 56 | smoke.velocity=250; 57 | smoke.velocityRange=100; 58 | smoke.emissionLongitude=M_PI+M_PI_2; 59 | smoke.emissionRange=M_PI_2; 60 | smoke.alphaSpeed = -0.05; 61 | 62 | _fireEmitter.emitterCells=[NSArray arrayWithObjects:smoke, fire,nil]; 63 | [self.view.layer addSublayer:_fireEmitter]; 64 | 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/heartBeatPulse/YSCHeartBeatPulseViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCHeartBeatPulseViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/11/16. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCHeartBeatPulseViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/heartBeatPulse/YSCHeartBeatPulseViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCHeartBeatPulseViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2016/11/16. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCHeartBeatPulseViewController.h" 10 | #import "YSCHeartBeatPulseView.h" 11 | 12 | @interface YSCHeartBeatPulseViewController () 13 | 14 | @property (nonatomic, strong) YSCHeartBeatPulseView *heartBeatPulseView; 15 | @property (nonatomic, strong) UIButton *startButton; 16 | @property (nonatomic, strong) UIButton *pauseButton; 17 | 18 | @end 19 | 20 | @implementation YSCHeartBeatPulseViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view. 25 | self.view.backgroundColor = [UIColor whiteColor]; 26 | 27 | [self.view addSubview:self.heartBeatPulseView]; 28 | [self.view addSubview:self.startButton]; 29 | [self.view addSubview:self.pauseButton]; 30 | } 31 | 32 | - (void)startHeartBaet:(UIButton *)sender 33 | { 34 | [_heartBeatPulseView startHeartBeat]; 35 | } 36 | 37 | - (void)pauseHeartBaet:(UIButton *)sender 38 | { 39 | [_heartBeatPulseView pauseHeartBeat]; 40 | } 41 | 42 | - (void)viewWillDisappear:(BOOL)animated 43 | { 44 | [_heartBeatPulseView stopHeartBeat]; 45 | [_heartBeatPulseView removeFromSuperview]; 46 | } 47 | 48 | - (void)didReceiveMemoryWarning { 49 | [super didReceiveMemoryWarning]; 50 | // Dispose of any resources that can be recreated. 51 | } 52 | 53 | #pragma mark - getters 54 | 55 | - (YSCHeartBeatPulseView *)heartBeatPulseView 56 | { 57 | if (!_heartBeatPulseView) { 58 | self.heartBeatPulseView = [[YSCHeartBeatPulseView alloc] initWithFrame:CGRectMake(0, 80, self.view.bounds.size.width, 300)]; 59 | [_heartBeatPulseView setHeartBeatSpeed:2]; 60 | } 61 | 62 | return _heartBeatPulseView; 63 | } 64 | 65 | - (UIButton *)startButton 66 | { 67 | if (!_startButton) { 68 | self.startButton = [[UIButton alloc] initWithFrame:CGRectMake(50, 450, 80, 40)]; 69 | [_startButton setTitle:@"start" forState:UIControlStateNormal]; 70 | [_startButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 71 | [_startButton addTarget:self action:@selector(startHeartBaet:) forControlEvents:UIControlEventTouchUpInside]; 72 | [_startButton setBackgroundColor:[UIColor blueColor]]; 73 | } 74 | 75 | return _startButton; 76 | } 77 | 78 | - (UIButton *)pauseButton 79 | { 80 | if (!_pauseButton) { 81 | self.pauseButton = [[UIButton alloc] initWithFrame:CGRectMake(200, 450, 80, 40)]; 82 | [_pauseButton setTitle:@"pause" forState:UIControlStateNormal]; 83 | [_pauseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 84 | [_pauseButton addTarget:self action:@selector(pauseHeartBaet:) forControlEvents:UIControlEventTouchUpInside]; 85 | [_pauseButton setBackgroundColor:[UIColor blueColor]]; 86 | } 87 | 88 | return _pauseButton; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCCircleLoadAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleLoadAnimationViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCCircleLoadAnimationViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCCircleLoadAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleLoadAnimationViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCCircleLoadAnimationViewController.h" 10 | #import "YSCCircleLoadAnimationView.h" 11 | 12 | @interface YSCCircleLoadAnimationViewController () 13 | 14 | @end 15 | 16 | @implementation YSCCircleLoadAnimationViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | 22 | YSCCircleLoadAnimationView *shapeView = [[YSCCircleLoadAnimationView alloc] initWithFrame:self.view.bounds]; 23 | UIImage *image = [UIImage imageNamed:@"tree.jpg"]; 24 | shapeView.loadingImage.image = image; 25 | [self.view addSubview:shapeView]; 26 | [shapeView startLoading]; 27 | } 28 | 29 | - (void)didReceiveMemoryWarning { 30 | [super didReceiveMemoryWarning]; 31 | // Dispose of any resources that can be recreated. 32 | } 33 | 34 | /* 35 | #pragma mark - Navigation 36 | 37 | // In a storyboard-based application, you will often want to do a little preparation before navigation 38 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 39 | // Get the new view controller using [segue destinationViewController]. 40 | // Pass the selected object to the new view controller. 41 | } 42 | */ 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCFanshapedViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCFanshapedViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCFanshapedViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCFanshapedViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCFanshapedViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCFanshapedViewController.h" 10 | #import "YSCFanShapedView.h" 11 | 12 | @interface YSCFanshapedViewController () 13 | 14 | @property (nonatomic, strong) YSCFanShapedView *fanshapedView; 15 | @property (nonatomic, strong) UIButton *fanShapedbutton; 16 | @end 17 | 18 | @implementation YSCFanshapedViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.view.backgroundColor = [UIColor whiteColor]; 24 | 25 | self.fanShapedbutton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 150, 50)]; 26 | _fanShapedbutton.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 + 100); 27 | [_fanShapedbutton setTitle:@"fanshaped expand" forState:UIControlStateNormal]; 28 | _fanShapedbutton.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.5]; 29 | [_fanShapedbutton addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside]; 30 | [self.view addSubview:_fanShapedbutton]; 31 | 32 | [self.fanshapedView showInParentView:self.view WithType:YSCFanShapedShowTypeExpand]; 33 | } 34 | 35 | - (void)didReceiveMemoryWarning { 36 | [super didReceiveMemoryWarning]; 37 | // Dispose of any resources that can be recreated. 38 | } 39 | 40 | - (void)buttonTouched:(UIButton *)sender 41 | { 42 | static NSInteger type = 0; 43 | type++; 44 | if (type % 2 == 0) { 45 | [_fanShapedbutton setTitle:@"fanshaped Shrink" forState:UIControlStateNormal]; 46 | } else { 47 | [_fanShapedbutton setTitle:@"fanshaped expand" forState:UIControlStateNormal]; 48 | } 49 | [self.fanshapedView setShowType:type % 2]; 50 | } 51 | 52 | - (YSCFanShapedView *)fanshapedView 53 | { 54 | if (!_fanshapedView) { 55 | self.fanshapedView = [[YSCFanShapedView alloc] init]; 56 | _fanshapedView.frame = CGRectMake(0, 0, 300, 150); 57 | _fanshapedView.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 - 100); 58 | } 59 | 60 | return _fanshapedView; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCMaskAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMaskAnimationViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/23. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCMaskAnimationViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCMaskAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMaskAnimationViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/23. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCMaskAnimationViewController.h" 10 | 11 | @interface YSCMaskAnimationViewController () 12 | 13 | @property (nonatomic, strong) NSMutableArray *cellDataArray; 14 | @end 15 | 16 | static NSString * const YSCCellDataName = @"YSCCellDataName"; 17 | static NSString * const YSCCellDataClass = @"YSCCellDataClass"; 18 | 19 | @implementation YSCMaskAnimationViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.title = @"Mask Animation Demo"; 25 | self.cellDataArray = [NSMutableArray array]; 26 | [self initCellDates]; 27 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 28 | } 29 | 30 | - (void)didReceiveMemoryWarning { 31 | [super didReceiveMemoryWarning]; 32 | // Dispose of any resources that can be recreated. 33 | } 34 | 35 | - (void)initCellDates 36 | { 37 | [_cellDataArray addObject:@{YSCCellDataName:@"circleLoad animation", YSCCellDataClass:@"YSCCircleLoadAnimationViewController"}]; 38 | [_cellDataArray addObject:@{YSCCellDataName:@"microphoneWave animation", YSCCellDataClass:@"YSCMicrophoneWaveViewController"}]; 39 | [_cellDataArray addObject:@{YSCCellDataName:@"fanshaped animation", YSCCellDataClass:@"YSCFanshapedViewController"}]; 40 | } 41 | 42 | #pragma mark - Table view data source 43 | 44 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 45 | 46 | return 1; 47 | } 48 | 49 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 50 | 51 | return _cellDataArray.count; 52 | } 53 | 54 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 55 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 56 | 57 | // Configure the cell... 58 | 59 | NSDictionary *cellData = nil; 60 | if (_cellDataArray.count > indexPath.row) { 61 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 62 | cell.textLabel.text = [cellData objectForKey:YSCCellDataName]; 63 | } 64 | 65 | return cell; 66 | } 67 | 68 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 69 | { 70 | NSDictionary *cellData = nil; 71 | if (_cellDataArray.count > indexPath.row) { 72 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 73 | NSString *className = [cellData objectForKey:YSCCellDataClass]; 74 | Class class = NSClassFromString(className); 75 | if ([class isSubclassOfClass:[UIViewController class]]) { 76 | UIViewController *entranceClassinStance = [[class alloc] init]; 77 | [self.navigationController pushViewController:entranceClassinStance animated:YES]; 78 | } 79 | } 80 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCMicrophoneWaveViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMicrophoneWaveViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/23. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCMicrophoneWaveViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/maskAnimation/YSCMicrophoneWaveViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCMicrophoneWaveViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/23. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCMicrophoneWaveViewController.h" 10 | #import "YSCMicrophoneWaveView.h" 11 | 12 | @interface YSCMicrophoneWaveViewController () 13 | 14 | @end 15 | 16 | @implementation YSCMicrophoneWaveViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | self.view.backgroundColor = [UIColor whiteColor]; 22 | 23 | YSCMicrophoneWaveView *microphoneWaveView = [[YSCMicrophoneWaveView alloc] init]; 24 | [microphoneWaveView showMicrophoneWaveInParentView:self.view withFrame:self.view.bounds]; 25 | } 26 | 27 | - (void)didReceiveMemoryWarning { 28 | [super didReceiveMemoryWarning]; 29 | // Dispose of any resources that can be recreated. 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/replicator/YSCCircleReplicatorAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleReplicatorAnimationViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCCircleReplicatorAnimationViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/replicator/YSCCircleReplicatorAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleReplicatorAnimationViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCCircleReplicatorAnimationViewController.h" 10 | #import "YSCMatrixCircleAnimationView.h" 11 | 12 | @interface YSCCircleReplicatorAnimationViewController () 13 | 14 | @property (nonatomic, strong) YSCMatrixCircleAnimationView *matrixCircleView; 15 | 16 | @end 17 | 18 | @implementation YSCCircleReplicatorAnimationViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.view.backgroundColor = [UIColor whiteColor]; 24 | 25 | self.matrixCircleView = [[YSCMatrixCircleAnimationView alloc] initWithFrame:CGRectMake(0, 0, 300, 300) xNum:8 yNum:8]; 26 | _matrixCircleView.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0); 27 | 28 | [self.view addSubview:_matrixCircleView]; 29 | } 30 | 31 | - (void)didReceiveMemoryWarning { 32 | [super didReceiveMemoryWarning]; 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/replicator/YSCCircleRippleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleRippleViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCCircleRippleViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/replicator/YSCCircleRippleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCCircleRippleViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCCircleRippleViewController.h" 10 | #import "YSCCircleRippleView.h" 11 | 12 | @interface YSCCircleRippleViewController () 13 | 14 | @property (nonatomic, strong) YSCCircleRippleView *rippleView; 15 | 16 | @end 17 | 18 | @implementation YSCCircleRippleViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.view.backgroundColor = [UIColor whiteColor]; 24 | 25 | self.rippleView = [[YSCCircleRippleView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)]; 26 | _rippleView.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0); 27 | [self.view addSubview:_rippleView]; 28 | _rippleView.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:0.4]; 29 | [_rippleView startAnimation]; 30 | } 31 | 32 | - (void)didReceiveMemoryWarning { 33 | [super didReceiveMemoryWarning]; 34 | // Dispose of any resources that can be recreated. 35 | } 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/replicator/YSCReplicatorAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCReplicatorAnimationViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCReplicatorAnimationViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/replicator/YSCReplicatorAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCReplicatorAnimationViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by 工作号 on 16/8/28. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCReplicatorAnimationViewController.h" 10 | 11 | @interface YSCReplicatorAnimationViewController () 12 | 13 | @property (nonatomic, strong) NSMutableArray *cellDataArray; 14 | @end 15 | 16 | static NSString * const YSCCellDataName = @"YSCCellDataName"; 17 | static NSString * const YSCCellDataClass = @"YSCCellDataClass"; 18 | 19 | @implementation YSCReplicatorAnimationViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.title = @"Replicator Animation Demo"; 25 | self.cellDataArray = [NSMutableArray array]; 26 | [self initCellDates]; 27 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 28 | } 29 | 30 | - (void)didReceiveMemoryWarning { 31 | [super didReceiveMemoryWarning]; 32 | // Dispose of any resources that can be recreated. 33 | } 34 | 35 | - (void)initCellDates 36 | { 37 | [_cellDataArray addObject:@{YSCCellDataName:@"matrix animation", YSCCellDataClass:@"YSCCircleReplicatorAnimationViewController"}]; 38 | [_cellDataArray addObject:@{YSCCellDataName:@"ripple animation", YSCCellDataClass:@"YSCCircleRippleViewController"}]; 39 | } 40 | 41 | #pragma mark - Table view data source 42 | 43 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 44 | 45 | return 1; 46 | } 47 | 48 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 49 | 50 | return _cellDataArray.count; 51 | } 52 | 53 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 54 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 55 | 56 | // Configure the cell... 57 | 58 | NSDictionary *cellData = nil; 59 | if (_cellDataArray.count > indexPath.row) { 60 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 61 | cell.textLabel.text = [cellData objectForKey:YSCCellDataName]; 62 | } 63 | 64 | return cell; 65 | } 66 | 67 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 68 | { 69 | NSDictionary *cellData = nil; 70 | if (_cellDataArray.count > indexPath.row) { 71 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 72 | NSString *className = [cellData objectForKey:YSCCellDataClass]; 73 | Class class = NSClassFromString(className); 74 | if ([class isSubclassOfClass:[UIViewController class]]) { 75 | UIViewController *entranceClassinStance = [[class alloc] init]; 76 | [self.navigationController pushViewController:entranceClassinStance animated:YES]; 77 | } 78 | } 79 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 80 | } 81 | 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/ripple/YSCRippleAnimationViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCRippleAnimationViewController.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCRippleAnimationViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/ripple/YSCRippleAnimationViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCRippleAnimationViewController.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCRippleAnimationViewController.h" 10 | #import "YSCRippleView.h" 11 | 12 | @interface YSCRippleAnimationViewController () 13 | 14 | @property (nonatomic, strong) YSCRippleView *rippleView; 15 | 16 | @end 17 | 18 | @implementation YSCRippleAnimationViewController 19 | 20 | - (void)viewDidLoad 21 | { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | UIButton *lineRippleButton = [[UIButton alloc] initWithFrame:CGRectMake(20 , 320, 150, 50)]; 27 | [lineRippleButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 28 | [lineRippleButton setTitle:@"singleLineRipple" forState:UIControlStateNormal]; 29 | [lineRippleButton addTarget:self action:@selector(lineRippleButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 30 | [self.view addSubview:lineRippleButton]; 31 | 32 | UIButton *ringRippleButton = [[UIButton alloc] initWithFrame:CGRectMake(200 , 320, 150, 50)]; 33 | [ringRippleButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 34 | [ringRippleButton setTitle:@"ringRipple" forState:UIControlStateNormal]; 35 | [ringRippleButton addTarget:self action:@selector(ringRippleButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 36 | [self.view addSubview:ringRippleButton]; 37 | 38 | UIButton *circleRippleButton = [[UIButton alloc] initWithFrame:CGRectMake(20 , 400, 150, 50)]; 39 | [circleRippleButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 40 | [circleRippleButton setTitle:@"circleRipple" forState:UIControlStateNormal]; 41 | [circleRippleButton addTarget:self action:@selector(circleRippleButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 42 | [self.view addSubview:circleRippleButton]; 43 | 44 | UIButton *mixedRippleButton = [[UIButton alloc] initWithFrame:CGRectMake(200 , 400, 150, 50)]; 45 | [mixedRippleButton setBackgroundColor:[[UIColor grayColor] colorWithAlphaComponent:0.5]]; 46 | [mixedRippleButton setTitle:@"mixedRipple" forState:UIControlStateNormal]; 47 | [mixedRippleButton addTarget:self action:@selector(mixedRippleButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 48 | [self.view addSubview:mixedRippleButton]; 49 | } 50 | 51 | - (void)didReceiveMemoryWarning 52 | { 53 | [super didReceiveMemoryWarning]; 54 | // Dispose of any resources that can be recreated. 55 | } 56 | 57 | - (void)lineRippleButtonTouched:(id)sender 58 | { 59 | [self removeSubViews]; 60 | [self.view addSubview:self.rippleView]; 61 | [_rippleView showWithRippleType:YSCRippleTypeLine]; 62 | } 63 | - (void)ringRippleButtonTouched:(id)sender 64 | { 65 | [self removeSubViews]; 66 | [self.view addSubview:self.rippleView]; 67 | [_rippleView showWithRippleType:YSCRippleTypeRing]; 68 | } 69 | - (void)circleRippleButtonTouched:(id)sender 70 | { 71 | [self removeSubViews]; 72 | [self.view addSubview:self.rippleView]; 73 | [_rippleView showWithRippleType:YSCRippleTypeCircle]; 74 | } 75 | - (void)mixedRippleButtonTouched:(id)sender 76 | { 77 | [self removeSubViews]; 78 | [self.view addSubview:self.rippleView]; 79 | [_rippleView showWithRippleType:YSCRippleTypeMixed]; 80 | } 81 | 82 | - (void)removeSubViews 83 | { 84 | if (_rippleView.superview == self.view) { 85 | [_rippleView removeFromParentView]; 86 | } 87 | } 88 | 89 | - (YSCRippleView *)rippleView 90 | { 91 | if (!_rippleView) { 92 | self.rippleView = [[YSCRippleView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 300)]; 93 | } 94 | 95 | return _rippleView; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/seawater/YSCSeaWaterWaveViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCSeaWaterWaveViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCSeaWaterWaveViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/seawater/YSCSeaWaterWaveViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCSeaWaterWaveViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCSeaWaterWaveViewController.h" 10 | #import "YSCSeaGLView.h" 11 | 12 | @interface YSCSeaWaterWaveViewController () 13 | 14 | @property (nonatomic, strong) YSCSeaGLView *seaGLView; 15 | 16 | @end 17 | 18 | @implementation YSCSeaWaterWaveViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.view.backgroundColor = [UIColor whiteColor]; 24 | 25 | self.seaGLView = [[YSCSeaGLView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)]; 26 | [self.view addSubview:_seaGLView]; 27 | 28 | } 29 | 30 | - (void)didReceiveMemoryWarning { 31 | [super didReceiveMemoryWarning]; 32 | // Dispose of any resources that can be recreated. 33 | } 34 | 35 | - (void)viewDidDisappear:(BOOL)animated 36 | { 37 | [_seaGLView removeFromParent]; 38 | _seaGLView = nil; 39 | } 40 | 41 | /* 42 | #pragma mark - Navigation 43 | 44 | // In a storyboard-based application, you will often want to do a little preparation before navigation 45 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 46 | // Get the new view controller using [segue destinationViewController]. 47 | // Pass the selected object to the new view controller. 48 | } 49 | */ 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/tableRefresh/YSCRefreshTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCRefreshTableViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/2/28. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCRefreshTableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/tableRefresh/YSCRefreshTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCRefreshTableViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/2/28. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCRefreshTableViewController.h" 10 | #import "UITableViewController+YSCRefresh.h" 11 | 12 | 13 | @interface YSCRefreshTableViewController () 14 | 15 | @property (nonatomic, assign) NSInteger dataNum; 16 | 17 | @end 18 | 19 | @implementation YSCRefreshTableViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | _dataNum = 10; 25 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"dataCell"]; 26 | } 27 | 28 | - (void)viewDidAppear:(BOOL)animated 29 | { 30 | __weak typeof(self) weakSelf = self; 31 | self.hasRefreshHeader = YES; 32 | [self setHeederRefreshingBlock:^{ 33 | //网络请求数据... 34 | 35 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 36 | NSInteger requestDataNum = 0; 37 | if (weakSelf.dataNum <= 90) { 38 | requestDataNum = 10; 39 | } 40 | weakSelf.dataNum = weakSelf.dataNum + requestDataNum; 41 | [weakSelf.tableView reloadData]; 42 | //请求完成后,修改刷新状态 43 | [weakSelf headerRefreshFinished:MMSCTRefreshStatusSuccess refreshItemsCount:requestDataNum]; 44 | }); 45 | }]; 46 | 47 | self.hasRefreshFooter = YES; 48 | [self setFooterRefreshingBlock:^{ 49 | //网络请求数据... 50 | 51 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 52 | NSInteger requestDataNum = 0; 53 | if (weakSelf.dataNum <= 90) { 54 | requestDataNum = 10; 55 | } 56 | weakSelf.dataNum = weakSelf.dataNum + requestDataNum; 57 | [weakSelf.tableView reloadData]; 58 | //请求完成后,修改刷新状态 59 | [weakSelf footerRefreshFinished:MMSCTRefreshStatusSuccess refreshItemsCount:requestDataNum]; 60 | }); 61 | }]; 62 | } 63 | 64 | - (void)viewDidDisappear:(BOOL)animated 65 | { 66 | //一定要移除Observers,否则会crash 67 | [self removeRefreshHeaderViewObservers]; 68 | [self removeRefreshFooterViewObservers]; 69 | } 70 | 71 | - (void)didReceiveMemoryWarning { 72 | [super didReceiveMemoryWarning]; 73 | // Dispose of any resources that can be recreated. 74 | } 75 | 76 | #pragma mark - Table view data source 77 | 78 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 79 | { 80 | return 1; 81 | } 82 | 83 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 84 | { 85 | return _dataNum; 86 | } 87 | 88 | 89 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 90 | { 91 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"dataCell" forIndexPath:indexPath]; 92 | 93 | // Configure the cell... 94 | cell.textLabel.text = [NSString stringWithFormat:@"第 %ld 行", indexPath.row]; 95 | return cell; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/voice wave/YSCVoiceWaveViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCVoiceWaveViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCVoiceWaveViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/voice wave/YSCVoiceWaveViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCVoiceWaveViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCVoiceWaveViewController.h" 10 | #import 11 | #import "YSCVoiceWaveView.h" 12 | #import "YSCVoiceLoadingCircleView.h" 13 | 14 | #import "YSCNewVoiceWaveView.h" 15 | 16 | @interface YSCVoiceWaveViewController () 17 | 18 | @property (nonatomic, strong) AVAudioRecorder *recorder; 19 | @property (nonatomic, strong) YSCVoiceWaveView *voiceWaveView; 20 | @property (nonatomic,strong) UIView *voiceWaveParentView; 21 | @property (nonatomic, strong) YSCVoiceLoadingCircleView *loadingView; 22 | @property (nonatomic, strong) NSTimer *updateVolumeTimer; 23 | @property (nonatomic, strong) UIButton *voiceWaveShowButton; 24 | 25 | @property (nonatomic, strong) YSCNewVoiceWaveView *voiceWaveViewNew; 26 | @property (nonatomic,strong) UIView *voiceWaveParentViewNew; 27 | @end 28 | 29 | @implementation YSCVoiceWaveViewController 30 | 31 | - (void)dealloc 32 | { 33 | [_voiceWaveView removeFromParent]; 34 | [_loadingView stopLoading]; 35 | _voiceWaveView = nil; 36 | } 37 | 38 | - (void)viewDidLoad { 39 | [super viewDidLoad]; 40 | // Do any additional setup after loading the view. 41 | 42 | [self setupRecorder]; 43 | self.view.backgroundColor = [UIColor whiteColor]; 44 | 45 | [self.view insertSubview:self.voiceWaveParentView atIndex:0]; 46 | [self.voiceWaveView showInParentView:self.voiceWaveParentView]; 47 | [self.voiceWaveView startVoiceWave]; 48 | 49 | [self.view insertSubview:self.voiceWaveParentViewNew atIndex:1]; 50 | [self.voiceWaveViewNew showInParentView:self.voiceWaveParentViewNew]; 51 | [self.voiceWaveViewNew startVoiceWave]; 52 | 53 | [[NSRunLoop currentRunLoop] addTimer:self.updateVolumeTimer forMode:NSRunLoopCommonModes]; 54 | 55 | [self.view addSubview:self.voiceWaveShowButton]; 56 | } 57 | 58 | - (void)didReceiveMemoryWarning { 59 | [super didReceiveMemoryWarning]; 60 | // Dispose of any resources that can be recreated. 61 | } 62 | 63 | - (void)updateVolume:(NSTimer *)timer 64 | { 65 | [self.recorder updateMeters]; 66 | CGFloat normalizedValue = pow (10, [self.recorder averagePowerForChannel:0] / 20); 67 | [_voiceWaveView changeVolume:normalizedValue]; 68 | 69 | [_voiceWaveViewNew changeVolume:normalizedValue]; 70 | } 71 | 72 | - (void)voiceWaveShowButtonTouched:(UIButton *)sender 73 | { 74 | static NSInteger status = 1; 75 | status++; 76 | if (status % 2 == 0) { 77 | [_voiceWaveShowButton setTitle:@"show" forState:UIControlStateNormal]; 78 | [self.voiceWaveView stopVoiceWaveWithShowLoadingViewCallback:^{ 79 | [self.updateVolumeTimer invalidate]; 80 | _updateVolumeTimer = nil; 81 | [self.loadingView startLoadingInParentView:self.view]; 82 | }]; 83 | } else { 84 | [_voiceWaveShowButton setTitle:@"hide" forState:UIControlStateNormal]; 85 | [self.loadingView stopLoading]; 86 | [self.voiceWaveView showInParentView:self.voiceWaveParentView]; 87 | [self.voiceWaveView startVoiceWave]; 88 | [[NSRunLoop currentRunLoop] addTimer:self.updateVolumeTimer forMode:NSRunLoopCommonModes]; 89 | } 90 | } 91 | 92 | -(void)setupRecorder 93 | { 94 | NSURL *url = [NSURL fileURLWithPath:@"/dev/null"]; 95 | NSDictionary *settings = @{AVSampleRateKey: [NSNumber numberWithFloat: 44100.0], 96 | AVFormatIDKey: [NSNumber numberWithInt: kAudioFormatAppleLossless], 97 | AVNumberOfChannelsKey: [NSNumber numberWithInt: 2], 98 | AVEncoderAudioQualityKey: [NSNumber numberWithInt: AVAudioQualityMin]}; 99 | 100 | NSError *error; 101 | self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error]; 102 | if(error) { 103 | NSLog(@"Ups, could not create recorder %@", error); 104 | return; 105 | } 106 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]; 107 | if (error) { 108 | NSLog(@"Error setting category: %@", [error description]); 109 | } 110 | [self.recorder prepareToRecord]; 111 | [self.recorder setMeteringEnabled:YES]; 112 | [self.recorder record]; 113 | } 114 | 115 | #pragma mark - getters 116 | 117 | - (YSCVoiceWaveView *)voiceWaveView 118 | { 119 | if (!_voiceWaveView) { 120 | self.voiceWaveView = [[YSCVoiceWaveView alloc] init]; 121 | } 122 | 123 | return _voiceWaveView; 124 | } 125 | 126 | - (UIView *)voiceWaveParentView 127 | { 128 | if (!_voiceWaveParentView) { 129 | self.voiceWaveParentView = [[UIView alloc] init]; 130 | CGSize screenSize = [UIScreen mainScreen].bounds.size; 131 | _voiceWaveParentView.frame = CGRectMake(0, 0, screenSize.width, 320); 132 | // _voiceWaveParentView.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0); 133 | } 134 | 135 | return _voiceWaveParentView; 136 | } 137 | 138 | - (YSCNewVoiceWaveView *)voiceWaveViewNew 139 | { 140 | if (!_voiceWaveViewNew) { 141 | self.voiceWaveViewNew = [[YSCNewVoiceWaveView alloc] init]; 142 | [_voiceWaveViewNew setVoiceWaveNumber:6]; 143 | } 144 | 145 | return _voiceWaveViewNew; 146 | } 147 | 148 | - (UIView *)voiceWaveParentViewNew 149 | { 150 | if (!_voiceWaveParentViewNew) { 151 | self.voiceWaveParentViewNew = [[UIView alloc] init]; 152 | CGSize screenSize = [UIScreen mainScreen].bounds.size; 153 | _voiceWaveParentViewNew.frame = CGRectMake(0, 330, screenSize.width, 320); 154 | // _voiceWaveParentViewNew.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0); 155 | } 156 | 157 | return _voiceWaveParentViewNew; 158 | } 159 | 160 | - (YSCVoiceLoadingCircleView *)loadingView 161 | { 162 | if (!_loadingView) { 163 | CGSize screenSize = [UIScreen mainScreen].bounds.size; 164 | CGPoint loadViewCenter = CGPointMake(screenSize.width / 2.0, 160); 165 | self.loadingView = [[YSCVoiceLoadingCircleView alloc] initWithCircleRadius:25 center:loadViewCenter]; 166 | } 167 | 168 | return _loadingView; 169 | } 170 | 171 | - (UIButton *)voiceWaveShowButton 172 | { 173 | if (!_voiceWaveShowButton) { 174 | self.voiceWaveShowButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 150, 50)]; 175 | _voiceWaveShowButton.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 + 200); 176 | [_voiceWaveShowButton setTitle:@"hide" forState:UIControlStateNormal]; 177 | _voiceWaveShowButton.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.5]; 178 | [_voiceWaveShowButton addTarget:self action:@selector(voiceWaveShowButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 179 | } 180 | 181 | return _voiceWaveShowButton; 182 | } 183 | 184 | - (NSTimer *)updateVolumeTimer 185 | { 186 | if (!_updateVolumeTimer) { 187 | self.updateVolumeTimer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(updateVolume:) userInfo:nil repeats:YES]; 188 | } 189 | 190 | return _updateVolumeTimer; 191 | } 192 | 193 | @end 194 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/water wave/YSCWaterWaveViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaterWaveViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCWaterWaveViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Demo controllers/water wave/YSCWaterWaveViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCWaterWaveViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/24. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCWaterWaveViewController.h" 10 | #import "YSCWaterWaveView.h" 11 | 12 | @interface YSCWaterWaveViewController () 13 | 14 | @property (nonatomic, strong) YSCWaterWaveView *waterWave; 15 | @property (nonatomic, strong) UIButton *waterWaveShowButton; 16 | 17 | @end 18 | 19 | @implementation YSCWaterWaveViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | [self.view addSubview:self.waterWave]; 27 | [self.view addSubview:self.waterWaveShowButton]; 28 | [self.waterWave startWave]; 29 | } 30 | 31 | - (void)didReceiveMemoryWarning { 32 | [super didReceiveMemoryWarning]; 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | - (void)waterWaveShowButtonTouched:(UIButton *)sender 37 | { 38 | static NSInteger status = 1; 39 | status++; 40 | if (status % 2 == 0) { 41 | [_waterWaveShowButton setTitle:@"show" forState:UIControlStateNormal]; 42 | [self.waterWave stopWave]; 43 | } else { 44 | [_waterWaveShowButton setTitle:@"hide" forState:UIControlStateNormal]; 45 | [self.view addSubview:self.waterWave]; 46 | [self.waterWave startWave]; 47 | } 48 | } 49 | 50 | - (YSCWaterWaveView *)waterWave 51 | { 52 | if (!_waterWave) { 53 | self.waterWave = [[YSCWaterWaveView alloc] init]; 54 | _waterWave.frame = CGRectMake(0, 0, self.view.bounds.size.width, 300); 55 | _waterWave.percent = 0.6; 56 | _waterWave.firstWaveColor = [UIColor colorWithRed:146/255.0 green:148/255.0 blue:216/255.0 alpha:1.0]; 57 | _waterWave.secondWaveColor = [UIColor colorWithRed:84/255.0 green:87/255.0 blue:197/255.0 alpha:1.0]; 58 | } 59 | 60 | return _waterWave; 61 | } 62 | 63 | - (UIButton *)waterWaveShowButton 64 | { 65 | if (!_waterWaveShowButton) { 66 | self.waterWaveShowButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 150, 50)]; 67 | _waterWaveShowButton.center = CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 + 200); 68 | [_waterWaveShowButton setTitle:@"hide" forState:UIControlStateNormal]; 69 | _waterWaveShowButton.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.5]; 70 | [_waterWaveShowButton addTarget:self action:@selector(waterWaveShowButtonTouched:) forControlEvents:UIControlEventTouchUpInside]; 71 | } 72 | 73 | return _waterWaveShowButton; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSMicrophoneUsageDescription 26 | 要在应用中使用语音功能,请点击“好”。 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Resource/butterfly1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Resource/butterfly1.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Resource/butterfly2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Resource/butterfly2.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Resource/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Resource/fire.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Resource/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Resource/music.png -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Resource/sw3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Resource/sw3.gif -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/Resource/tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fendouzhe/voice-Wave-CAReplicatorLayer-Animation/b3de30052b1d01d893494d51871c78eababd9cb3/YSCAnimationDemo/YSCAnimationDemo/Resource/tree.jpg -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UINavigationController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "YSCAnimationDemoViewController.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | 22 | YSCAnimationDemoViewController *demoMainViewController = [[YSCAnimationDemoViewController alloc] init]; 23 | [self pushViewController:demoMainViewController animated:NO]; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/YSCAnimationDemoPrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // YSCAnimationDemoPrefixHeader.pch 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/3/1. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #ifndef YSCAnimationDemoPrefixHeader_pch 10 | #define YSCAnimationDemoPrefixHeader_pch 11 | 12 | // Include any system framework and library headers here that should be included in all compilation units. 13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 14 | 15 | #endif /* YSCAnimationDemoPrefixHeader_pch */ 16 | 17 | 18 | #import "YSCGlobleMethod.h" 19 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/YSCAnimationDemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCAnimationDemoViewController.h 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCAnimationDemoViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/YSCAnimationDemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCAnimationDemoViewController.m 3 | // AnimationLearn 4 | // 5 | // Created by yushichao on 16/8/19. 6 | // Copyright © 2016年 yushichao. All rights reserved. 7 | // 8 | 9 | #import "YSCAnimationDemoViewController.h" 10 | 11 | @interface YSCAnimationDemoViewController () 12 | 13 | @property (nonatomic, strong) NSMutableArray *cellDataArray; 14 | @end 15 | 16 | static NSString * const YSCCellDataName = @"YSCCellDataName"; 17 | static NSString * const YSCCellDataClass = @"YSCCellDataClass"; 18 | 19 | @implementation YSCAnimationDemoViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | self.title = @"Animation Demo"; 25 | self.cellDataArray = [NSMutableArray array]; 26 | [self initCellDates]; 27 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 28 | } 29 | 30 | 31 | - (void)initCellDates 32 | { 33 | [_cellDataArray addObject:@{YSCCellDataName:@"Layer animation", YSCCellDataClass:@"YSCLayerAnimationViewController"}]; 34 | [_cellDataArray addObject:@{YSCCellDataName:@"heart beat animation", YSCCellDataClass:@"YSCHeartBeatPulseViewController"}]; 35 | [_cellDataArray addObject:@{YSCCellDataName:@"ripple animation", YSCCellDataClass:@"YSCRippleAnimationViewController"}]; 36 | [_cellDataArray addObject:@{YSCCellDataName:@"wave animation", YSCCellDataClass:@"YSCWaveAnimationViewController"}]; 37 | [_cellDataArray addObject:@{YSCCellDataName:@"mask animation", YSCCellDataClass:@"YSCMaskAnimationViewController"}]; 38 | [_cellDataArray addObject:@{YSCCellDataName:@"voiceWave animation", YSCCellDataClass:@"YSCVoiceWaveViewController"}]; 39 | [_cellDataArray addObject:@{YSCCellDataName:@"waterWave animation", YSCCellDataClass:@"YSCWaterWaveViewController"}]; 40 | [_cellDataArray addObject:@{YSCCellDataName:@"seawaterWave animation", YSCCellDataClass:@"YSCSeaWaterWaveViewController"}]; 41 | [_cellDataArray addObject:@{YSCCellDataName:@"emitter animation", YSCCellDataClass:@"YSCEmitterAnimationViewController"}]; 42 | [_cellDataArray addObject:@{YSCCellDataName:@"replicator animation", YSCCellDataClass:@"YSCReplicatorAnimationViewController"}]; 43 | [_cellDataArray addObject:@{YSCCellDataName:@"loadGif animation", YSCCellDataClass:@"YSCloadGifViewController"}]; 44 | 45 | //add 2017/03/01 46 | [_cellDataArray addObject:@{YSCCellDataName:@"carouselTitle animation", YSCCellDataClass:@"YSCCarouselTitleViewController"}]; 47 | [_cellDataArray addObject:@{YSCCellDataName:@"refreshTable animation", YSCCellDataClass:@"YSCRefreshTableViewController"}]; 48 | } 49 | 50 | #pragma mark - Table view data source 51 | 52 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 53 | 54 | return 1; 55 | } 56 | 57 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 58 | 59 | return _cellDataArray.count; 60 | } 61 | 62 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 63 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; 64 | 65 | // Configure the cell... 66 | 67 | NSDictionary *cellData = nil; 68 | if (_cellDataArray.count > indexPath.row) { 69 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 70 | cell.textLabel.text = [cellData objectForKey:YSCCellDataName]; 71 | } 72 | 73 | return cell; 74 | } 75 | 76 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 77 | { 78 | NSDictionary *cellData = nil; 79 | if (_cellDataArray.count > indexPath.row) { 80 | cellData = [_cellDataArray objectAtIndex:indexPath.row]; 81 | NSString *className = [cellData objectForKey:YSCCellDataClass]; 82 | Class class = NSClassFromString(className); 83 | if ([class isSubclassOfClass:[UIViewController class]]) { 84 | UIViewController *entranceClassinStance = [[class alloc] init]; 85 | [self.navigationController pushViewController:entranceClassinStance animated:YES]; 86 | } 87 | } 88 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/YSCGlobleMethod.h: -------------------------------------------------------------------------------- 1 | // 2 | // YSCGlobleMethod.h 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/2/28. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCGlobleMethod : NSObject 12 | 13 | + (UIColor *)uiColorFromHexString:(NSString *)hexString; 14 | + (CGFloat)getStringWidth:(NSString *)string withRect:(CGRect)rect attributes:(NSDictionary *)attribute; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/YSCGlobleMethod.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCGlobleMethod.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 2017/2/28. 6 | // Copyright © 2017年 YSC. All rights reserved. 7 | // 8 | 9 | #import "YSCGlobleMethod.h" 10 | 11 | @implementation YSCGlobleMethod 12 | 13 | /** 14 | * @desc 从十六进制字符串,生成uicolor 15 | * @param hexString 格式:#ffffff or 0xffffff or 0Xffffff or ffffff 16 | */ 17 | + (UIColor *)uiColorFromHexString:(NSString *)hexString 18 | { 19 | if (!(hexString.length > 0)) { 20 | return nil; 21 | } 22 | NSString *rgbString = nil; 23 | if (hexString.length < 6) { 24 | return nil; 25 | } else if (hexString.length == 6) { //ffffff 26 | rgbString = hexString; 27 | } else if (hexString.length == 7 && [hexString hasPrefix:@"#"]) { //#ffffff 28 | rgbString = [hexString substringFromIndex:1]; 29 | } else if (hexString.length == 8 && ([hexString hasPrefix:@"0x"] || [hexString hasPrefix:@"0X"])) {//0xffffff 0Xffffff 30 | rgbString = [hexString substringFromIndex:2]; 31 | } else { 32 | return nil; 33 | } 34 | // 35 | unsigned int rValue = 0; 36 | unsigned int gValue = 0; 37 | unsigned int bValue = 0; 38 | NSRange range; 39 | range.length = 2; 40 | //r 41 | range.location = 0; 42 | NSScanner *rScanner = [NSScanner scannerWithString:[rgbString substringWithRange:range]]; 43 | [rScanner scanHexInt:&rValue]; 44 | //g 45 | range.location = 2; 46 | NSScanner *gScanner = [NSScanner scannerWithString:[rgbString substringWithRange:range]]; 47 | [gScanner scanHexInt:&gValue]; 48 | //b 49 | range.location = 4; 50 | NSScanner *bScanner = [NSScanner scannerWithString:[rgbString substringWithRange:range]]; 51 | [bScanner scanHexInt:&bValue]; 52 | 53 | UIColor *color = [UIColor colorWithRed:rValue/255.0 green:gValue/255.0 blue:bValue/255.0 alpha:1]; 54 | return color; 55 | } 56 | 57 | + (CGFloat)getStringWidth:(NSString *)string withRect:(CGRect)rect attributes:(NSDictionary *)attribute 58 | { 59 | if (!(string.length > 0)) { 60 | return 0.0; 61 | } 62 | CGSize textSize = CGSizeZero; 63 | if ([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { 64 | textSize = [string boundingRectWithSize:CGSizeMake(CGFLOAT_MAX , rect.size.height) 65 | options:NSStringDrawingUsesLineFragmentOrigin 66 | attributes:attribute 67 | context:nil].size; 68 | } else { 69 | textSize = [string sizeWithFont:attribute[NSFontAttributeName] 70 | constrainedToSize:CGSizeMake(CGFLOAT_MAX, rect.size.height) 71 | lineBreakMode:NSLineBreakByWordWrapping]; 72 | } 73 | return textSize.width; 74 | } 75 | 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // YSCAnimationDemo 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemoTests/YSCAnimationDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCAnimationDemoTests.m 3 | // YSCAnimationDemoTests 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCAnimationDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation YSCAnimationDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /YSCAnimationDemo/YSCAnimationDemoUITests/YSCAnimationDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YSCAnimationDemoUITests.m 3 | // YSCAnimationDemoUITests 4 | // 5 | // Created by yushichao on 16/8/22. 6 | // Copyright © 2016年 YSC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YSCAnimationDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation YSCAnimationDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------