FLEXCollectionContentSection.m 7.0 KB

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