LKS_PerspectiveDataSource.m 12 KB

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