LKS_PerspectiveDataSource.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. #ifdef SHOULD_COMPILE_LOOKIN_SERVER
  2. //
  3. // LKS_PerspectiveDataSource.m
  4. // LookinServer
  5. //
  6. // Created by Li Kai on 2019/5/17.
  7. // https://lookin.work
  8. //
  9. #import "LKS_PerspectiveDataSource.h"
  10. #import "UIColor+LookinServer.h"
  11. #import "LookinDisplayItem.h"
  12. #import "LookinHierarchyInfo.h"
  13. #import "LookinServerDefines.h"
  14. #import "LKS_PerspectiveLayer.h"
  15. @interface LKS_PerspectiveDataSource ()
  16. @property(nonatomic, copy, readwrite) NSArray<LookinDisplayItem *> *flatItems;
  17. @property(nonatomic, copy, readwrite) NSArray<LookinDisplayItem *> *displayingFlatItems;
  18. /**
  19. key 是 rgba 字符串,value 是 alias 字符串数组,比如:
  20. @{
  21. @"(255, 255, 255, 1)": @[@"MyWhite", @"MainThemeWhite"],
  22. @"(255, 0, 0, 0.5)": @[@"BestRed", @"TransparentRed"]
  23. };
  24. */
  25. @property(nonatomic, strong) NSDictionary<NSString *, NSArray<NSString *> *> *colorToAliasMap;
  26. @end
  27. @implementation LKS_PerspectiveDataSource
  28. - (instancetype)initWithHierarchyInfo:(LookinHierarchyInfo *)info {
  29. if (self = [self init]) {
  30. _rawHierarchyInfo = info;
  31. // [self _setUpColors];
  32. // 打平为二维数组
  33. self.flatItems = [LookinDisplayItem flatItemsFromHierarchicalItems:info.displayItems];
  34. // 设置 preferToBeCollapsed 属性
  35. NSSet<NSString *> *classesPreferredToCollapse = [NSSet setWithObjects:@"UILabel", @"UIPickerView", @"UIProgressView", @"UIActivityIndicatorView", @"UIAlertView", @"UIActionSheet", @"UISearchBar", @"UIButton", @"UITextView", @"UIDatePicker", @"UIPageControl", @"UISegmentedControl", @"UITextField", @"UISlider", @"UISwitch", @"UIVisualEffectView", @"UIImageView", @"WKCommonWebView", @"UITextEffectsWindow", @"LKS_LocalInspectContainerWindow", nil];
  36. if (info.collapsedClassList.count) {
  37. classesPreferredToCollapse = [classesPreferredToCollapse setByAddingObjectsFromArray:info.collapsedClassList];
  38. }
  39. // no preview
  40. NSSet<NSString *> *classesWithNoPreview = [NSSet setWithArray:@[@"UITextEffectsWindow", @"UIRemoteKeyboardWindow", @"LKS_LocalInspectContainerWindow"]];
  41. [self.flatItems enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  42. if ([obj itemIsKindOfClassesWithNames:classesPreferredToCollapse]) {
  43. [obj enumerateSelfAndChildren:^(LookinDisplayItem *item) {
  44. item.preferToBeCollapsed = YES;
  45. }];
  46. }
  47. if (obj.indentLevel == 0) {
  48. if ([obj itemIsKindOfClassesWithNames:classesWithNoPreview]) {
  49. obj.noPreview = YES;
  50. }
  51. }
  52. }];
  53. // 设置展开和折叠
  54. LookinDisplayItem *shouldSelectedItem;
  55. [self _adjustExpansionWithPreferedSelectedItem:&shouldSelectedItem];
  56. // 设置选中
  57. if (!shouldSelectedItem) {
  58. shouldSelectedItem = self.flatItems.firstObject;
  59. }
  60. self.selectedItem = shouldSelectedItem;
  61. }
  62. return self;
  63. }
  64. - (NSInteger)numberOfRows {
  65. return self.displayingFlatItems.count;
  66. }
  67. - (LookinDisplayItem *)itemAtRow:(NSInteger)index {
  68. return [self.displayingFlatItems lookin_safeObjectAtIndex:index];
  69. }
  70. - (NSInteger)rowForItem:(LookinDisplayItem *)item {
  71. NSInteger row = [self.displayingFlatItems indexOfObject:item];
  72. return row;
  73. }
  74. - (void)setSelectedItem:(LookinDisplayItem *)selectedItem {
  75. if (_selectedItem == selectedItem) {
  76. return;
  77. }
  78. _selectedItem.isSelected = NO;
  79. _selectedItem = selectedItem;
  80. _selectedItem.isSelected = YES;
  81. if ([self.hierarchyView respondsToSelector:@selector(dataSourceDidChangeSelectedItem:)]) {
  82. [self.hierarchyView dataSourceDidChangeSelectedItem:self];
  83. }
  84. if ([self.perspectiveLayer respondsToSelector:@selector(dataSourceDidChangeSelectedItem:)]) {
  85. [self.perspectiveLayer dataSourceDidChangeSelectedItem:self];
  86. }
  87. }
  88. - (void)collapseItem:(LookinDisplayItem *)item {
  89. if (!item.isExpandable) {
  90. return;
  91. }
  92. if (!item.isExpanded) {
  93. return;
  94. }
  95. item.isExpanded = NO;
  96. [self _updateDisplayingFlatItems];
  97. }
  98. - (void)expandItem:(LookinDisplayItem *)item {
  99. if (!item.isExpandable) {
  100. return;
  101. }
  102. if (item.isExpanded) {
  103. return;
  104. }
  105. item.isExpanded = YES;
  106. [self _updateDisplayingFlatItems];
  107. }
  108. #pragma mark - Colors
  109. - (NSArray<NSString *> *)aliasForColor:(UIColor *)color {
  110. if (!color) {
  111. return nil;
  112. }
  113. NSString *rgbaString = color.lks_rgbaString;
  114. NSArray<NSString *> *names = self.colorToAliasMap[rgbaString];
  115. return names;
  116. }
  117. //- (void)_setUpColors {
  118. // NSMutableDictionary<NSString *, NSMutableArray<NSString *> *> *colorToAliasMap = [NSMutableDictionary dictionary];
  119. //
  120. // /**
  121. // hierarchyInfo.colorAlias 可以有三种结构:
  122. // 1)key 是颜色别名,value 是 UIColor/UIColor。即 <NSString *, Color *>
  123. // 2)key 是一组颜色的标题,value 是 NSDictionary,而这个 NSDictionary 的 key 是颜色别名,value 是 UIColor / UIColor。即 <NSString *, NSDictionary<NSString *, Color *> *>
  124. // 3)以上两者混在一起
  125. // */
  126. // [self.rawHierarchyInfo.colorAlias enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull colorOrDict, BOOL * _Nonnull stop) {
  127. // if ([colorOrDict isKindOfClass:[UIColor class]]) {
  128. // NSString *colorDesc = [((UIColor *)colorOrDict) lks_rgbaString];
  129. // if (colorDesc) {
  130. // if (!colorToAliasMap[colorDesc]) {
  131. // colorToAliasMap[colorDesc] = [NSMutableArray array];
  132. // }
  133. // [colorToAliasMap[colorDesc] addObject:key];
  134. // }
  135. //
  136. // } else if ([colorOrDict isKindOfClass:[NSDictionary class]]) {
  137. // [((NSDictionary *)colorOrDict) enumerateKeysAndObjectsUsingBlock:^(NSString *colorAliaName, UIColor *colorObj, BOOL * _Nonnull stop) {
  138. // NSString *colorDesc = colorObj.lks_rgbaString;
  139. // if (colorDesc) {
  140. // if (!colorToAliasMap[colorDesc]) {
  141. // colorToAliasMap[colorDesc] = [NSMutableArray array];
  142. // }
  143. // [colorToAliasMap[colorDesc] addObject:colorAliaName];
  144. // }
  145. // }];
  146. //
  147. // } else {
  148. // NSAssert(NO, @"");
  149. // }
  150. // }];
  151. // self.colorToAliasMap = colorToAliasMap;
  152. //}
  153. #pragma mark - Others
  154. - (void)_adjustExpansionWithPreferedSelectedItem:(LookinDisplayItem **)selectedItem {
  155. [self.flatItems enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  156. obj.hasDeterminedExpansion = NO;
  157. if (!obj.isExpandable) {
  158. obj.hasDeterminedExpansion = YES;
  159. return;
  160. }
  161. }];
  162. LookinDisplayItem *keyWindowItem = [self.rawHierarchyInfo.displayItems lookin_firstFiltered:^BOOL(LookinDisplayItem *windowItem) {
  163. return windowItem.representedAsKeyWindow;
  164. }];
  165. if (!keyWindowItem) {
  166. keyWindowItem = self.rawHierarchyInfo.displayItems.firstObject;
  167. }
  168. [self.rawHierarchyInfo.displayItems enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull windowItem, NSUInteger idx, BOOL * _Nonnull stop) {
  169. if (windowItem == keyWindowItem) {
  170. return;
  171. }
  172. // 非 keyWindow 上的都折叠起来
  173. [[LookinDisplayItem flatItemsFromHierarchicalItems:@[windowItem]] enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  174. if (obj.hasDeterminedExpansion) {
  175. return;
  176. }
  177. obj.isExpanded = NO;
  178. obj.hasDeterminedExpansion = YES;
  179. }];
  180. }];
  181. NSArray<LookinDisplayItem *> *UITransitionViewItems = [keyWindowItem.subitems lookin_filter:^BOOL(LookinDisplayItem *obj) {
  182. return [obj.title isEqualToString:@"UITransitionView"];
  183. }];
  184. [UITransitionViewItems enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  185. if (obj.hasDeterminedExpansion) {
  186. return;
  187. }
  188. if (idx == (UITransitionViewItems.count - 1)) {
  189. // 展开最后一个 UITransitionView
  190. obj.isExpanded = YES;
  191. } else {
  192. // 折叠前几个 UITransitionView
  193. obj.isExpanded = NO;
  194. }
  195. obj.hasDeterminedExpansion = YES;
  196. }];
  197. NSMutableArray<LookinDisplayItem *> *viewControllerItems = [NSMutableArray array];
  198. [[LookinDisplayItem flatItemsFromHierarchicalItems:@[keyWindowItem]] enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  199. if (!!obj.hostViewControllerObject) {
  200. [viewControllerItems addObject:obj];
  201. return;
  202. }
  203. if (obj.hasDeterminedExpansion) {
  204. return;
  205. }
  206. if (obj.inNoPreviewHierarchy || obj.preferToBeCollapsed || obj.inHiddenHierarchy) {
  207. // 把 noPreview 和 UIButton 之类常用控件叠起来
  208. obj.isExpanded = NO;
  209. obj.hasDeterminedExpansion = YES;
  210. return;
  211. }
  212. if ([obj itemIsKindOfClassesWithNames:[NSSet setWithObjects:@"UINavigationBar", @"UITabBar", nil]]) {
  213. // 把 NavigationBar 和 TabBar 折叠起来
  214. [obj enumerateSelfAndChildren:^(LookinDisplayItem *item) {
  215. if (item.hasDeterminedExpansion) {
  216. return;
  217. }
  218. item.isExpanded = NO;
  219. item.hasDeterminedExpansion = YES;
  220. }];
  221. return;
  222. }
  223. }];
  224. // 从 viewController 开始算向 leaf 多推 3 层
  225. [viewControllerItems enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(LookinDisplayItem * _Nonnull viewControllerItem, NSUInteger idx, BOOL * _Nonnull stop) {
  226. [viewControllerItem enumerateAncestors:^(LookinDisplayItem *item, BOOL *stop) {
  227. // 把 viewController 的 ancestors 都展开
  228. if (item.hasDeterminedExpansion) {
  229. return;
  230. }
  231. item.isExpanded = YES;
  232. item.hasDeterminedExpansion = YES;
  233. }];
  234. BOOL hasTableOrCollectionView = [viewControllerItem.subitems.firstObject itemIsKindOfClassesWithNames:[NSSet setWithObjects:@"UITableView", @"UICollectionView", nil]];
  235. // 如果是那种典型的 UITableView 或 UICollectionView 的话,则向 leaf 方向推进 2 层(这样就可以让 cell 恰好露出来而不露出来 cell 的 contentView),否则就推 3 层
  236. NSUInteger indentsForward = hasTableOrCollectionView ? 2 : 3;
  237. [viewControllerItem enumerateSelfAndChildren:^(LookinDisplayItem *item) {
  238. if (item.hasDeterminedExpansion) {
  239. return;
  240. }
  241. // 向 leaf 方向推 2 或 3 层
  242. if (item.indentLevel < viewControllerItem.indentLevel + indentsForward) {
  243. item.isExpanded = YES;
  244. item.hasDeterminedExpansion = YES;
  245. }
  246. }];
  247. }];
  248. // 剩下未处理的都折叠
  249. [self.flatItems enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  250. if (obj.hasDeterminedExpansion) {
  251. return;
  252. }
  253. obj.isExpanded = NO;
  254. }];
  255. if (selectedItem) {
  256. *selectedItem = viewControllerItems.lastObject;
  257. }
  258. [self _updateDisplayingFlatItems];
  259. }
  260. - (void)_updateDisplayingFlatItems {
  261. __block NSInteger maxIndentationLevel = 0;
  262. NSMutableArray<LookinDisplayItem *> *displayingItems = [NSMutableArray array];
  263. [self.flatItems enumerateObjectsUsingBlock:^(LookinDisplayItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  264. if (obj.displayingInHierarchy) {
  265. maxIndentationLevel = MAX(maxIndentationLevel, obj.indentLevel);
  266. [displayingItems addObject:obj];
  267. }
  268. }];
  269. self.displayingFlatItems = displayingItems;
  270. if ([self.hierarchyView respondsToSelector:@selector(dataSourceDidChangeDisplayItems:)]) {
  271. [self.hierarchyView dataSourceDidChangeDisplayItems:self];
  272. }
  273. if ([self.perspectiveLayer respondsToSelector:@selector(dataSourceDidChangeDisplayItems:)]) {
  274. [self.perspectiveLayer dataSourceDidChangeDisplayItems:self];
  275. }
  276. }
  277. @end
  278. #endif /* SHOULD_COMPILE_LOOKIN_SERVER */