FLEXMetadataSection.m 7.8 KB

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