UIView+LookinServer.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //
  2. // UIView+LookinServer.m
  3. // LookinServer
  4. //
  5. // Created by Li Kai on 2019/3/19.
  6. // https://lookin.work
  7. //
  8. #import "UIView+LookinServer.h"
  9. #import <objc/runtime.h>
  10. #import "LookinObject.h"
  11. #import "LookinAutoLayoutConstraint.h"
  12. #import "LookinServerDefines.h"
  13. @implementation UIView (LookinServer)
  14. + (void)load {
  15. static dispatch_once_t onceToken;
  16. dispatch_once(&onceToken, ^{
  17. Method oriMethod = class_getInstanceMethod([self class], @selector(initWithFrame:));
  18. Method newMethod = class_getInstanceMethod([self class], @selector(lks_initWithFrame:));
  19. method_exchangeImplementations(oriMethod, newMethod);
  20. oriMethod = class_getInstanceMethod([self class], @selector(initWithCoder:));
  21. newMethod = class_getInstanceMethod([self class], @selector(lks_initWithCoder:));
  22. method_exchangeImplementations(oriMethod, newMethod);
  23. });
  24. }
  25. - (instancetype)lks_initWithFrame:(CGRect)frame {
  26. UIView *view = [self lks_initWithFrame:frame];
  27. view.layer.lks_hostView = view;
  28. return view;
  29. }
  30. - (instancetype)lks_initWithCoder:(NSCoder *)coder {
  31. UIView *view = [self lks_initWithCoder:coder];
  32. view.layer.lks_hostView = view;
  33. return view;
  34. }
  35. - (void)setLks_hostViewController:(UIViewController *)lks_hostViewController {
  36. [self lookin_bindObjectWeakly:lks_hostViewController forKey:@"lks_hostViewController"];
  37. }
  38. - (UIViewController *)lks_hostViewController {
  39. return [self lookin_getBindObjectForKey:@"lks_hostViewController"];
  40. }
  41. - (UIView *)lks_subviewAtPoint:(CGPoint)point preferredClasses:(NSArray<Class> *)preferredClasses {
  42. BOOL isPreferredClassForSelf = [preferredClasses lookin_any:^BOOL(Class obj) {
  43. return [self isKindOfClass:obj];
  44. }];
  45. if (isPreferredClassForSelf) {
  46. return self;
  47. }
  48. UIView *targetView = [self.subviews lookin_lastFiltered:^BOOL(__kindof UIView *obj) {
  49. if (obj.layer.lks_isLookinPrivateLayer) {
  50. return NO;
  51. }
  52. if (obj.hidden || obj.alpha <= 0.01) {
  53. return NO;
  54. }
  55. BOOL contains = CGRectContainsPoint(obj.frame, point);
  56. return contains;
  57. }];
  58. if (!targetView) {
  59. return self;
  60. }
  61. CGPoint newPoint = [targetView convertPoint:point fromView:self];
  62. targetView = [targetView lks_subviewAtPoint:newPoint preferredClasses:preferredClasses];
  63. return targetView;
  64. }
  65. - (CGSize)lks_bestSize {
  66. return [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
  67. }
  68. - (CGFloat)lks_bestWidth {
  69. return self.lks_bestSize.width;
  70. }
  71. - (CGFloat)lks_bestHeight {
  72. return self.lks_bestSize.height;
  73. }
  74. - (void)setLks_isChildrenViewOfTabBar:(BOOL)lks_isChildrenViewOfTabBar {
  75. [self lookin_bindBOOL:lks_isChildrenViewOfTabBar forKey:@"lks_isChildrenViewOfTabBar"];
  76. }
  77. - (BOOL)lks_isChildrenViewOfTabBar {
  78. return [self lookin_getBindBOOLForKey:@"lks_isChildrenViewOfTabBar"];
  79. }
  80. - (void)setLks_verticalContentHuggingPriority:(float)lks_verticalContentHuggingPriority {
  81. [self setContentHuggingPriority:lks_verticalContentHuggingPriority forAxis:UILayoutConstraintAxisVertical];
  82. }
  83. - (float)lks_verticalContentHuggingPriority {
  84. return [self contentHuggingPriorityForAxis:UILayoutConstraintAxisVertical];
  85. }
  86. - (void)setLks_horizontalContentHuggingPriority:(float)lks_horizontalContentHuggingPriority {
  87. [self setContentHuggingPriority:lks_horizontalContentHuggingPriority forAxis:UILayoutConstraintAxisHorizontal];
  88. }
  89. - (float)lks_horizontalContentHuggingPriority {
  90. return [self contentHuggingPriorityForAxis:UILayoutConstraintAxisHorizontal];
  91. }
  92. - (void)setLks_verticalContentCompressionResistancePriority:(float)lks_verticalContentCompressionResistancePriority {
  93. [self setContentCompressionResistancePriority:lks_verticalContentCompressionResistancePriority forAxis:UILayoutConstraintAxisVertical];
  94. }
  95. - (float)lks_verticalContentCompressionResistancePriority {
  96. return [self contentCompressionResistancePriorityForAxis:UILayoutConstraintAxisVertical];
  97. }
  98. - (void)setLks_horizontalContentCompressionResistancePriority:(float)lks_horizontalContentCompressionResistancePriority {
  99. [self setContentCompressionResistancePriority:lks_horizontalContentCompressionResistancePriority forAxis:UILayoutConstraintAxisHorizontal];
  100. }
  101. - (float)lks_horizontalContentCompressionResistancePriority {
  102. return [self contentCompressionResistancePriorityForAxis:UILayoutConstraintAxisHorizontal];
  103. }
  104. + (void)lks_rebuildGlobalInvolvedRawConstraints {
  105. [[[UIApplication sharedApplication].windows copy] enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
  106. [self lks_removeInvolvedRawConstraintsForViewsRootedByView:window];
  107. }];
  108. [[[UIApplication sharedApplication].windows copy] enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
  109. [self lks_addInvolvedRawConstraintsForViewsRootedByView:window];
  110. }];
  111. }
  112. + (void)lks_addInvolvedRawConstraintsForViewsRootedByView:(UIView *)rootView {
  113. [rootView.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull constraint, NSUInteger idx, BOOL * _Nonnull stop) {
  114. UIView *firstView = constraint.firstItem;
  115. if ([firstView isKindOfClass:[UIView class]] && ![firstView.lks_involvedRawConstraints containsObject:constraint]) {
  116. if (!firstView.lks_involvedRawConstraints) {
  117. firstView.lks_involvedRawConstraints = [NSMutableArray array];
  118. }
  119. [firstView.lks_involvedRawConstraints addObject:constraint];
  120. }
  121. UIView *secondView = constraint.secondItem;
  122. if ([secondView isKindOfClass:[UIView class]] && ![secondView.lks_involvedRawConstraints containsObject:constraint]) {
  123. if (!secondView.lks_involvedRawConstraints) {
  124. secondView.lks_involvedRawConstraints = [NSMutableArray array];
  125. }
  126. [secondView.lks_involvedRawConstraints addObject:constraint];
  127. }
  128. }];
  129. [rootView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
  130. [self lks_addInvolvedRawConstraintsForViewsRootedByView:subview];
  131. }];
  132. }
  133. + (void)lks_removeInvolvedRawConstraintsForViewsRootedByView:(UIView *)rootView {
  134. [rootView.lks_involvedRawConstraints removeAllObjects];
  135. [rootView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
  136. [self lks_removeInvolvedRawConstraintsForViewsRootedByView:subview];
  137. }];
  138. }
  139. - (void)setLks_involvedRawConstraints:(NSMutableArray<NSLayoutConstraint *> *)lks_involvedRawConstraints {
  140. [self lookin_bindObject:lks_involvedRawConstraints forKey:@"lks_involvedRawConstraints"];
  141. }
  142. - (NSMutableArray<NSLayoutConstraint *> *)lks_involvedRawConstraints {
  143. return [self lookin_getBindObjectForKey:@"lks_involvedRawConstraints"];
  144. }
  145. - (NSArray<LookinAutoLayoutConstraint *> *)lks_constraints {
  146. /**
  147. - lks_involvedRawConstraints 保存了牵扯到了 self 的所有的 constraints(包括未生效的,但不包括 inactive 的,整个产品逻辑都是直接忽略 inactive 的 constraints)
  148. - 通过 constraintsAffectingLayoutForAxis 可以拿到会影响 self 布局的所有已生效的 constraints(这里称之为 effectiveConstraints)
  149. - 很多情况下,一条 constraint 会出现在 effectiveConstraints 里但不会出现在 lks_involvedRawConstraints 里,比如:
  150. · UIWindow 拥有 minX, minY, width, height 四个 effectiveConstraints,但 lks_involvedRawConstraints 为空,因为它的 constraints 属性为空(这一点不知道为啥,但 Xcode Inspector 和 Reveal 确实也不会显示这四个 constraints)
  151. · 如果设置了 View1 的 center 和 superview 的 center 保持一致,则 superview 的 width 和 height 也会出现在 effectiveConstraints 里,但不会出现在 lks_involvedRawConstraints 里(这点可以理解,毕竟这种场景下 superview 的 width 和 height 确实会影响到 View1)
  152. */
  153. NSMutableArray<NSLayoutConstraint *> *effectiveConstraints = [NSMutableArray array];
  154. [effectiveConstraints addObjectsFromArray:[self constraintsAffectingLayoutForAxis:UILayoutConstraintAxisHorizontal]];
  155. [effectiveConstraints addObjectsFromArray:[self constraintsAffectingLayoutForAxis:UILayoutConstraintAxisVertical]];
  156. NSArray<LookinAutoLayoutConstraint *> *lookinConstraints = [self.lks_involvedRawConstraints lookin_map:^id(NSUInteger idx, __kindof NSLayoutConstraint *constraint) {
  157. BOOL isEffective = [effectiveConstraints containsObject:constraint];
  158. LookinConstraintItemType firstItemType = [self _lks_constraintItemTypeForItem:constraint.firstItem];
  159. LookinConstraintItemType secondItemType = [self _lks_constraintItemTypeForItem:constraint.secondItem];
  160. LookinAutoLayoutConstraint *lookinConstraint = [LookinAutoLayoutConstraint instanceFromNSConstraint:constraint isEffective:isEffective firstItemType:firstItemType secondItemType:secondItemType];
  161. return lookinConstraint;
  162. }];
  163. return lookinConstraints.count ? lookinConstraints : nil;
  164. }
  165. - (LookinConstraintItemType)_lks_constraintItemTypeForItem:(id)item {
  166. if (!item) {
  167. return LookinConstraintItemTypeNil;
  168. }
  169. if (item == self) {
  170. return LookinConstraintItemTypeSelf;
  171. }
  172. if (item == self.superview) {
  173. return LookinConstraintItemTypeSuper;
  174. }
  175. // 在 runtime 时,这里会遇到的 UILayoutGuide 和 _UILayoutGuide 居然是 UIView 的子类,不知道是看错了还是有什么玄机,所以在判断是否是 UIView 之前要先判断这个
  176. if (@available(iOS 9.0, *)) {
  177. if ([item isKindOfClass:[UILayoutGuide class]]) {
  178. return LookinConstraintItemTypeLayoutGuide;
  179. }
  180. }
  181. if ([[item lks_shortClassName] isEqualToString:@"_UILayoutGuide"]) {
  182. return LookinConstraintItemTypeLayoutGuide;
  183. }
  184. if ([item isKindOfClass:[UIView class]]) {
  185. return LookinConstraintItemTypeView;
  186. }
  187. NSAssert(NO, @"");
  188. return LookinConstraintItemTypeUnknown;
  189. }
  190. @end