FLEXCollectionContentSection.m 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. //
  2. // FLEXCollectionContentSection.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 8/28/19.
  6. // Copyright © 2020 FLEX Team. All rights reserved.
  7. //
  8. #import "FLEXCollectionContentSection.h"
  9. #import "FLEXUtility.h"
  10. #import "FLEXRuntimeUtility.h"
  11. #import "FLEXSubtitleTableViewCell.h"
  12. #import "FLEXTableView.h"
  13. #import "FLEXObjectExplorerFactory.h"
  14. #import "FLEXDefaultEditorViewController.h"
  15. typedef NS_ENUM(NSUInteger, FLEXCollectionType) {
  16. FLEXUnsupportedCollection,
  17. FLEXOrderedCollection,
  18. FLEXUnorderedCollection,
  19. FLEXKeyedCollection
  20. };
  21. @interface NSArray (FLEXCollection) <FLEXCollection> @end
  22. @interface NSSet (FLEXCollection) <FLEXCollection> @end
  23. @interface NSOrderedSet (FLEXCollection) <FLEXCollection> @end
  24. @interface NSDictionary (FLEXCollection) <FLEXCollection> @end
  25. @interface NSMutableArray (FLEXMutableCollection) <FLEXMutableCollection> @end
  26. @interface NSMutableSet (FLEXMutableCollection) <FLEXMutableCollection> @end
  27. @interface NSMutableOrderedSet (FLEXMutableCollection) <FLEXMutableCollection> @end
  28. @interface NSMutableDictionary (FLEXMutableCollection) <FLEXMutableCollection>
  29. - (void)filterUsingPredicate:(NSPredicate *)predicate;
  30. @end
  31. @interface FLEXCollectionContentSection ()
  32. /// Generated from \c collectionFuture or \c collection
  33. @property (nonatomic, copy) id<FLEXCollection> cachedCollection;
  34. /// A static collection to display
  35. @property (nonatomic, readonly) id<FLEXCollection> collection;
  36. /// A collection that may change over time and can be called upon for new data
  37. @property (nonatomic, readonly) FLEXCollectionContentFuture collectionFuture;
  38. @property (nonatomic, readonly) FLEXCollectionType collectionType;
  39. @property (nonatomic, readonly) BOOL isMutable;
  40. @end
  41. @implementation FLEXCollectionContentSection
  42. @synthesize filterText = _filterText;
  43. #pragma mark Initialization
  44. + (instancetype)forObject:(id)object {
  45. return [self forCollection:object];
  46. }
  47. + (id)forCollection:(id<FLEXCollection>)collection {
  48. FLEXCollectionContentSection *section = [self new];
  49. section->_collectionType = [self typeForCollection:collection];
  50. section->_collection = collection;
  51. section.cachedCollection = collection;
  52. section->_isMutable = [collection respondsToSelector:@selector(filterUsingPredicate:)];
  53. return section;
  54. }
  55. + (id)forReusableFuture:(FLEXCollectionContentFuture)collectionFuture {
  56. FLEXCollectionContentSection *section = [self new];
  57. section->_collectionFuture = collectionFuture;
  58. section.cachedCollection = (id<FLEXCollection>)collectionFuture(section);
  59. section->_collectionType = [self typeForCollection:section.cachedCollection];
  60. section->_isMutable = [section->_cachedCollection respondsToSelector:@selector(filterUsingPredicate:)];
  61. return section;
  62. }
  63. #pragma mark - Misc
  64. + (FLEXCollectionType)typeForCollection:(id<FLEXCollection>)collection {
  65. // Order matters here, as NSDictionary is keyed but it responds to allObjects
  66. if ([collection respondsToSelector:@selector(objectAtIndex:)]) {
  67. return FLEXOrderedCollection;
  68. }
  69. if ([collection respondsToSelector:@selector(objectForKey:)]) {
  70. return FLEXKeyedCollection;
  71. }
  72. if ([collection respondsToSelector:@selector(allObjects)]) {
  73. return FLEXUnorderedCollection;
  74. }
  75. [NSException raise:NSInvalidArgumentException
  76. format:@"Given collection does not properly conform to FLEXCollection"];
  77. return FLEXUnsupportedCollection;
  78. }
  79. /// Row titles
  80. /// - Ordered: the index
  81. /// - Unordered: the object
  82. /// - Keyed: the key
  83. - (NSString *)titleForRow:(NSInteger)row {
  84. switch (self.collectionType) {
  85. case FLEXOrderedCollection:
  86. if (!self.hideOrderIndexes) {
  87. return @(row).stringValue;
  88. }
  89. // Fall-through
  90. case FLEXUnorderedCollection:
  91. return [self describe:[self objectForRow:row]];
  92. case FLEXKeyedCollection:
  93. return [self describe:self.cachedCollection.allKeys[row]];
  94. case FLEXUnsupportedCollection:
  95. return nil;
  96. }
  97. }
  98. /// Row subtitles
  99. /// - Ordered: the object
  100. /// - Unordered: nothing
  101. /// - Keyed: the value
  102. - (NSString *)subtitleForRow:(NSInteger)row {
  103. switch (self.collectionType) {
  104. case FLEXOrderedCollection:
  105. if (!self.hideOrderIndexes) {
  106. nil;
  107. }
  108. // Fall-through
  109. case FLEXKeyedCollection:
  110. return [self describe:[self objectForRow:row]];
  111. case FLEXUnorderedCollection:
  112. return nil;
  113. case FLEXUnsupportedCollection:
  114. return nil;
  115. }
  116. }
  117. - (NSString *)describe:(id)object {
  118. return [FLEXRuntimeUtility summaryForObject:object];
  119. }
  120. - (id)objectForRow:(NSInteger)row {
  121. switch (self.collectionType) {
  122. case FLEXOrderedCollection:
  123. return self.cachedCollection[row];
  124. case FLEXUnorderedCollection:
  125. return self.cachedCollection.allObjects[row];
  126. case FLEXKeyedCollection:
  127. return self.cachedCollection[self.cachedCollection.allKeys[row]];
  128. case FLEXUnsupportedCollection:
  129. return nil;
  130. }
  131. }
  132. - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row {
  133. return UITableViewCellAccessoryDisclosureIndicator;
  134. // return self.isMutable ? UITableViewCellAccessoryDetailDisclosureButton : UITableViewCellAccessoryDisclosureIndicator;
  135. }
  136. #pragma mark - Overrides
  137. - (NSString *)title {
  138. if (!self.hideSectionTitle) {
  139. if (self.customTitle) {
  140. return self.customTitle;
  141. }
  142. return FLEXPluralString(self.cachedCollection.count, @"Entries", @"Entry");
  143. }
  144. return nil;
  145. }
  146. - (NSInteger)numberOfRows {
  147. return self.cachedCollection.count;
  148. }
  149. - (void)setFilterText:(NSString *)filterText {
  150. super.filterText = filterText;
  151. if (filterText.length) {
  152. BOOL (^matcher)(id, id) = self.customFilter ?: ^BOOL(NSString *query, id obj) {
  153. return [[self describe:obj] localizedCaseInsensitiveContainsString:query];
  154. };
  155. NSPredicate *filter = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *bindings) {
  156. return matcher(filterText, obj);
  157. }];
  158. id<FLEXMutableCollection> tmp = self.cachedCollection.mutableCopy;
  159. [tmp filterUsingPredicate:filter];
  160. self.cachedCollection = tmp;
  161. } else {
  162. self.cachedCollection = self.collection ?: (id<FLEXCollection>)self.collectionFuture(self);
  163. }
  164. }
  165. - (void)reloadData {
  166. if (self.collectionFuture) {
  167. self.cachedCollection = (id<FLEXCollection>)self.collectionFuture(self);
  168. } else {
  169. self.cachedCollection = self.collection.copy;
  170. }
  171. }
  172. - (BOOL)canSelectRow:(NSInteger)row {
  173. return YES;
  174. }
  175. - (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
  176. return [FLEXObjectExplorerFactory explorerViewControllerForObject:[self objectForRow:row]];
  177. }
  178. - (NSString *)reuseIdentifierForRow:(NSInteger)row {
  179. return kFLEXDetailCell;
  180. }
  181. - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row {
  182. cell.titleLabel.text = [self titleForRow:row];
  183. cell.subtitleLabel.text = [self subtitleForRow:row];
  184. cell.accessoryType = [self accessoryTypeForRow:row];
  185. }
  186. @end
  187. #pragma mark - NSMutableDictionary
  188. @implementation NSMutableDictionary (FLEXMutableCollection)
  189. - (void)filterUsingPredicate:(NSPredicate *)predicate {
  190. id test = ^BOOL(id key, NSUInteger idx, BOOL *stop) {
  191. if ([predicate evaluateWithObject:key]) {
  192. return NO;
  193. }
  194. return ![predicate evaluateWithObject:self[key]];
  195. };
  196. NSArray *keys = self.allKeys;
  197. NSIndexSet *remove = [keys indexesOfObjectsPassingTest:test];
  198. [self removeObjectsForKeys:[keys objectsAtIndexes:remove]];
  199. }
  200. @end