VTContentView.m 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. //
  2. // VTContentView.m
  3. // VTMagicView
  4. //
  5. // Created by tianzhuo on 14/12/29.
  6. // Copyright (c) 2014年 tianzhuo. All rights reserved.
  7. //
  8. #import "VTContentView.h"
  9. #import "UIViewController+VTMagic.h"
  10. #import "UIScrollView+VTMagic.h"
  11. @interface VTContentView()
  12. @property (nonatomic, strong) NSMutableDictionary *visibleDict; // 屏幕上可见的控制器
  13. @property (nonatomic, strong) NSMutableArray *indexList; // 索引集合
  14. @property (nonatomic, strong) NSMutableArray *frameList; // 控制器的坐标集合
  15. @property (nonatomic, strong) NSString *identifier; // 重用标识符
  16. @property (nonatomic, strong) NSCache *pageCache; // 缓存池
  17. @end
  18. @implementation VTContentView
  19. #pragma mark - Lifecycle
  20. - (instancetype)initWithFrame:(CGRect)frame {
  21. self = [super initWithFrame:frame];
  22. if (self) {
  23. _needPreloading = YES;
  24. _indexList = [[NSMutableArray alloc] init];
  25. _frameList = [[NSMutableArray alloc] init];
  26. _visibleDict = [[NSMutableDictionary alloc] init];
  27. _pageCache = [[NSCache alloc] init];
  28. _pageCache.name = @"com.tianzhuo.magic";
  29. _pageCache.countLimit = 10;
  30. #if TARGET_OS_IPHONE
  31. [[NSNotificationCenter defaultCenter] addObserver:self
  32. selector:@selector(clearMemoryCache)
  33. name:UIApplicationDidReceiveMemoryWarningNotification
  34. object:nil];
  35. #endif
  36. }
  37. return self;
  38. }
  39. - (void)dealloc {
  40. [[NSNotificationCenter defaultCenter] removeObserver:self];
  41. }
  42. - (void)layoutSubviews {
  43. [super layoutSubviews];
  44. if (CGRectIsEmpty(self.frame)) {
  45. return;
  46. }
  47. CGFloat offset = self.contentOffset.x;
  48. CGFloat width = self.frame.size.width;
  49. BOOL isNotBorder = 0 != (int)offset%(int)width;
  50. NSInteger currentPage = offset/self.frame.size.width;
  51. if (_currentPage == currentPage && isNotBorder) {
  52. return;
  53. }
  54. _currentPage = currentPage;
  55. CGRect frame = CGRectZero;
  56. UIViewController *viewController = nil;
  57. NSArray *pathList = [_visibleDict allKeys];
  58. for (NSIndexPath *indexPath in pathList) {
  59. frame = [_frameList[indexPath.row] CGRectValue];
  60. viewController = _visibleDict[indexPath];
  61. // 控制器若移出屏幕则将其视图从父类中移除,并添加到缓存池中
  62. if (![self vtm_isNeedDisplayWithFrame:frame preloading:_needPreloading]) {
  63. [self moveViewControllerToCache:viewController];
  64. [_visibleDict removeObjectForKey:indexPath];
  65. } else {
  66. viewController.view.frame = frame;
  67. }
  68. }
  69. NSMutableArray *tempPaths = [_indexList mutableCopy];
  70. [tempPaths removeObjectsInArray:pathList];
  71. for (NSIndexPath *indexPath in tempPaths) {
  72. frame = [_frameList[indexPath.row] CGRectValue];
  73. if ([self vtm_isNeedDisplayWithFrame:frame preloading:_needPreloading]) {
  74. [self loadViewControllerAtIndexPath:indexPath];
  75. }
  76. }
  77. }
  78. #pragma mark - functional methods
  79. - (void)reloadData {
  80. [self resetCacheData];
  81. [self resetPageFrames];
  82. [self setNeedsLayout];
  83. [self layoutIfNeeded];
  84. }
  85. -(void)resetCacheData {
  86. [_indexList removeAllObjects];
  87. for (NSInteger i = 0; i < _pageCount; i++) {
  88. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
  89. [_indexList addObject:indexPath];
  90. }
  91. // reload时清除所有页面
  92. NSArray *viewControllers = [_visibleDict allValues];
  93. for (UIViewController *viewController in viewControllers) {
  94. [self moveViewControllerToCache:viewController];
  95. }
  96. [_visibleDict removeAllObjects];
  97. }
  98. - (void)moveViewControllerToCache:(UIViewController *)viewController {
  99. [viewController willMoveToParentViewController:nil];
  100. [viewController.view removeFromSuperview];
  101. [viewController removeFromParentViewController];
  102. // 添加到缓存池
  103. NSMutableSet *cacheSet = [_pageCache objectForKey:viewController.reuseIdentifier];
  104. if (!cacheSet) cacheSet = [[NSMutableSet alloc] init];
  105. [cacheSet addObject:viewController];
  106. [_pageCache setObject:cacheSet forKey:viewController.reuseIdentifier];
  107. }
  108. - (void)resetPageFrames {
  109. [_frameList removeAllObjects];
  110. CGRect frame = self.bounds;
  111. for (NSIndexPath *indexPath in _indexList) {
  112. frame.origin.x = indexPath.row * frame.size.width;
  113. [_frameList addObject:[NSValue valueWithCGRect:frame]];
  114. }
  115. self.contentSize = CGSizeMake(CGRectGetMaxX(frame), 0);
  116. self.contentOffset = CGPointMake(CGRectGetWidth(frame)*_currentPage, 0);
  117. }
  118. #pragma mark - 根据页面控制器获取对应的索引
  119. - (NSInteger)pageIndexForViewController:(UIViewController *)viewController {
  120. for (NSIndexPath *indexPath in _visibleDict.allKeys) {
  121. if ([viewController isEqual:_visibleDict[indexPath]]) {
  122. return indexPath.row;
  123. }
  124. }
  125. return NSNotFound;
  126. }
  127. - (CGRect)frameOfViewControllerAtPage:(NSUInteger)pageIndex
  128. {
  129. if (_frameList.count <= pageIndex) {
  130. return CGRectZero;
  131. }
  132. return [_frameList[pageIndex] CGRectValue];
  133. }
  134. #pragma mark - 根据索引获取页面控制器
  135. - (UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex {
  136. return [self viewControllerAtPage:pageIndex autoCreate:NO];
  137. }
  138. - (UIViewController *)creatViewControllerAtPage:(NSUInteger)pageIndex {
  139. return [self viewControllerAtPage:pageIndex autoCreate:YES];
  140. }
  141. - (UIViewController *)viewControllerAtPage:(NSUInteger)pageIndex autoCreate:(BOOL)autoCreate {
  142. if (_pageCount <= pageIndex) {
  143. return nil;
  144. }
  145. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:pageIndex inSection:0];
  146. UIViewController *viewController = [_visibleDict objectForKey:indexPath];
  147. if (!viewController && autoCreate) {
  148. viewController = [self loadViewControllerAtIndexPath:indexPath];
  149. }
  150. return viewController;
  151. }
  152. - (UIViewController *)loadViewControllerAtIndexPath:(NSIndexPath *)indexPath {
  153. UIViewController *viewController = [_dataSource contentView:self viewControllerAtPage:indexPath.row];
  154. if (viewController) {
  155. viewController.reuseIdentifier = _identifier;
  156. CGRect pageFrame = [_frameList[indexPath.row] CGRectValue];
  157. viewController.view.frame = pageFrame;
  158. [self addSubview:viewController.view];
  159. [_visibleDict setObject:viewController forKey:indexPath];
  160. }
  161. _identifier = nil;
  162. return viewController;
  163. }
  164. #pragma mark - 根据缓存标识查询可重用的视图控制器
  165. - (UIViewController *)dequeueReusablePageWithIdentifier:(NSString *)identifier {
  166. _identifier = identifier;
  167. NSMutableSet *cacheSet = [_pageCache objectForKey:identifier];
  168. UIViewController *viewController = [cacheSet anyObject];
  169. if (viewController) {
  170. [cacheSet removeObject:viewController];
  171. [_pageCache setObject:cacheSet forKey:identifier];
  172. }
  173. return viewController;
  174. }
  175. #pragma mark - clear memory cache
  176. - (void)clearMemoryCache {
  177. [_pageCache removeAllObjects];
  178. }
  179. #pragma mark - accessor methods
  180. - (NSArray *)visibleList {
  181. return [_visibleDict allValues];
  182. }
  183. @end