FLEXMetadataSection.m 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. //
  2. // FLEXMetadataSection.m
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 9/19/19.
  6. // Copyright © 2019 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXMetadataSection.h"
  9. #import "FLEXTableView.h"
  10. #import "FLEXTableViewCell.h"
  11. #import "FLEXObjectExplorerFactory.h"
  12. #import "FLEXFieldEditorViewController.h"
  13. #import "FLEXMethodCallingViewController.h"
  14. #import "FLEXIvar.h"
  15. #import "NSArray+FLEX.h"
  16. #import "FLEXRuntime+UIKitHelpers.h"
  17. @interface FLEXMetadataSection ()
  18. @property (nonatomic, readonly) FLEXObjectExplorer *explorer;
  19. /// Filtered
  20. @property (nonatomic, copy) NSArray<id<FLEXRuntimeMetadata>> *metadata;
  21. /// Unfiltered
  22. @property (nonatomic, copy) NSArray<id<FLEXRuntimeMetadata>> *allMetadata;
  23. @end
  24. @implementation FLEXMetadataSection
  25. #pragma mark - Initialization
  26. + (instancetype)explorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind {
  27. return [[self alloc] initWithExplorer:explorer kind:metadataKind];
  28. }
  29. - (id)initWithExplorer:(FLEXObjectExplorer *)explorer kind:(FLEXMetadataKind)metadataKind {
  30. self = [super init];
  31. if (self) {
  32. _explorer = explorer;
  33. _metadataKind = metadataKind;
  34. [self reloadData];
  35. }
  36. return self;
  37. }
  38. #pragma mark - Private
  39. - (NSString *)titleWithBaseName:(NSString *)baseName {
  40. unsigned long totalCount = self.allMetadata.count;
  41. unsigned long filteredCount = self.metadata.count;
  42. if (totalCount == filteredCount) {
  43. return [baseName stringByAppendingFormat:@" (%lu)", totalCount];
  44. } else {
  45. return [baseName stringByAppendingFormat:@" (%lu of %lu)", filteredCount, totalCount];
  46. }
  47. }
  48. - (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row {
  49. return [self.metadata[row] suggestedAccessoryTypeWithTarget:self.explorer.object];
  50. }
  51. #pragma mark - Public
  52. - (void)setExcludedMetadata:(NSSet<NSString *> *)excludedMetadata {
  53. _excludedMetadata = excludedMetadata;
  54. [self reloadData];
  55. }
  56. #pragma mark - Overrides
  57. - (NSString *)titleForRow:(NSInteger)row {
  58. return [self.metadata[row] description];
  59. }
  60. - (NSString *)subtitleForRow:(NSInteger)row {
  61. return [self.metadata[row] previewWithTarget:self.explorer.object];
  62. }
  63. - (NSString *)title {
  64. switch (self.metadataKind) {
  65. case FLEXMetadataKindProperties:
  66. return [self titleWithBaseName:@"Properties"];
  67. case FLEXMetadataKindClassProperties:
  68. return [self titleWithBaseName:@"Class Properties"];
  69. case FLEXMetadataKindIvars:
  70. return [self titleWithBaseName:@"Ivars"];
  71. case FLEXMetadataKindMethods:
  72. return [self titleWithBaseName:@"Methods"];
  73. case FLEXMetadataKindClassMethods:
  74. return [self titleWithBaseName:@"Class Methods"];
  75. case FLEXMetadataKindClassHierarchy:
  76. return [self titleWithBaseName:@"Class Hierarchy"];
  77. case FLEXMetadataKindProtocols:
  78. return [self titleWithBaseName:@"Protocols"];
  79. case FLEXMetadataKindOther:
  80. return @"Miscellaneous";
  81. }
  82. }
  83. - (NSInteger)numberOfRows {
  84. return self.metadata.count;
  85. }
  86. - (void)setFilterText:(NSString *)filterText {
  87. super.filterText = filterText;
  88. if (!self.filterText.length) {
  89. self.metadata = self.allMetadata;
  90. } else {
  91. self.metadata = [self.allMetadata flex_filtered:^BOOL(FLEXProperty *obj, NSUInteger idx) {
  92. return [obj.description localizedCaseInsensitiveContainsString:self.filterText];
  93. }];
  94. }
  95. }
  96. - (void)reloadData {
  97. switch (self.metadataKind) {
  98. case FLEXMetadataKindProperties:
  99. self.allMetadata = self.explorer.properties;
  100. break;
  101. case FLEXMetadataKindClassProperties:
  102. self.allMetadata = self.explorer.classProperties;
  103. break;
  104. case FLEXMetadataKindIvars:
  105. self.allMetadata = self.explorer.ivars;
  106. break;
  107. case FLEXMetadataKindMethods:
  108. self.allMetadata = self.explorer.methods;
  109. break;
  110. case FLEXMetadataKindClassMethods:
  111. self.allMetadata = self.explorer.classMethods;
  112. break;
  113. case FLEXMetadataKindProtocols:
  114. self.allMetadata = self.explorer.conformedProtocols;
  115. break;
  116. case FLEXMetadataKindClassHierarchy:
  117. self.allMetadata = self.explorer.classHierarchy;
  118. break;
  119. case FLEXMetadataKindOther:
  120. self.allMetadata = @[self.explorer.instanceSize, self.explorer.imageName];
  121. break;
  122. }
  123. // Remove excluded metadata
  124. if (self.excludedMetadata.count) {
  125. id filterBlock = ^BOOL(id<FLEXRuntimeMetadata> obj, NSUInteger idx) {
  126. return ![self.excludedMetadata containsObject:obj.name];
  127. };
  128. // Filter exclusions and sort
  129. self.allMetadata = [[self.allMetadata flex_filtered:filterBlock]
  130. sortedArrayUsingSelector:@selector(compare:)
  131. ];
  132. }
  133. // Re-filter data
  134. self.filterText = self.filterText;
  135. }
  136. - (BOOL)canSelectRow:(NSInteger)row {
  137. UITableViewCellAccessoryType accessory = [self accessoryTypeForRow:row];
  138. return accessory == UITableViewCellAccessoryDisclosureIndicator ||
  139. accessory == UITableViewCellAccessoryDetailDisclosureButton;
  140. }
  141. - (NSString *)reuseIdentifierForRow:(NSInteger)row {
  142. return [self.metadata[row] reuseIdentifierWithTarget:self.explorer.object] ?: kFLEXCodeFontCell;
  143. }
  144. - (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
  145. return [self.metadata[row] viewerWithTarget:self.explorer.object];
  146. }
  147. - (void (^)(__kindof UIViewController *))didPressInfoButtonAction:(NSInteger)row {
  148. return ^(UIViewController *host) {
  149. [host.navigationController pushViewController:[self editorForRow:row] animated:YES];
  150. };
  151. }
  152. - (UIViewController *)editorForRow:(NSInteger)row {
  153. return [self.metadata[row] editorWithTarget:self.explorer.object];
  154. }
  155. - (void)configureCell:(__kindof FLEXTableViewCell *)cell forRow:(NSInteger)row {
  156. cell.titleLabel.text = [self titleForRow:row];
  157. cell.subtitleLabel.text = [self subtitleForRow:row];
  158. cell.accessoryType = [self accessoryTypeForRow:row];
  159. }
  160. #if FLEX_AT_LEAST_IOS13_SDK
  161. - (NSString *)menuSubtitleForRow:(NSInteger)row {
  162. return [self.metadata[row] contextualSubtitleWithTarget:self.explorer.object];
  163. }
  164. - (NSArray<UIMenuElement *> *)menuItemsForRow:(NSInteger)row sender:(UIViewController *)sender {
  165. NSArray<UIMenuElement *> *existingItems = [super menuItemsForRow:row sender:sender];
  166. // These two metadata kinds don't any of the additional options below
  167. switch (self.metadataKind) {
  168. case FLEXMetadataKindClassHierarchy:
  169. case FLEXMetadataKindOther:
  170. return existingItems;
  171. default: break;
  172. }
  173. id<FLEXRuntimeMetadata> metadata = self.metadata[row];
  174. NSMutableArray<UIMenuElement *> *menuItems = [NSMutableArray new];
  175. [menuItems addObject:[UIAction
  176. actionWithTitle:@"Explore Metadata"
  177. image:nil
  178. identifier:nil
  179. handler:^(__kindof UIAction *action) {
  180. [sender.navigationController pushViewController:[FLEXObjectExplorerFactory
  181. explorerViewControllerForObject:metadata
  182. ] animated:YES];
  183. }
  184. ]];
  185. [menuItems addObjectsFromArray:[metadata
  186. additionalActionsWithTarget:self.explorer.object sender:sender
  187. ]];
  188. [menuItems addObjectsFromArray:existingItems];
  189. return menuItems.copy;
  190. }
  191. - (NSArray<NSString *> *)copyMenuItemsForRow:(NSInteger)row {
  192. return [self.metadata[row] copiableMetadataWithTarget:self.explorer.object];
  193. }
  194. #endif
  195. @end