YYAnimatedImageView.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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 "YYWeakProxy.h"
  13. #import "UIDevice+YYAdd.h"
  14. #import "YYImageCoder.h"
  15. #import "YYKitMacro.h"
  16. #define BUFFER_SIZE (10 * 1024 * 1024) // 10MB (minimum memory buffer size)
  17. #define LOCK(...) dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER); \
  18. __VA_ARGS__; \
  19. dispatch_semaphore_signal(self->_lock);
  20. #define LOCK_VIEW(...) dispatch_semaphore_wait(view->_lock, DISPATCH_TIME_FOREVER); \
  21. __VA_ARGS__; \
  22. dispatch_semaphore_signal(view->_lock);
  23. typedef NS_ENUM(NSUInteger, YYAnimatedImageType) {
  24. YYAnimatedImageTypeNone = 0,
  25. YYAnimatedImageTypeImage,
  26. YYAnimatedImageTypeHighlightedImage,
  27. YYAnimatedImageTypeImages,
  28. YYAnimatedImageTypeHighlightedImages,
  29. };
  30. @interface YYAnimatedImageView() {
  31. @package
  32. UIImage <YYAnimatedImage> *_curAnimatedImage;
  33. dispatch_semaphore_t _lock; ///< lock for _buffer
  34. NSOperationQueue *_requestQueue; ///< image request queue, serial
  35. CADisplayLink *_link; ///< ticker for change frame
  36. NSTimeInterval _time; ///< time after last frame
  37. UIImage *_curFrame; ///< current frame to display
  38. NSUInteger _curIndex; ///< current frame index (from 0)
  39. NSUInteger _totalFrameCount; ///< total frame count
  40. BOOL _loopEnd; ///< whether the loop is end.
  41. NSUInteger _curLoop; ///< current loop count (from 0)
  42. NSUInteger _totalLoop; ///< total loop count, 0 means infinity
  43. NSMutableDictionary *_buffer; ///< frame buffer
  44. BOOL _bufferMiss; ///< whether miss frame on last opportunity
  45. NSUInteger _maxBufferCount; ///< maximum buffer count
  46. NSInteger _incrBufferCount; ///< current allowed buffer count (will increase by step)
  47. CGRect _curContentsRect;
  48. BOOL _curImageHasContentsRect; ///< image has implementated "animatedImageContentsRectAtIndex:"
  49. }
  50. @property (nonatomic, readwrite) BOOL currentIsPlayingAnimation;
  51. - (void)calcMaxBufferCount;
  52. @end
  53. /// An operation for image fetch
  54. @interface _YYAnimatedImageViewFetchOperation : NSOperation
  55. @property (nonatomic, weak) YYAnimatedImageView *view;
  56. @property (nonatomic, assign) NSUInteger nextIndex;
  57. @property (nonatomic, strong) UIImage <YYAnimatedImage> *curImage;
  58. @end
  59. @implementation _YYAnimatedImageViewFetchOperation
  60. - (void)main {
  61. __strong YYAnimatedImageView *view = _view;
  62. if (!view) return;
  63. if ([self isCancelled]) return;
  64. view->_incrBufferCount++;
  65. if (view->_incrBufferCount == 0) [view calcMaxBufferCount];
  66. if (view->_incrBufferCount > (NSInteger)view->_maxBufferCount) {
  67. view->_incrBufferCount = view->_maxBufferCount;
  68. }
  69. NSUInteger idx = _nextIndex;
  70. NSUInteger max = view->_incrBufferCount < 1 ? 1 : view->_incrBufferCount;
  71. NSUInteger total = view->_totalFrameCount;
  72. view = nil;
  73. for (int i = 0; i < max; i++, idx++) {
  74. @autoreleasepool {
  75. if (idx >= total) idx = 0;
  76. if ([self isCancelled]) break;
  77. __strong YYAnimatedImageView *view = _view;
  78. if (!view) break;
  79. LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
  80. if (miss) {
  81. UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
  82. img = img.imageByDecoded;
  83. if ([self isCancelled]) break;
  84. LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
  85. view = nil;
  86. }
  87. }
  88. }
  89. }
  90. @end
  91. @implementation YYAnimatedImageView
  92. - (instancetype)init {
  93. self = [super init];
  94. _runloopMode = NSRunLoopCommonModes;
  95. _autoPlayAnimatedImage = YES;
  96. return self;
  97. }
  98. - (instancetype)initWithFrame:(CGRect)frame {
  99. self = [super initWithFrame:frame];
  100. _runloopMode = NSRunLoopCommonModes;
  101. _autoPlayAnimatedImage = YES;
  102. return self;
  103. }
  104. - (instancetype)initWithImage:(UIImage *)image {
  105. self = [super init];
  106. _runloopMode = NSRunLoopCommonModes;
  107. _autoPlayAnimatedImage = YES;
  108. self.frame = (CGRect) {CGPointZero, image.size };
  109. self.image = image;
  110. return self;
  111. }
  112. - (instancetype)initWithImage:(UIImage *)image highlightedImage:(UIImage *)highlightedImage {
  113. self = [super init];
  114. _runloopMode = NSRunLoopCommonModes;
  115. _autoPlayAnimatedImage = YES;
  116. CGSize size = image ? image.size : highlightedImage.size;
  117. self.frame = (CGRect) {CGPointZero, size };
  118. self.image = image;
  119. self.highlightedImage = highlightedImage;
  120. return self;
  121. }
  122. // init the animated params.
  123. - (void)resetAnimated {
  124. if (!_link) {
  125. _lock = dispatch_semaphore_create(1);
  126. _buffer = [NSMutableDictionary new];
  127. _requestQueue = [[NSOperationQueue alloc] init];
  128. _requestQueue.maxConcurrentOperationCount = 1;
  129. _link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(step:)];
  130. if (_runloopMode) {
  131. [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
  132. }
  133. _link.paused = YES;
  134. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  135. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
  136. }
  137. [_requestQueue cancelAllOperations];
  138. LOCK(
  139. if (_buffer.count) {
  140. NSMutableDictionary *holder = _buffer;
  141. _buffer = [NSMutableDictionary new];
  142. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  143. // Capture the dictionary to global queue,
  144. // release these images in background to avoid blocking UI thread.
  145. [holder class];
  146. });
  147. }
  148. );
  149. _link.paused = YES;
  150. _time = 0;
  151. if (_curIndex != 0) {
  152. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  153. _curIndex = 0;
  154. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  155. }
  156. _curAnimatedImage = nil;
  157. _curFrame = nil;
  158. _curLoop = 0;
  159. _totalLoop = 0;
  160. _totalFrameCount = 1;
  161. _loopEnd = NO;
  162. _bufferMiss = NO;
  163. _incrBufferCount = 0;
  164. }
  165. - (void)setImage:(UIImage *)image {
  166. if (self.image == image) return;
  167. [self setImage:image withType:YYAnimatedImageTypeImage];
  168. }
  169. - (void)setHighlightedImage:(UIImage *)highlightedImage {
  170. if (self.highlightedImage == highlightedImage) return;
  171. [self setImage:highlightedImage withType:YYAnimatedImageTypeHighlightedImage];
  172. }
  173. - (void)setAnimationImages:(NSArray *)animationImages {
  174. if (self.animationImages == animationImages) return;
  175. [self setImage:animationImages withType:YYAnimatedImageTypeImages];
  176. }
  177. - (void)setHighlightedAnimationImages:(NSArray *)highlightedAnimationImages {
  178. if (self.highlightedAnimationImages == highlightedAnimationImages) return;
  179. [self setImage:highlightedAnimationImages withType:YYAnimatedImageTypeHighlightedImages];
  180. }
  181. - (void)setHighlighted:(BOOL)highlighted {
  182. [super setHighlighted:highlighted];
  183. if (_link) [self resetAnimated];
  184. [self imageChanged];
  185. }
  186. - (id)imageForType:(YYAnimatedImageType)type {
  187. switch (type) {
  188. case YYAnimatedImageTypeNone: return nil;
  189. case YYAnimatedImageTypeImage: return self.image;
  190. case YYAnimatedImageTypeHighlightedImage: return self.highlightedImage;
  191. case YYAnimatedImageTypeImages: return self.animationImages;
  192. case YYAnimatedImageTypeHighlightedImages: return self.highlightedAnimationImages;
  193. }
  194. return nil;
  195. }
  196. - (YYAnimatedImageType)currentImageType {
  197. YYAnimatedImageType curType = YYAnimatedImageTypeNone;
  198. if (self.highlighted) {
  199. if (self.highlightedAnimationImages.count) curType = YYAnimatedImageTypeHighlightedImages;
  200. else if (self.highlightedImage) curType = YYAnimatedImageTypeHighlightedImage;
  201. }
  202. if (curType == YYAnimatedImageTypeNone) {
  203. if (self.animationImages.count) curType = YYAnimatedImageTypeImages;
  204. else if (self.image) curType = YYAnimatedImageTypeImage;
  205. }
  206. return curType;
  207. }
  208. - (void)setImage:(id)image withType:(YYAnimatedImageType)type {
  209. [self stopAnimating];
  210. if (_link) [self resetAnimated];
  211. _curFrame = nil;
  212. switch (type) {
  213. case YYAnimatedImageTypeNone: break;
  214. case YYAnimatedImageTypeImage: super.image = image; break;
  215. case YYAnimatedImageTypeHighlightedImage: super.highlightedImage = image; break;
  216. case YYAnimatedImageTypeImages: super.animationImages = image; break;
  217. case YYAnimatedImageTypeHighlightedImages: super.highlightedAnimationImages = image; break;
  218. }
  219. [self imageChanged];
  220. }
  221. - (void)imageChanged {
  222. YYAnimatedImageType newType = [self currentImageType];
  223. id newVisibleImage = [self imageForType:newType];
  224. NSUInteger newImageFrameCount = 0;
  225. BOOL hasContentsRect = NO;
  226. if ([newVisibleImage isKindOfClass:[UIImage class]] &&
  227. [newVisibleImage conformsToProtocol:@protocol(YYAnimatedImage)]) {
  228. newImageFrameCount = ((UIImage<YYAnimatedImage> *) newVisibleImage).animatedImageFrameCount;
  229. if (newImageFrameCount > 1) {
  230. hasContentsRect = [((UIImage<YYAnimatedImage> *) newVisibleImage) respondsToSelector:@selector(animatedImageContentsRectAtIndex:)];
  231. }
  232. }
  233. if (!hasContentsRect && _curImageHasContentsRect) {
  234. if (!CGRectEqualToRect(self.layer.contentsRect, CGRectMake(0, 0, 1, 1)) ) {
  235. [CATransaction begin];
  236. [CATransaction setDisableActions:YES];
  237. self.layer.contentsRect = CGRectMake(0, 0, 1, 1);
  238. [CATransaction commit];
  239. }
  240. }
  241. _curImageHasContentsRect = hasContentsRect;
  242. if (hasContentsRect) {
  243. CGRect rect = [((UIImage<YYAnimatedImage> *) newVisibleImage) animatedImageContentsRectAtIndex:0];
  244. [self setContentsRect:rect forImage:newVisibleImage];
  245. }
  246. if (newImageFrameCount > 1) {
  247. [self resetAnimated];
  248. _curAnimatedImage = newVisibleImage;
  249. _curFrame = newVisibleImage;
  250. _totalLoop = _curAnimatedImage.animatedImageLoopCount;
  251. _totalFrameCount = _curAnimatedImage.animatedImageFrameCount;
  252. [self calcMaxBufferCount];
  253. }
  254. [self setNeedsDisplay];
  255. [self didMoved];
  256. }
  257. // dynamically adjust buffer size for current memory.
  258. - (void)calcMaxBufferCount {
  259. int64_t bytes = (int64_t)_curAnimatedImage.animatedImageBytesPerFrame;
  260. if (bytes == 0) bytes = 1024;
  261. int64_t total = [UIDevice currentDevice].memoryTotal;
  262. int64_t free = [UIDevice currentDevice].memoryFree;
  263. int64_t max = MIN(total * 0.2, free * 0.6);
  264. max = MAX(max, BUFFER_SIZE);
  265. if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
  266. double maxBufferCount = (double)max / (double)bytes;
  267. maxBufferCount = YY_CLAMP(maxBufferCount, 1, 512);
  268. _maxBufferCount = maxBufferCount;
  269. }
  270. - (void)dealloc {
  271. [_requestQueue cancelAllOperations];
  272. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  273. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
  274. [_link invalidate];
  275. }
  276. - (BOOL)isAnimating {
  277. return self.currentIsPlayingAnimation;
  278. }
  279. - (void)stopAnimating {
  280. [super stopAnimating];
  281. [_requestQueue cancelAllOperations];
  282. _link.paused = YES;
  283. self.currentIsPlayingAnimation = NO;
  284. }
  285. - (void)startAnimating {
  286. YYAnimatedImageType type = [self currentImageType];
  287. if (type == YYAnimatedImageTypeImages || type == YYAnimatedImageTypeHighlightedImages) {
  288. NSArray *images = [self imageForType:type];
  289. if (images.count > 0) {
  290. [super startAnimating];
  291. self.currentIsPlayingAnimation = YES;
  292. }
  293. } else {
  294. if (_curAnimatedImage && _link.paused) {
  295. _curLoop = 0;
  296. _loopEnd = NO;
  297. _link.paused = NO;
  298. self.currentIsPlayingAnimation = YES;
  299. }
  300. }
  301. }
  302. - (void)didReceiveMemoryWarning:(NSNotification *)notification {
  303. [_requestQueue cancelAllOperations];
  304. [_requestQueue addOperationWithBlock: ^{
  305. _incrBufferCount = -60 - (int)(arc4random() % 120); // about 1~3 seconds to grow back..
  306. NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
  307. LOCK(
  308. NSArray * keys = _buffer.allKeys;
  309. for (NSNumber * key in keys) {
  310. if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
  311. [_buffer removeObjectForKey:key];
  312. }
  313. }
  314. )//LOCK
  315. }];
  316. }
  317. - (void)didEnterBackground:(NSNotification *)notification {
  318. [_requestQueue cancelAllOperations];
  319. NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
  320. LOCK(
  321. NSArray * keys = _buffer.allKeys;
  322. for (NSNumber * key in keys) {
  323. if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
  324. [_buffer removeObjectForKey:key];
  325. }
  326. }
  327. )//LOCK
  328. }
  329. - (void)step:(CADisplayLink *)link {
  330. UIImage <YYAnimatedImage> *image = _curAnimatedImage;
  331. NSMutableDictionary *buffer = _buffer;
  332. UIImage *bufferedImage = nil;
  333. NSUInteger nextIndex = (_curIndex + 1) % _totalFrameCount;
  334. BOOL bufferIsFull = NO;
  335. if (!image) return;
  336. if (_loopEnd) { // view will keep in last frame
  337. [self stopAnimating];
  338. return;
  339. }
  340. NSTimeInterval delay = 0;
  341. if (!_bufferMiss) {
  342. _time += link.duration;
  343. delay = [image animatedImageDurationAtIndex:_curIndex];
  344. if (_time < delay) return;
  345. _time -= delay;
  346. if (nextIndex == 0) {
  347. _curLoop++;
  348. if (_curLoop >= _totalLoop && _totalLoop != 0) {
  349. _loopEnd = YES;
  350. [self stopAnimating];
  351. [self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
  352. return; // stop at last frame
  353. }
  354. }
  355. delay = [image animatedImageDurationAtIndex:nextIndex];
  356. if (_time > delay) _time = delay; // do not jump over frame
  357. }
  358. LOCK(
  359. bufferedImage = buffer[@(nextIndex)];
  360. if (bufferedImage) {
  361. if ((int)_incrBufferCount < _totalFrameCount) {
  362. [buffer removeObjectForKey:@(nextIndex)];
  363. }
  364. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  365. _curIndex = nextIndex;
  366. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  367. _curFrame = bufferedImage == (id)[NSNull null] ? nil : bufferedImage;
  368. if (_curImageHasContentsRect) {
  369. _curContentsRect = [image animatedImageContentsRectAtIndex:_curIndex];
  370. [self setContentsRect:_curContentsRect forImage:_curFrame];
  371. }
  372. nextIndex = (_curIndex + 1) % _totalFrameCount;
  373. _bufferMiss = NO;
  374. if (buffer.count == _totalFrameCount) {
  375. bufferIsFull = YES;
  376. }
  377. } else {
  378. _bufferMiss = YES;
  379. }
  380. )//LOCK
  381. if (!_bufferMiss) {
  382. [self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
  383. }
  384. if (!bufferIsFull && _requestQueue.operationCount == 0) { // if some work not finished, wait for next opportunity
  385. _YYAnimatedImageViewFetchOperation *operation = [_YYAnimatedImageViewFetchOperation new];
  386. operation.view = self;
  387. operation.nextIndex = nextIndex;
  388. operation.curImage = image;
  389. [_requestQueue addOperation:operation];
  390. }
  391. }
  392. - (void)displayLayer:(CALayer *)layer {
  393. if (_curFrame) {
  394. layer.contents = (__bridge id)_curFrame.CGImage;
  395. }
  396. }
  397. - (void)setContentsRect:(CGRect)rect forImage:(UIImage *)image{
  398. CGRect layerRect = CGRectMake(0, 0, 1, 1);
  399. if (image) {
  400. CGSize imageSize = image.size;
  401. if (imageSize.width > 0.01 && imageSize.height > 0.01) {
  402. layerRect.origin.x = rect.origin.x / imageSize.width;
  403. layerRect.origin.y = rect.origin.y / imageSize.height;
  404. layerRect.size.width = rect.size.width / imageSize.width;
  405. layerRect.size.height = rect.size.height / imageSize.height;
  406. layerRect = CGRectIntersection(layerRect, CGRectMake(0, 0, 1, 1));
  407. if (CGRectIsNull(layerRect) || CGRectIsEmpty(layerRect)) {
  408. layerRect = CGRectMake(0, 0, 1, 1);
  409. }
  410. }
  411. }
  412. [CATransaction begin];
  413. [CATransaction setDisableActions:YES];
  414. self.layer.contentsRect = layerRect;
  415. [CATransaction commit];
  416. }
  417. - (void)didMoved {
  418. if (self.autoPlayAnimatedImage) {
  419. if(self.superview && self.window) {
  420. [self startAnimating];
  421. } else {
  422. [self stopAnimating];
  423. }
  424. }
  425. }
  426. - (void)didMoveToWindow {
  427. [super didMoveToWindow];
  428. [self didMoved];
  429. }
  430. - (void)didMoveToSuperview {
  431. [super didMoveToSuperview];
  432. [self didMoved];
  433. }
  434. - (void)setCurrentAnimatedImageIndex:(NSUInteger)currentAnimatedImageIndex {
  435. if (!_curAnimatedImage) return;
  436. if (currentAnimatedImageIndex >= _curAnimatedImage.animatedImageFrameCount) return;
  437. if (_curIndex == currentAnimatedImageIndex) return;
  438. dispatch_async_on_main_queue(^{
  439. LOCK(
  440. [_requestQueue cancelAllOperations];
  441. [_buffer removeAllObjects];
  442. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  443. _curIndex = currentAnimatedImageIndex;
  444. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  445. _curFrame = [_curAnimatedImage animatedImageFrameAtIndex:_curIndex];
  446. if (_curImageHasContentsRect) {
  447. _curContentsRect = [_curAnimatedImage animatedImageContentsRectAtIndex:_curIndex];
  448. }
  449. _time = 0;
  450. _loopEnd = NO;
  451. _bufferMiss = NO;
  452. [self.layer setNeedsDisplay];
  453. )//LOCK
  454. });
  455. }
  456. - (NSUInteger)currentAnimatedImageIndex {
  457. return _curIndex;
  458. }
  459. - (void)setRunloopMode:(NSString *)runloopMode {
  460. if ([_runloopMode isEqual:runloopMode]) return;
  461. if (_link) {
  462. if (_runloopMode) {
  463. [_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
  464. }
  465. if (runloopMode.length) {
  466. [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:runloopMode];
  467. }
  468. }
  469. _runloopMode = runloopMode.copy;
  470. }
  471. #pragma mark - Overrice NSObject(NSKeyValueObservingCustomization)
  472. + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
  473. if ([key isEqualToString:@"currentAnimatedImageIndex"]) {
  474. return NO;
  475. }
  476. return [super automaticallyNotifiesObserversForKey:key];
  477. }
  478. #pragma mark - NSCoding
  479. - (instancetype)initWithCoder:(NSCoder *)aDecoder {
  480. self = [super initWithCoder:aDecoder];
  481. _runloopMode = [aDecoder decodeObjectForKey:@"runloopMode"];
  482. if (_runloopMode.length == 0) _runloopMode = NSRunLoopCommonModes;
  483. if ([aDecoder containsValueForKey:@"autoPlayAnimatedImage"]) {
  484. _autoPlayAnimatedImage = [aDecoder decodeBoolForKey:@"autoPlayAnimatedImage"];
  485. } else {
  486. _autoPlayAnimatedImage = YES;
  487. }
  488. UIImage *image = [aDecoder decodeObjectForKey:@"YYAnimatedImage"];
  489. UIImage *highlightedImage = [aDecoder decodeObjectForKey:@"YYHighlightedAnimatedImage"];
  490. if (image) {
  491. self.image = image;
  492. [self setImage:image withType:YYAnimatedImageTypeImage];
  493. }
  494. if (highlightedImage) {
  495. self.highlightedImage = highlightedImage;
  496. [self setImage:highlightedImage withType:YYAnimatedImageTypeHighlightedImage];
  497. }
  498. return self;
  499. }
  500. - (void)encodeWithCoder:(NSCoder *)aCoder {
  501. [super encodeWithCoder:aCoder];
  502. [aCoder encodeObject:_runloopMode forKey:@"runloopMode"];
  503. [aCoder encodeBool:_autoPlayAnimatedImage forKey:@"autoPlayAnimatedImage"];
  504. BOOL ani, multi;
  505. ani = [self.image conformsToProtocol:@protocol(YYAnimatedImage)];
  506. multi = (ani && ((UIImage <YYAnimatedImage> *)self.image).animatedImageFrameCount > 1);
  507. if (multi) [aCoder encodeObject:self.image forKey:@"YYAnimatedImage"];
  508. ani = [self.highlightedImage conformsToProtocol:@protocol(YYAnimatedImage)];
  509. multi = (ani && ((UIImage <YYAnimatedImage> *)self.highlightedImage).animatedImageFrameCount > 1);
  510. if (multi) [aCoder encodeObject:self.highlightedImage forKey:@"YYHighlightedAnimatedImage"];
  511. }
  512. @end