YYAnimatedImageView.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. //
  2. // YYAnimatedImageView.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 14/10/19.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYAnimatedImageView.h"
  12. #import "YYImageCoder.h"
  13. #import <pthread.h>
  14. #import <libkern/OSAtomic.h>
  15. #import <mach/mach.h>
  16. #define BUFFER_SIZE (10 * 1024 * 1024) // 10MB (minimum memory buffer size)
  17. #define LOCK(...) OSSpinLockLock(&self->_lock); \
  18. __VA_ARGS__; \
  19. OSSpinLockUnlock(&self->_lock);
  20. #define LOCK_VIEW(...) OSSpinLockLock(&view->_lock); \
  21. __VA_ARGS__; \
  22. OSSpinLockUnlock(&view->_lock);
  23. static int64_t _YYDeviceMemoryTotal() {
  24. int64_t mem = [[NSProcessInfo processInfo] physicalMemory];
  25. if (mem < -1) mem = -1;
  26. return mem;
  27. }
  28. static int64_t _YYDeviceMemoryFree() {
  29. mach_port_t host_port = mach_host_self();
  30. mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
  31. vm_size_t page_size;
  32. vm_statistics_data_t vm_stat;
  33. kern_return_t kern;
  34. kern = host_page_size(host_port, &page_size);
  35. if (kern != KERN_SUCCESS) return -1;
  36. kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
  37. if (kern != KERN_SUCCESS) return -1;
  38. return vm_stat.free_count * page_size;
  39. }
  40. /**
  41. A proxy used to hold a weak object.
  42. It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink.
  43. */
  44. @interface _YYImageWeakProxy : NSProxy
  45. @property (nonatomic, weak, readonly) id target;
  46. - (instancetype)initWithTarget:(id)target;
  47. + (instancetype)proxyWithTarget:(id)target;
  48. @end
  49. @implementation _YYImageWeakProxy
  50. - (instancetype)initWithTarget:(id)target {
  51. _target = target;
  52. return self;
  53. }
  54. + (instancetype)proxyWithTarget:(id)target {
  55. return [[_YYImageWeakProxy alloc] initWithTarget:target];
  56. }
  57. - (id)forwardingTargetForSelector:(SEL)selector {
  58. return _target;
  59. }
  60. - (void)forwardInvocation:(NSInvocation *)invocation {
  61. void *null = NULL;
  62. [invocation setReturnValue:&null];
  63. }
  64. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
  65. return [NSObject instanceMethodSignatureForSelector:@selector(init)];
  66. }
  67. - (BOOL)respondsToSelector:(SEL)aSelector {
  68. return [_target respondsToSelector:aSelector];
  69. }
  70. - (BOOL)isEqual:(id)object {
  71. return [_target isEqual:object];
  72. }
  73. - (NSUInteger)hash {
  74. return [_target hash];
  75. }
  76. - (Class)superclass {
  77. return [_target superclass];
  78. }
  79. - (Class)class {
  80. return [_target class];
  81. }
  82. - (BOOL)isKindOfClass:(Class)aClass {
  83. return [_target isKindOfClass:aClass];
  84. }
  85. - (BOOL)isMemberOfClass:(Class)aClass {
  86. return [_target isMemberOfClass:aClass];
  87. }
  88. - (BOOL)conformsToProtocol:(Protocol *)aProtocol {
  89. return [_target conformsToProtocol:aProtocol];
  90. }
  91. - (BOOL)isProxy {
  92. return YES;
  93. }
  94. - (NSString *)description {
  95. return [_target description];
  96. }
  97. - (NSString *)debugDescription {
  98. return [_target debugDescription];
  99. }
  100. @end
  101. typedef NS_ENUM(NSUInteger, YYAnimagedImageType) {
  102. YYAnimagedImageTypeNone = 0,
  103. YYAnimagedImageTypeImage,
  104. YYAnimagedImageTypeHighlightedImage,
  105. YYAnimagedImageTypeImages,
  106. YYAnimagedImageTypeHighlightedImages,
  107. };
  108. @interface YYAnimatedImageView() {
  109. @package
  110. UIImage <YYAnimatedImage> *_curAnimatedImage;
  111. dispatch_once_t _onceToken;
  112. OSSpinLock _lock; ///< lock for _buffer
  113. NSOperationQueue *_requestQueue; ///< image request queue, serial
  114. CADisplayLink *_link; ///< ticker for change frame
  115. NSTimeInterval _time; ///< time after last frame
  116. UIImage *_curFrame; ///< current frame to display
  117. NSUInteger _curIndex; ///< current frame index (from 0)
  118. NSUInteger _totalFrameCount; ///< total frame count
  119. BOOL _loopEnd; ///< weather the loop is end.
  120. NSUInteger _curLoop; ///< current loop count (from 0)
  121. NSUInteger _totalLoop; ///< total loop count, 0 means infinity
  122. NSMutableDictionary *_buffer; ///< frame buffer
  123. BOOL _bufferMiss; ///< whether miss frame on last opportunity
  124. NSUInteger _maxBufferCount; ///< maximum buffer count
  125. NSInteger _incrBufferCount; ///< current allowed buffer count (will increase by step)
  126. CGRect _curContentsRect;
  127. BOOL _curImageHasContentsRect; ///< image has implementated "animatedImageContentsRectAtIndex:"
  128. }
  129. @property (nonatomic, readwrite) BOOL currentIsPlayingAnimation;
  130. - (void)calcMaxBufferCount;
  131. @end
  132. /// An operation for image fetch
  133. @interface _YYAnimatedImageViewFetchOperation : NSOperation
  134. @property (nonatomic, weak) YYAnimatedImageView *view;
  135. @property (nonatomic, assign) NSUInteger nextIndex;
  136. @property (nonatomic, strong) UIImage <YYAnimatedImage> *curImage;
  137. @end
  138. @implementation _YYAnimatedImageViewFetchOperation
  139. - (void)main {
  140. __strong YYAnimatedImageView *view = _view;
  141. if (!view) return;
  142. if ([self isCancelled]) return;
  143. view->_incrBufferCount++;
  144. if (view->_incrBufferCount == 0) [view calcMaxBufferCount];
  145. if ((int)view->_incrBufferCount > (int)view->_maxBufferCount) {
  146. view->_incrBufferCount = view->_maxBufferCount;
  147. }
  148. NSUInteger idx = _nextIndex;
  149. NSUInteger max = view->_incrBufferCount < 1 ? 1 : view->_incrBufferCount;
  150. NSUInteger total = view->_totalFrameCount;
  151. for (int i = 0; i < max; i++, idx++) {
  152. @autoreleasepool {
  153. if (idx >= total) idx = 0;
  154. if ([self isCancelled]) break;
  155. LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
  156. if (miss) {
  157. UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
  158. img = [img yy_imageByDecoded];
  159. if ([self isCancelled]) break;
  160. LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
  161. }
  162. }
  163. }
  164. }
  165. @end
  166. @implementation YYAnimatedImageView
  167. - (instancetype)init {
  168. self = [super init];
  169. _runloopMode = NSRunLoopCommonModes;
  170. _autoPlayAnimatedImage = YES;
  171. return self;
  172. }
  173. - (instancetype)initWithImage:(UIImage *)image {
  174. self = [super init];
  175. _runloopMode = NSRunLoopCommonModes;
  176. _autoPlayAnimatedImage = YES;
  177. self.frame = (CGRect) {CGPointZero, image.size };
  178. self.image = image;
  179. return self;
  180. }
  181. - (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage {
  182. self = [super init];
  183. _runloopMode = NSRunLoopCommonModes;
  184. _autoPlayAnimatedImage = YES;
  185. CGSize size = image ? image.size : highlightedImage.size;
  186. self.frame = (CGRect) {CGPointZero, size };
  187. self.image = image;
  188. self.highlightedImage = highlightedImage;
  189. return self;
  190. }
  191. // init the animated params.
  192. - (void)resetAnimated {
  193. dispatch_once(&_onceToken, ^{
  194. _lock = OS_SPINLOCK_INIT;
  195. _buffer = [NSMutableDictionary new];
  196. _requestQueue = [[NSOperationQueue alloc] init];
  197. _requestQueue.maxConcurrentOperationCount = 1;
  198. _link = [CADisplayLink displayLinkWithTarget:[_YYImageWeakProxy proxyWithTarget:self] selector:@selector(step:)];
  199. if (_runloopMode) {
  200. [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
  201. }
  202. _link.paused = YES;
  203. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  204. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
  205. });
  206. [_requestQueue cancelAllOperations];
  207. LOCK(
  208. if (_buffer.count) {
  209. NSMutableDictionary *holder = _buffer;
  210. _buffer = [NSMutableDictionary new];
  211. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  212. // Capture the dictionary to global queue,
  213. // release these images in background to avoid blocking UI thread.
  214. [holder class];
  215. });
  216. }
  217. );
  218. _link.paused = YES;
  219. _time = 0;
  220. if (_curIndex != 0) {
  221. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  222. _curIndex = 0;
  223. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  224. }
  225. _curAnimatedImage = nil;
  226. _curFrame = nil;
  227. _curLoop = 0;
  228. _totalLoop = 0;
  229. _totalFrameCount = 1;
  230. _loopEnd = NO;
  231. _bufferMiss = NO;
  232. _incrBufferCount = 0;
  233. }
  234. - (void)setImage:(UIImage *)image {
  235. if (self.image == image) return;
  236. [self setImage:image withType:YYAnimagedImageTypeImage];
  237. }
  238. - (void)setHighlightedImage:(UIImage *)highlightedImage {
  239. if (self.highlightedImage == highlightedImage) return;
  240. [self setImage:highlightedImage withType:YYAnimagedImageTypeHighlightedImage];
  241. }
  242. - (void)setAnimationImages:(NSArray *)animationImages {
  243. if (self.animationImages == animationImages) return;
  244. [self setImage:animationImages withType:YYAnimagedImageTypeImages];
  245. }
  246. - (void)setHighlightedAnimationImages:(NSArray *)highlightedAnimationImages {
  247. if (self.highlightedAnimationImages == highlightedAnimationImages) return;
  248. [self setImage:highlightedAnimationImages withType:YYAnimagedImageTypeHighlightedImages];
  249. }
  250. - (void)setHighlighted:(BOOL)highlighted {
  251. [super setHighlighted:highlighted];
  252. if (_link) [self resetAnimated];
  253. [self imageChanged];
  254. }
  255. - (id)imageForType:(YYAnimagedImageType)type {
  256. switch (type) {
  257. case YYAnimagedImageTypeNone: return nil;
  258. case YYAnimagedImageTypeImage: return self.image;
  259. case YYAnimagedImageTypeHighlightedImage: return self.highlightedImage;
  260. case YYAnimagedImageTypeImages: return self.animationImages;
  261. case YYAnimagedImageTypeHighlightedImages: return self.highlightedAnimationImages;
  262. }
  263. return nil;
  264. }
  265. - (YYAnimagedImageType)currentImageType {
  266. YYAnimagedImageType curType = YYAnimagedImageTypeNone;
  267. if (self.highlighted) {
  268. if (self.highlightedAnimationImages.count) curType = YYAnimagedImageTypeHighlightedImages;
  269. else if (self.highlightedImage) curType = YYAnimagedImageTypeHighlightedImage;
  270. }
  271. if (curType == YYAnimagedImageTypeNone) {
  272. if (self.animationImages.count) curType = YYAnimagedImageTypeImages;
  273. else if (self.image) curType = YYAnimagedImageTypeImage;
  274. }
  275. return curType;
  276. }
  277. - (void)setImage:(id)image withType:(YYAnimagedImageType)type {
  278. [self stopAnimating];
  279. if (_link) [self resetAnimated];
  280. _curFrame = nil;
  281. switch (type) {
  282. case YYAnimagedImageTypeNone: break;
  283. case YYAnimagedImageTypeImage: super.image = image; break;
  284. case YYAnimagedImageTypeHighlightedImage: super.highlightedImage = image; break;
  285. case YYAnimagedImageTypeImages: super.animationImages = image; break;
  286. case YYAnimagedImageTypeHighlightedImages: super.highlightedAnimationImages = image; break;
  287. }
  288. [self imageChanged];
  289. }
  290. - (void)imageChanged {
  291. YYAnimagedImageType newType = [self currentImageType];
  292. id newVisibleImage = [self imageForType:newType];
  293. NSUInteger newImageFrameCount = 0;
  294. BOOL hasContentsRect = NO;
  295. if ([newVisibleImage isKindOfClass:[UIImage class]] &&
  296. [newVisibleImage conformsToProtocol:@protocol(YYAnimatedImage)]) {
  297. newImageFrameCount = ((UIImage<YYAnimatedImage> *) newVisibleImage).animatedImageFrameCount;
  298. if (newImageFrameCount > 1) {
  299. hasContentsRect = [((UIImage<YYAnimatedImage> *) newVisibleImage) respondsToSelector:@selector(animatedImageContentsRectAtIndex:)];
  300. }
  301. }
  302. if (!hasContentsRect && _curImageHasContentsRect) {
  303. if (!CGRectEqualToRect(self.layer.contentsRect, CGRectMake(0, 0, 1, 1)) ) {
  304. [CATransaction begin];
  305. [CATransaction setDisableActions:YES];
  306. self.layer.contentsRect = CGRectMake(0, 0, 1, 1);
  307. [CATransaction commit];
  308. }
  309. }
  310. _curImageHasContentsRect = hasContentsRect;
  311. if (hasContentsRect) {
  312. CGRect rect = [((UIImage<YYAnimatedImage> *) newVisibleImage) animatedImageContentsRectAtIndex:0];
  313. [self setContentsRect:rect forImage:newVisibleImage];
  314. }
  315. if (newImageFrameCount > 1) {
  316. [self resetAnimated];
  317. _curAnimatedImage = newVisibleImage;
  318. _curFrame = newVisibleImage;
  319. _totalLoop = _curAnimatedImage.animatedImageLoopCount;
  320. _totalFrameCount = _curAnimatedImage.animatedImageFrameCount;
  321. [self calcMaxBufferCount];
  322. }
  323. [self setNeedsDisplay];
  324. [self didMoved];
  325. }
  326. // dynamically adjust buffer size for current memory.
  327. - (void)calcMaxBufferCount {
  328. NSUInteger bytes = _curAnimatedImage.animatedImageBytesPerFrame;
  329. if (bytes == 0) bytes = 1;
  330. int64_t total = _YYDeviceMemoryTotal();
  331. int64_t free = _YYDeviceMemoryFree();
  332. int64_t max = MIN(total * 0.2, free * 0.6);
  333. max = MAX(max, BUFFER_SIZE);
  334. if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
  335. _maxBufferCount = (float)max / (float)bytes;
  336. if (_maxBufferCount == 0) _maxBufferCount = 1;
  337. }
  338. - (void)dealloc {
  339. [_requestQueue cancelAllOperations];
  340. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  341. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
  342. [_link invalidate];
  343. }
  344. - (BOOL)isAnimating {
  345. return self.currentIsPlayingAnimation;
  346. }
  347. - (void)stopAnimating {
  348. [super stopAnimating];
  349. _link.paused = YES;
  350. self.currentIsPlayingAnimation = NO;
  351. }
  352. - (void)startAnimating {
  353. YYAnimagedImageType type = [self currentImageType];
  354. if (type == YYAnimagedImageTypeImages || type == YYAnimagedImageTypeHighlightedImages) {
  355. NSArray *images = [self imageForType:type];
  356. if (images.count > 0) {
  357. [super startAnimating];
  358. self.currentIsPlayingAnimation = YES;
  359. }
  360. } else {
  361. if (_curAnimatedImage && _link.paused) {
  362. _curLoop = 0;
  363. _loopEnd = NO;
  364. _link.paused = NO;
  365. self.currentIsPlayingAnimation = YES;
  366. }
  367. }
  368. }
  369. - (void)didReceiveMemoryWarning:(NSNotification *)notification {
  370. [_requestQueue cancelAllOperations];
  371. [_requestQueue addOperationWithBlock: ^{
  372. _incrBufferCount = -60 - (int)(arc4random() % 120); // about 1~3 seconds to grow back..
  373. NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
  374. LOCK(
  375. NSArray * keys = _buffer.allKeys;
  376. for (NSNumber * key in keys) {
  377. if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
  378. [_buffer removeObjectForKey:key];
  379. }
  380. }
  381. )//LOCK
  382. }];
  383. }
  384. - (void)didEnterBackground:(NSNotification *)notification {
  385. [_requestQueue cancelAllOperations];
  386. NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
  387. LOCK(
  388. NSArray * keys = _buffer.allKeys;
  389. for (NSNumber * key in keys) {
  390. if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
  391. [_buffer removeObjectForKey:key];
  392. }
  393. }
  394. )//LOCK
  395. }
  396. - (void)step:(CADisplayLink *)link {
  397. UIImage <YYAnimatedImage> *image = _curAnimatedImage;
  398. NSMutableDictionary *buffer = _buffer;
  399. UIImage *bufferedImage = nil;
  400. NSUInteger nextIndex = (_curIndex + 1) % _totalFrameCount;
  401. BOOL bufferIsFull = NO;
  402. if (!image) return;
  403. if (_loopEnd) { // view will keep in last frame
  404. [self stopAnimating];
  405. return;
  406. }
  407. NSTimeInterval delay = 0;
  408. if (!_bufferMiss) {
  409. _time += link.duration;
  410. delay = [image animatedImageDurationAtIndex:_curIndex];
  411. if (_time < delay) return;
  412. _time -= delay;
  413. if (nextIndex == 0) {
  414. _curLoop++;
  415. if (_curLoop >= _totalLoop && _totalLoop != 0) {
  416. _loopEnd = YES;
  417. [self stopAnimating];
  418. [self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
  419. return; // stop at last frame
  420. }
  421. }
  422. delay = [image animatedImageDurationAtIndex:nextIndex];
  423. if (_time > delay) _time = delay; // do not jump over frame
  424. }
  425. LOCK(
  426. bufferedImage = buffer[@(nextIndex)];
  427. if (bufferedImage) {
  428. if ((int)_incrBufferCount < _totalFrameCount) {
  429. [buffer removeObjectForKey:@(nextIndex)];
  430. }
  431. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  432. _curIndex = nextIndex;
  433. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  434. _curFrame = bufferedImage == (id)[NSNull null] ? nil : bufferedImage;
  435. if (_curImageHasContentsRect) {
  436. _curContentsRect = [image animatedImageContentsRectAtIndex:_curIndex];
  437. [self setContentsRect:_curContentsRect forImage:_curFrame];
  438. }
  439. nextIndex = (_curIndex + 1) % _totalFrameCount;
  440. _bufferMiss = NO;
  441. if (buffer.count == _totalFrameCount) {
  442. bufferIsFull = YES;
  443. }
  444. } else {
  445. _bufferMiss = YES;
  446. }
  447. )//LOCK
  448. if (!_bufferMiss) {
  449. [self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
  450. }
  451. if (!bufferIsFull && _requestQueue.operationCount == 0) { // if some work not finished, wait for next opportunity
  452. _YYAnimatedImageViewFetchOperation *operation = [_YYAnimatedImageViewFetchOperation new];
  453. operation.view = self;
  454. operation.nextIndex = nextIndex;
  455. operation.curImage = image;
  456. [_requestQueue addOperation:operation];
  457. }
  458. }
  459. - (void)displayLayer:(CALayer *)layer {
  460. if (_curFrame) {
  461. layer.contents = (__bridge id)_curFrame.CGImage;
  462. }
  463. }
  464. - (void)setContentsRect:(CGRect)rect forImage:(UIImage *)image{
  465. CGRect layerRect = CGRectMake(0, 0, 1, 1);
  466. if (image) {
  467. CGSize imageSize = image.size;
  468. if (imageSize.width > 0.01 && imageSize.height > 0.01) {
  469. layerRect.origin.x = rect.origin.x / imageSize.width;
  470. layerRect.origin.y = rect.origin.y / imageSize.height;
  471. layerRect.size.width = rect.size.width / imageSize.width;
  472. layerRect.size.height = rect.size.height / imageSize.height;
  473. layerRect = CGRectIntersection(layerRect, CGRectMake(0, 0, 1, 1));
  474. if (CGRectIsNull(layerRect) || CGRectIsEmpty(layerRect)) {
  475. layerRect = CGRectMake(0, 0, 1, 1);
  476. }
  477. }
  478. }
  479. [CATransaction begin];
  480. [CATransaction setDisableActions:YES];
  481. self.layer.contentsRect = layerRect;
  482. [CATransaction commit];
  483. }
  484. - (void)didMoved {
  485. if (self.autoPlayAnimatedImage) {
  486. if(self.superview && self.window) {
  487. [self startAnimating];
  488. } else {
  489. [self stopAnimating];
  490. }
  491. }
  492. }
  493. - (void)didMoveToWindow {
  494. [super didMoveToWindow];
  495. [self didMoved];
  496. }
  497. - (void)didMoveToSuperview {
  498. [super didMoveToSuperview];
  499. [self didMoved];
  500. }
  501. - (void)setCurrentAnimatedImageIndex:(NSUInteger)currentAnimatedImageIndex {
  502. if (!_curAnimatedImage) return;
  503. if (currentAnimatedImageIndex >= _curAnimatedImage.animatedImageFrameCount) return;
  504. if (_curIndex == currentAnimatedImageIndex) return;
  505. void (^block)() = ^{
  506. LOCK(
  507. [_requestQueue cancelAllOperations];
  508. [_buffer removeAllObjects];
  509. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  510. _curIndex = currentAnimatedImageIndex;
  511. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  512. _curFrame = [_curAnimatedImage animatedImageFrameAtIndex:_curIndex];
  513. if (_curImageHasContentsRect) {
  514. _curContentsRect = [_curAnimatedImage animatedImageContentsRectAtIndex:_curIndex];
  515. }
  516. _time = 0;
  517. _loopEnd = NO;
  518. _bufferMiss = NO;
  519. [self.layer setNeedsDisplay];
  520. )//LOCK
  521. };
  522. if (pthread_main_np()) {
  523. block();
  524. } else {
  525. dispatch_async(dispatch_get_main_queue(), block);
  526. }
  527. }
  528. - (NSUInteger)currentAnimatedImageIndex {
  529. return _curIndex;
  530. }
  531. - (void)setRunloopMode:(NSString *)runloopMode {
  532. if ([_runloopMode isEqual:runloopMode]) return;
  533. if (_link) {
  534. if (_runloopMode) {
  535. [_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
  536. }
  537. if (runloopMode.length) {
  538. [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:runloopMode];
  539. }
  540. }
  541. _runloopMode = runloopMode.copy;
  542. }
  543. #pragma mark - Overrice NSObject(NSKeyValueObservingCustomization)
  544. + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
  545. if ([key isEqualToString:@"currentAnimatedImageIndex"]) {
  546. return NO;
  547. }
  548. return [super automaticallyNotifiesObserversForKey:key];
  549. }
  550. #pragma mark - NSCoding
  551. - (instancetype)initWithCoder:(NSCoder *)aDecoder {
  552. self = [super initWithCoder:aDecoder];
  553. _runloopMode = [aDecoder decodeObjectForKey:@"runloopMode"];
  554. if (_runloopMode.length == 0) _runloopMode = NSRunLoopCommonModes;
  555. _autoPlayAnimatedImage = [aDecoder decodeBoolForKey:@"autoPlayAnimatedImage"];
  556. UIImage *image = [aDecoder decodeObjectForKey:@"YYAnimatedImage"];
  557. UIImage *highlightedImage = [aDecoder decodeObjectForKey:@"YYHighlightedAnimatedImage"];
  558. if (image) {
  559. self.image = image;
  560. [self setImage:image withType:YYAnimagedImageTypeImage];
  561. }
  562. if (highlightedImage) {
  563. self.highlightedImage = highlightedImage;
  564. [self setImage:highlightedImage withType:YYAnimagedImageTypeHighlightedImage];
  565. }
  566. return self;
  567. }
  568. - (void)encodeWithCoder:(NSCoder *)aCoder {
  569. [super encodeWithCoder:aCoder];
  570. [aCoder encodeObject:_runloopMode forKey:@"runloopMode"];
  571. [aCoder encodeBool:_autoPlayAnimatedImage forKey:@"autoPlayAnimatedImage"];
  572. BOOL ani, multi;
  573. ani = [self.image conformsToProtocol:@protocol(YYAnimatedImage)];
  574. multi = (ani && ((UIImage <YYAnimatedImage> *)self.image).animatedImageFrameCount > 1);
  575. if (multi) [aCoder encodeObject:self.image forKey:@"YYAnimatedImage"];
  576. ani = [self.highlightedImage conformsToProtocol:@protocol(YYAnimatedImage)];
  577. multi = (ani && ((UIImage <YYAnimatedImage> *)self.highlightedImage).animatedImageFrameCount > 1);
  578. if (multi) [aCoder encodeObject:self.highlightedImage forKey:@"YYHighlightedAnimatedImage"];
  579. }
  580. @end