YYAnimatedImageView.m 21 KB

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