1
This commit is contained in:
26
Pods/LookinServer/Src/Main/Server/Others/LKSConfigManager.h
generated
Normal file
26
Pods/LookinServer/Src/Main/Server/Others/LKSConfigManager.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKSConfigManager.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/1/10.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface LKSConfigManager : NSObject
|
||||
|
||||
+ (NSArray<NSString *> *)collapsedClassList;
|
||||
|
||||
+ (NSDictionary<NSString *, UIColor *> *)colorAlias;
|
||||
|
||||
+ (BOOL)shouldCaptureScreenshotOfLayer:(CALayer *)layer;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
195
Pods/LookinServer/Src/Main/Server/Others/LKSConfigManager.m
generated
Normal file
195
Pods/LookinServer/Src/Main/Server/Others/LKSConfigManager.m
generated
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKSConfigManager.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/1/10.
|
||||
//
|
||||
|
||||
#import "LKSConfigManager.h"
|
||||
#import "NSArray+Lookin.h"
|
||||
#import "CALayer+LookinServer.h"
|
||||
|
||||
@implementation LKSConfigManager
|
||||
|
||||
+ (NSArray<NSString *> *)collapsedClassList {
|
||||
NSArray<NSString *> *result = [self queryCollapsedClassListWithClass:[NSObject class] selector:@"lookin_collapsedClassList"];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Legacy logic. Deprecated.
|
||||
Class configClass = NSClassFromString(@"LookinConfig");
|
||||
if (!configClass) {
|
||||
return nil;
|
||||
}
|
||||
NSArray<NSString *> *legacyCodeResult = [self queryCollapsedClassListWithClass:configClass selector:@"collapsedClasses"];
|
||||
return legacyCodeResult;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)queryCollapsedClassListWithClass:(Class)class selector:(NSString *)selectorName {
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
if (![class respondsToSelector:selector]) {
|
||||
return nil;
|
||||
}
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[class methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:class];
|
||||
[invocation setSelector:selector];
|
||||
[invocation invoke];
|
||||
void *arrayValue;
|
||||
[invocation getReturnValue:&arrayValue];
|
||||
id classList = (__bridge id)(arrayValue);
|
||||
|
||||
if ([classList isKindOfClass:[NSArray class]]) {
|
||||
NSArray *validClassList = [((NSArray *)classList) lookin_filter:^BOOL(id obj) {
|
||||
return [obj isKindOfClass:[NSString class]];
|
||||
}];
|
||||
return [validClassList copy];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, UIColor *> *)colorAlias {
|
||||
NSDictionary<NSString *, UIColor *> *result = [self queryColorAliasWithClass:[NSObject class] selector:@"lookin_colorAlias"];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Legacy logic. Deprecated.
|
||||
Class configClass = NSClassFromString(@"LookinConfig");
|
||||
if (!configClass) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary<NSString *, UIColor *> *legacyCodeResult = [self queryColorAliasWithClass:configClass selector:@"colors"];
|
||||
return legacyCodeResult;
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, UIColor *> *)queryColorAliasWithClass:(Class)class selector:(NSString *)selectorName {
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
if (![class respondsToSelector:selector]) {
|
||||
return nil;
|
||||
}
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[class methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:class];
|
||||
[invocation setSelector:selector];
|
||||
[invocation invoke];
|
||||
void *dictValue;
|
||||
[invocation getReturnValue:&dictValue];
|
||||
id colorAlias = (__bridge id)(dictValue);
|
||||
|
||||
if ([colorAlias isKindOfClass:[NSDictionary class]]) {
|
||||
NSMutableDictionary *validDictionary = [NSMutableDictionary dictionary];
|
||||
[(NSDictionary *)colorAlias enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if ([key isKindOfClass:[NSString class]]) {
|
||||
if ([obj isKindOfClass:[UIColor class]]) {
|
||||
[validDictionary setObject:obj forKey:key];
|
||||
|
||||
} else if ([obj isKindOfClass:[NSDictionary class]]) {
|
||||
__block BOOL isValidSubDict = YES;
|
||||
[((NSDictionary *)obj) enumerateKeysAndObjectsUsingBlock:^(id _Nonnull subKey, id _Nonnull subObj, BOOL * _Nonnull stop) {
|
||||
if (![subKey isKindOfClass:[NSString class]] || ![subObj isKindOfClass:[UIColor class]]) {
|
||||
isValidSubDict = NO;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
if (isValidSubDict) {
|
||||
[validDictionary setObject:obj forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
return [validDictionary copy];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (BOOL)shouldCaptureScreenshotOfLayer:(CALayer *)layer {
|
||||
if (!layer) {
|
||||
return YES;
|
||||
}
|
||||
if (![self shouldCaptureImageOfLayer:layer]) {
|
||||
return NO;
|
||||
}
|
||||
UIView *view = layer.lks_hostView;
|
||||
if (!view) {
|
||||
return YES;
|
||||
}
|
||||
if (![self shouldCaptureImageOfView:view]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)shouldCaptureImageOfLayer:(CALayer *)layer {
|
||||
if (!layer) {
|
||||
return YES;
|
||||
}
|
||||
SEL selector = NSSelectorFromString(@"lookin_shouldCaptureImageOfLayer:");
|
||||
if ([NSObject respondsToSelector:selector]) {
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSObject methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:[NSObject class]];
|
||||
[invocation setSelector:selector];
|
||||
[invocation setArgument:&layer atIndex:2];
|
||||
[invocation invoke];
|
||||
BOOL resultValue = YES;
|
||||
[invocation getReturnValue:&resultValue];
|
||||
if (!resultValue) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
SEL selector2 = NSSelectorFromString(@"lookin_shouldCaptureImage");
|
||||
if ([layer respondsToSelector:selector2]) {
|
||||
NSInvocation *invocation2 = [NSInvocation invocationWithMethodSignature:[layer methodSignatureForSelector:selector2]];
|
||||
[invocation2 setTarget:layer];
|
||||
[invocation2 setSelector:selector2];
|
||||
[invocation2 invoke];
|
||||
BOOL resultValue2 = YES;
|
||||
[invocation2 getReturnValue:&resultValue2];
|
||||
if (!resultValue2) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)shouldCaptureImageOfView:(UIView *)view {
|
||||
if (!view) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
SEL selector = NSSelectorFromString(@"lookin_shouldCaptureImageOfView:");
|
||||
if ([NSObject respondsToSelector:selector]) {
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSObject methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:[NSObject class]];
|
||||
[invocation setSelector:selector];
|
||||
[invocation setArgument:&view atIndex:2];
|
||||
[invocation invoke];
|
||||
BOOL resultValue = YES;
|
||||
[invocation getReturnValue:&resultValue];
|
||||
if (!resultValue) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
SEL selector2 = NSSelectorFromString(@"lookin_shouldCaptureImage");
|
||||
if ([view respondsToSelector:selector2]) {
|
||||
NSInvocation *invocation2 = [NSInvocation invocationWithMethodSignature:[view methodSignatureForSelector:selector2]];
|
||||
[invocation2 setTarget:view];
|
||||
[invocation2 setSelector:selector2];
|
||||
[invocation2 invoke];
|
||||
BOOL resultValue2 = YES;
|
||||
[invocation2 getReturnValue:&resultValue2];
|
||||
if (!resultValue2) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Others/LKS_AttrGroupsMaker.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Others/LKS_AttrGroupsMaker.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_AttrGroupsMaker.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/6.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
@class LookinAttributesGroup;
|
||||
|
||||
@interface LKS_AttrGroupsMaker : NSObject
|
||||
|
||||
+ (NSArray<LookinAttributesGroup *> *)attrGroupsForLayer:(CALayer *)layer;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
302
Pods/LookinServer/Src/Main/Server/Others/LKS_AttrGroupsMaker.m
generated
Normal file
302
Pods/LookinServer/Src/Main/Server/Others/LKS_AttrGroupsMaker.m
generated
Normal file
@@ -0,0 +1,302 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_AttrGroupsMaker.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/6.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LookinAttributesGroup.h"
|
||||
#import "LookinAttributesSection.h"
|
||||
#import "LookinAttribute.h"
|
||||
#import "LookinDashboardBlueprint.h"
|
||||
#import "LookinIvarTrace.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
#import "LookinServerDefines.h"
|
||||
|
||||
@implementation LKS_AttrGroupsMaker
|
||||
|
||||
+ (NSArray<LookinAttributesGroup *> *)attrGroupsForLayer:(CALayer *)layer {
|
||||
if (!layer) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
NSArray<LookinAttributesGroup *> *groups = [[LookinDashboardBlueprint groupIDs] lookin_map:^id(NSUInteger idx, LookinAttrGroupIdentifier groupID) {
|
||||
LookinAttributesGroup *group = [LookinAttributesGroup new];
|
||||
group.identifier = groupID;
|
||||
|
||||
NSArray<LookinAttrSectionIdentifier> *secIDs = [LookinDashboardBlueprint sectionIDsForGroupID:groupID];
|
||||
group.attrSections = [secIDs lookin_map:^id(NSUInteger idx, LookinAttrSectionIdentifier secID) {
|
||||
LookinAttributesSection *sec = [LookinAttributesSection new];
|
||||
sec.identifier = secID;
|
||||
|
||||
NSArray<LookinAttrIdentifier> *attrIDs = [LookinDashboardBlueprint attrIDsForSectionID:secID];
|
||||
sec.attributes = [attrIDs lookin_map:^id(NSUInteger idx, LookinAttrIdentifier attrID) {
|
||||
NSInteger minAvailableVersion = [LookinDashboardBlueprint minAvailableOSVersionWithAttrID:attrID];
|
||||
if (minAvailableVersion > 0 && (NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < minAvailableVersion)) {
|
||||
// iOS 版本过低不支持该属性
|
||||
return nil;
|
||||
}
|
||||
|
||||
id targetObj = nil;
|
||||
if ([LookinDashboardBlueprint isUIViewPropertyWithAttrID:attrID]) {
|
||||
targetObj = layer.lks_hostView;
|
||||
} else {
|
||||
targetObj = layer;
|
||||
}
|
||||
|
||||
if (targetObj) {
|
||||
Class targetClass = NSClassFromString([LookinDashboardBlueprint classNameWithAttrID:attrID]);
|
||||
if (![targetObj isKindOfClass:targetClass]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
LookinAttribute *attr = [self _attributeWithIdentifer:attrID targetObject:targetObj];
|
||||
return attr;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}];
|
||||
|
||||
if (sec.attributes.count) {
|
||||
return sec;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}];
|
||||
|
||||
if ([groupID isEqualToString:LookinAttrGroup_AutoLayout]) {
|
||||
// 这里特殊处理一下,如果 AutoLayout 里面不包含 Constraints 的话(只有 Hugging 和 Resistance),就丢弃掉这整个 AutoLayout 不显示
|
||||
BOOL hasConstraits = [group.attrSections lookin_any:^BOOL(LookinAttributesSection *obj) {
|
||||
return [obj.identifier isEqualToString:LookinAttrSec_AutoLayout_Constraints];
|
||||
}];
|
||||
if (!hasConstraits) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (group.attrSections.count) {
|
||||
return group;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}];
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
+ (LookinAttribute *)_attributeWithIdentifer:(LookinAttrIdentifier)identifier targetObject:(id)target {
|
||||
if (!target) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
LookinAttribute *attribute = [LookinAttribute new];
|
||||
attribute.identifier = identifier;
|
||||
|
||||
SEL getter = [LookinDashboardBlueprint getterWithAttrID:identifier];
|
||||
if (!getter) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
if (![target respondsToSelector:getter]) {
|
||||
// 比如某些 QMUI 的属性,不引入 QMUI 就会走到这个分支里
|
||||
return nil;
|
||||
}
|
||||
NSMethodSignature *signature = [target methodSignatureForSelector:getter];
|
||||
if (signature.numberOfArguments > 2) {
|
||||
NSAssert(NO, @"getter 不可以有参数");
|
||||
return nil;
|
||||
}
|
||||
if (strcmp([signature methodReturnType], @encode(void)) == 0) {
|
||||
NSAssert(NO, @"getter 返回值不能为 void");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
invocation.target = target;
|
||||
invocation.selector = getter;
|
||||
[invocation invoke];
|
||||
|
||||
const char *returnType = [signature methodReturnType];
|
||||
|
||||
if (strcmp(returnType, @encode(char)) == 0) {
|
||||
char targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeChar;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(int)) == 0) {
|
||||
int targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.value = @(targetValue);
|
||||
if ([LookinDashboardBlueprint enumListNameWithAttrID:identifier]) {
|
||||
attribute.attrType = LookinAttrTypeEnumInt;
|
||||
} else {
|
||||
attribute.attrType = LookinAttrTypeInt;
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(short)) == 0) {
|
||||
short targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeShort;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(long)) == 0) {
|
||||
long targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.value = @(targetValue);
|
||||
if ([LookinDashboardBlueprint enumListNameWithAttrID:identifier]) {
|
||||
attribute.attrType = LookinAttrTypeEnumLong;
|
||||
} else {
|
||||
attribute.attrType = LookinAttrTypeLong;
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(long long)) == 0) {
|
||||
long long targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeLongLong;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned char)) == 0) {
|
||||
unsigned char targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUnsignedChar;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned int)) == 0) {
|
||||
unsigned int targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUnsignedInt;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned short)) == 0) {
|
||||
unsigned short targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUnsignedShort;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned long)) == 0) {
|
||||
unsigned long targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUnsignedLong;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
|
||||
unsigned long long targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUnsignedLongLong;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(float)) == 0) {
|
||||
float targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeFloat;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(double)) == 0) {
|
||||
double targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeDouble;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(BOOL)) == 0) {
|
||||
BOOL targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeBOOL;
|
||||
attribute.value = @(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(SEL)) == 0) {
|
||||
SEL targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeSel;
|
||||
attribute.value = NSStringFromSelector(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(Class)) == 0) {
|
||||
Class targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeClass;
|
||||
attribute.value = NSStringFromClass(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGPoint)) == 0) {
|
||||
CGPoint targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeCGPoint;
|
||||
attribute.value = [NSValue valueWithCGPoint:targetValue];
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGVector)) == 0) {
|
||||
CGVector targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeCGVector;
|
||||
attribute.value = [NSValue valueWithCGVector:targetValue];
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGSize)) == 0) {
|
||||
CGSize targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeCGSize;
|
||||
attribute.value = [NSValue valueWithCGSize:targetValue];
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGRect)) == 0) {
|
||||
CGRect targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeCGRect;
|
||||
attribute.value = [NSValue valueWithCGRect:targetValue];
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGAffineTransform)) == 0) {
|
||||
CGAffineTransform targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeCGAffineTransform;
|
||||
attribute.value = [NSValue valueWithCGAffineTransform:targetValue];
|
||||
|
||||
} else if (strcmp(returnType, @encode(UIEdgeInsets)) == 0) {
|
||||
UIEdgeInsets targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUIEdgeInsets;
|
||||
attribute.value = [NSValue valueWithUIEdgeInsets:targetValue];
|
||||
|
||||
} else if (strcmp(returnType, @encode(UIOffset)) == 0) {
|
||||
UIOffset targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
attribute.attrType = LookinAttrTypeUIOffset;
|
||||
attribute.value = [NSValue valueWithUIOffset:targetValue];
|
||||
|
||||
} else {
|
||||
NSString *argType_string = [[NSString alloc] lookin_safeInitWithUTF8String:returnType];
|
||||
if ([argType_string hasPrefix:@"@"]) {
|
||||
__unsafe_unretained id returnObjValue;
|
||||
[invocation getReturnValue:&returnObjValue];
|
||||
|
||||
if (!returnObjValue && [LookinDashboardBlueprint hideIfNilWithAttrID:identifier]) {
|
||||
// 对于某些属性,若 value 为 nil 则不显示
|
||||
return nil;
|
||||
}
|
||||
|
||||
attribute.attrType = [LookinDashboardBlueprint objectAttrTypeWithAttrID:identifier];
|
||||
if (attribute.attrType == LookinAttrTypeUIColor) {
|
||||
if (returnObjValue == nil) {
|
||||
attribute.value = nil;
|
||||
} else if ([returnObjValue isKindOfClass:[UIColor class]] && [returnObjValue respondsToSelector:@selector(lks_rgbaComponents)]) {
|
||||
attribute.value = [returnObjValue lks_rgbaComponents];
|
||||
} else {
|
||||
// https://github.com/QMUI/LookinServer/issues/124
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
attribute.value = returnObjValue;
|
||||
}
|
||||
|
||||
} else {
|
||||
NSAssert(NO, @"不支持解析该类型的返回值");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
27
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrGroupsMaker.h
generated
Normal file
27
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrGroupsMaker.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrGroupsMaker.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by LikaiMacStudioWork on 2023/10/31.
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
@class LookinAttributesGroup;
|
||||
|
||||
@interface LKS_CustomAttrGroupsMaker : NSObject
|
||||
|
||||
- (instancetype)initWithLayer:(CALayer *)layer;
|
||||
|
||||
- (void)execute;
|
||||
|
||||
- (NSArray<LookinAttributesGroup *> *)getGroups;
|
||||
- (NSString *)getCustomDisplayTitle;
|
||||
- (NSString *)getDanceUISource;
|
||||
|
||||
+ (NSArray<LookinAttributesGroup *> *)makeGroupsFromRawProperties:(NSArray *)rawProperties saveCustomSetter:(BOOL)saveCustomSetter;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
486
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrGroupsMaker.m
generated
Normal file
486
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrGroupsMaker.m
generated
Normal file
@@ -0,0 +1,486 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrGroupsMaker.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by LikaiMacStudioWork on 2023/10/31.
|
||||
//
|
||||
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LookinAttributesGroup.h"
|
||||
#import "LookinAttributesSection.h"
|
||||
#import "LookinAttribute.h"
|
||||
#import "LookinDashboardBlueprint.h"
|
||||
#import "LookinIvarTrace.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_CustomAttrSetterManager.h"
|
||||
|
||||
@interface LKS_CustomAttrGroupsMaker ()
|
||||
|
||||
/// key 是 section title
|
||||
@property(nonatomic, strong) NSMutableDictionary<NSString *, NSMutableArray<LookinAttribute *> *> *sectionAndAttrs;
|
||||
|
||||
@property(nonatomic, copy) NSString *resolvedCustomDisplayTitle;
|
||||
@property(nonatomic, copy) NSString *resolvedDanceUISource;
|
||||
@property(nonatomic, strong) NSMutableArray *resolvedGroups;
|
||||
|
||||
@property(nonatomic, weak) CALayer *layer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_CustomAttrGroupsMaker
|
||||
|
||||
- (instancetype)initWithLayer:(CALayer *)layer {
|
||||
if (self = [super init]) {
|
||||
self.sectionAndAttrs = [NSMutableDictionary dictionary];
|
||||
self.layer = layer;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)execute {
|
||||
if (!self.layer) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
NSMutableArray<NSString *> *selectors = [NSMutableArray array];
|
||||
[selectors addObject:@"lookin_customDebugInfos"];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
[selectors addObject:[NSString stringWithFormat:@"lookin_customDebugInfos_%@", @(i)]];
|
||||
}
|
||||
|
||||
for (NSString *name in selectors) {
|
||||
[self makeAttrsForViewOrLayer:self.layer selectorName:name];
|
||||
|
||||
UIView *view = self.layer.lks_hostView;
|
||||
if (view) {
|
||||
[self makeAttrsForViewOrLayer:view selectorName:name];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self.sectionAndAttrs count] == 0) {
|
||||
return;
|
||||
}
|
||||
NSMutableArray<LookinAttributesGroup *> *groups = [NSMutableArray array];
|
||||
[self.sectionAndAttrs enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull groupTitle, NSMutableArray<LookinAttribute *> * _Nonnull attrs, BOOL * _Nonnull stop) {
|
||||
LookinAttributesGroup *group = [LookinAttributesGroup new];
|
||||
group.userCustomTitle = groupTitle;
|
||||
group.identifier = LookinAttrGroup_UserCustom;
|
||||
|
||||
NSMutableArray<LookinAttributesSection *> *sections = [NSMutableArray array];
|
||||
[attrs enumerateObjectsUsingBlock:^(LookinAttribute * _Nonnull attr, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinAttributesSection *sec = [LookinAttributesSection new];
|
||||
sec.identifier = LookinAttrSec_UserCustom;
|
||||
sec.attributes = @[attr];
|
||||
[sections addObject:sec];
|
||||
}];
|
||||
|
||||
group.attrSections = sections;
|
||||
[groups addObject:group];
|
||||
}];
|
||||
[groups sortedArrayUsingComparator:^NSComparisonResult(LookinAttributesGroup *obj1, LookinAttributesGroup *obj2) {
|
||||
return [obj1.userCustomTitle compare:obj2.userCustomTitle];
|
||||
}];
|
||||
|
||||
self.resolvedGroups = groups;
|
||||
}
|
||||
|
||||
- (void)makeAttrsForViewOrLayer:(id)viewOrLayer selectorName:(NSString *)selectorName {
|
||||
if (!viewOrLayer || !selectorName.length) {
|
||||
return;
|
||||
}
|
||||
if (![viewOrLayer isKindOfClass:[UIView class]] && ![viewOrLayer isKindOfClass:[CALayer class]]) {
|
||||
return;
|
||||
}
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
if (![viewOrLayer respondsToSelector:selector]) {
|
||||
return;
|
||||
}
|
||||
NSMethodSignature *signature = [viewOrLayer methodSignatureForSelector:selector];
|
||||
if (signature.numberOfArguments > 2) {
|
||||
NSAssert(NO, @"LookinServer - There should be no explicit parameters.");
|
||||
return;
|
||||
}
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setTarget:viewOrLayer];
|
||||
[invocation setSelector:selector];
|
||||
[invocation invoke];
|
||||
|
||||
// 小心这里的内存管理
|
||||
NSDictionary<NSString *, id> * __unsafe_unretained tempRawData;
|
||||
[invocation getReturnValue:&tempRawData];
|
||||
if (!tempRawData || ![tempRawData isKindOfClass:[NSDictionary class]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary<NSString *, id> *rawData = tempRawData;
|
||||
NSArray *rawProperties = rawData[@"properties"];
|
||||
|
||||
NSString *customTitle = rawData[@"title"];
|
||||
if (customTitle && [customTitle isKindOfClass:[NSString class]] && customTitle.length > 0) {
|
||||
self.resolvedCustomDisplayTitle = customTitle;
|
||||
}
|
||||
|
||||
NSString *danceSource = rawData[@"lookin_source"];
|
||||
if (danceSource && [danceSource isKindOfClass:[NSString class]] && danceSource.length > 0) {
|
||||
self.resolvedDanceUISource = danceSource;
|
||||
}
|
||||
|
||||
[self makeAttrsFromRawProperties:rawProperties];
|
||||
}
|
||||
|
||||
- (void)makeAttrsFromRawProperties:(NSArray *)rawProperties {
|
||||
if (!rawProperties || ![rawProperties isKindOfClass:[NSArray class]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSDictionary<NSString *, id> *dict in rawProperties) {
|
||||
NSString *groupTitle;
|
||||
LookinAttribute *attr = [LKS_CustomAttrGroupsMaker attrFromRawDict:dict saveCustomSetter:YES groupTitle:&groupTitle];
|
||||
if (!attr) {
|
||||
continue;
|
||||
}
|
||||
if (!self.sectionAndAttrs[groupTitle]) {
|
||||
self.sectionAndAttrs[groupTitle] = [NSMutableArray array];
|
||||
}
|
||||
[self.sectionAndAttrs[groupTitle] addObject:attr];
|
||||
}
|
||||
}
|
||||
|
||||
+ (LookinAttribute *)attrFromRawDict:(NSDictionary *)dict saveCustomSetter:(BOOL)saveCustomSetter groupTitle:(inout NSString **)inoutGroupTitle {
|
||||
LookinAttribute *attr = [LookinAttribute new];
|
||||
attr.identifier = LookinAttr_UserCustom;
|
||||
|
||||
NSString *title = dict[@"title"];
|
||||
NSString *type = dict[@"valueType"];
|
||||
NSString *section = dict[@"section"];
|
||||
id value = dict[@"value"];
|
||||
|
||||
if (!title || ![title isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"LookinServer - Wrong title");
|
||||
return nil;
|
||||
}
|
||||
if (!type || ![type isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"LookinServer - Wrong valueType");
|
||||
return nil;
|
||||
}
|
||||
if (!section || ![section isKindOfClass:[NSString class]] || section.length == 0) {
|
||||
*inoutGroupTitle = @"Custom";
|
||||
} else {
|
||||
*inoutGroupTitle = section;
|
||||
}
|
||||
|
||||
attr.displayTitle = title;
|
||||
|
||||
NSString *fixedType = type.lowercaseString;
|
||||
if ([fixedType isEqualToString:@"string"]) {
|
||||
if (value != nil && ![value isKindOfClass:[NSString class]]) {
|
||||
// nil 是合法的
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeNSString;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_StringSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveStringSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"number"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeDouble;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_NumberSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveNumberSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"bool"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSNumber class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeBOOL;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_BoolSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveBoolSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"color"]) {
|
||||
if (value != nil && ![value isKindOfClass:[UIColor class]]) {
|
||||
// nil 是合法的
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeUIColor;
|
||||
attr.value = [(UIColor *)value lks_rgbaComponents];
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_ColorSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveColorSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"rect"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSValue class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeCGRect;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_RectSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveRectSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"size"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSValue class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeCGSize;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_SizeSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveSizeSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"point"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSValue class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeCGPoint;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_PointSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] savePointSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"insets"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSValue class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeUIEdgeInsets;
|
||||
attr.value = value;
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_InsetsSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveInsetsSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"shadow"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSDictionary class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *shadowInfo = value;
|
||||
if (![shadowInfo[@"offset"] isKindOfClass:[NSValue class]]) {
|
||||
NSLog(@"LookinServer - Wrong value. No offset.");
|
||||
return nil;
|
||||
}
|
||||
if (![shadowInfo[@"opacity"] isKindOfClass:[NSNumber class]]) {
|
||||
NSLog(@"LookinServer - Wrong value. No opacity.");
|
||||
return nil;
|
||||
}
|
||||
if (![shadowInfo[@"radius"] isKindOfClass:[NSNumber class]]) {
|
||||
NSLog(@"LookinServer - Wrong value. No radius.");
|
||||
return nil;
|
||||
}
|
||||
NSMutableDictionary *checkedShadowInfo = [@{
|
||||
@"offset": shadowInfo[@"offset"],
|
||||
@"opacity": shadowInfo[@"opacity"],
|
||||
@"radius": shadowInfo[@"radius"]
|
||||
} mutableCopy];
|
||||
if ([shadowInfo[@"color"] isKindOfClass:[UIColor class]]) {
|
||||
checkedShadowInfo[@"color"] = [(UIColor *)shadowInfo[@"color"] lks_rgbaComponents];
|
||||
}
|
||||
|
||||
attr.attrType = LookinAttrTypeShadow;
|
||||
attr.value = checkedShadowInfo;
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"enum"]) {
|
||||
if (value == nil) {
|
||||
NSLog(@"LookinServer - No value.");
|
||||
return nil;
|
||||
}
|
||||
if (![value isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeEnumString;
|
||||
attr.value = value;
|
||||
|
||||
NSArray<NSString *> *allEnumCases = dict[@"allEnumCases"];
|
||||
if ([allEnumCases isKindOfClass:[NSArray class]]) {
|
||||
attr.extraValue = allEnumCases;
|
||||
}
|
||||
|
||||
if (saveCustomSetter && dict[@"retainedSetter"]) {
|
||||
NSString *uniqueID = [[NSUUID new] UUIDString];
|
||||
LKS_EnumSetter setter = dict[@"retainedSetter"];
|
||||
[[LKS_CustomAttrSetterManager sharedInstance] saveEnumSetter:setter uniqueID:uniqueID];
|
||||
attr.customSetterID = uniqueID;
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
if ([fixedType isEqualToString:@"json"]) {
|
||||
if (![value isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"LookinServer - Wrong value type.");
|
||||
return nil;
|
||||
}
|
||||
attr.attrType = LookinAttrTypeJson;
|
||||
attr.value = value;
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
NSLog(@"LookinServer - Unsupported value type.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray<LookinAttributesGroup *> *)getGroups {
|
||||
return self.resolvedGroups;
|
||||
}
|
||||
|
||||
- (NSString *)getCustomDisplayTitle {
|
||||
return self.resolvedCustomDisplayTitle;
|
||||
}
|
||||
|
||||
- (NSString *)getDanceUISource {
|
||||
return self.resolvedDanceUISource;
|
||||
}
|
||||
|
||||
+ (NSArray<LookinAttributesGroup *> *)makeGroupsFromRawProperties:(NSArray *)rawProperties saveCustomSetter:(BOOL)saveCustomSetter {
|
||||
if (!rawProperties || ![rawProperties isKindOfClass:[NSArray class]]) {
|
||||
return nil;
|
||||
}
|
||||
// key 是 group title
|
||||
NSMutableDictionary<NSString *, NSMutableArray<LookinAttribute *> *> *groupTitleAndAttrs = [NSMutableDictionary dictionary];
|
||||
|
||||
for (NSDictionary<NSString *, id> *dict in rawProperties) {
|
||||
NSString *groupTitle;
|
||||
LookinAttribute *attr = [LKS_CustomAttrGroupsMaker attrFromRawDict:dict saveCustomSetter:saveCustomSetter groupTitle:&groupTitle];
|
||||
if (!attr) {
|
||||
continue;
|
||||
}
|
||||
if (!groupTitleAndAttrs[groupTitle]) {
|
||||
groupTitleAndAttrs[groupTitle] = [NSMutableArray array];
|
||||
}
|
||||
[groupTitleAndAttrs[groupTitle] addObject:attr];
|
||||
}
|
||||
|
||||
if ([groupTitleAndAttrs count] == 0) {
|
||||
return nil;
|
||||
}
|
||||
NSMutableArray<LookinAttributesGroup *> *groups = [NSMutableArray array];
|
||||
[groupTitleAndAttrs enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull groupTitle, NSMutableArray<LookinAttribute *> * _Nonnull attrs, BOOL * _Nonnull stop) {
|
||||
LookinAttributesGroup *group = [LookinAttributesGroup new];
|
||||
group.userCustomTitle = groupTitle;
|
||||
group.identifier = LookinAttrGroup_UserCustom;
|
||||
|
||||
NSMutableArray<LookinAttributesSection *> *sections = [NSMutableArray array];
|
||||
[attrs enumerateObjectsUsingBlock:^(LookinAttribute * _Nonnull attr, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinAttributesSection *sec = [LookinAttributesSection new];
|
||||
sec.identifier = LookinAttrSec_UserCustom;
|
||||
sec.attributes = @[attr];
|
||||
[sections addObject:sec];
|
||||
}];
|
||||
|
||||
group.attrSections = sections;
|
||||
[groups addObject:group];
|
||||
}];
|
||||
[groups sortedArrayUsingComparator:^NSComparisonResult(LookinAttributesGroup *obj1, LookinAttributesGroup *obj2) {
|
||||
return [obj1.userCustomTitle compare:obj2.userCustomTitle];
|
||||
}];
|
||||
return [groups copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
56
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrSetterManager.h
generated
Normal file
56
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrSetterManager.h
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrSetterManager.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/11/4.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef void(^LKS_StringSetter)(NSString *);
|
||||
typedef void(^LKS_NumberSetter)(NSNumber *);
|
||||
typedef void(^LKS_BoolSetter)(BOOL);
|
||||
typedef void(^LKS_ColorSetter)(UIColor *);
|
||||
typedef void(^LKS_EnumSetter)(NSString *);
|
||||
typedef void(^LKS_RectSetter)(CGRect);
|
||||
typedef void(^LKS_SizeSetter)(CGSize);
|
||||
typedef void(^LKS_PointSetter)(CGPoint);
|
||||
typedef void(^LKS_InsetsSetter)(UIEdgeInsets);
|
||||
|
||||
@interface LKS_CustomAttrSetterManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)removeAll;
|
||||
|
||||
- (void)saveStringSetter:(LKS_StringSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_StringSetter)getStringSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveNumberSetter:(LKS_NumberSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_NumberSetter)getNumberSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveBoolSetter:(LKS_BoolSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_BoolSetter)getBoolSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveColorSetter:(LKS_ColorSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_ColorSetter)getColorSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveEnumSetter:(LKS_EnumSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_EnumSetter)getEnumSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveRectSetter:(LKS_RectSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_RectSetter)getRectSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveSizeSetter:(LKS_SizeSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_SizeSetter)getSizeSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)savePointSetter:(LKS_PointSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_PointSetter)getPointSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
- (void)saveInsetsSetter:(LKS_InsetsSetter)setter uniqueID:(NSString *)uniqueID;
|
||||
- (LKS_InsetsSetter)getInsetsSetterWithID:(NSString *)uniqueID;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
117
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrSetterManager.m
generated
Normal file
117
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomAttrSetterManager.m
generated
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrSetterManager.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/11/4.
|
||||
//
|
||||
|
||||
#import "LKS_CustomAttrSetterManager.h"
|
||||
|
||||
@interface LKS_CustomAttrSetterManager ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableDictionary *settersMap;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_CustomAttrSetterManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static LKS_CustomAttrSetterManager *instance = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone {
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.settersMap = [NSMutableDictionary new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)removeAll {
|
||||
[self.settersMap removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)saveStringSetter:(nonnull LKS_StringSetter)setter uniqueID:(nonnull NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (nullable LKS_StringSetter)getStringSetterWithID:(nonnull NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveNumberSetter:(LKS_NumberSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (nullable LKS_NumberSetter)getNumberSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveBoolSetter:(LKS_BoolSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_BoolSetter)getBoolSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveColorSetter:(LKS_ColorSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_ColorSetter)getColorSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveEnumSetter:(LKS_EnumSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_EnumSetter)getEnumSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveRectSetter:(LKS_RectSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_RectSetter)getRectSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveSizeSetter:(LKS_SizeSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_SizeSetter)getSizeSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)savePointSetter:(LKS_PointSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_PointSetter)getPointSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
- (void)saveInsetsSetter:(LKS_InsetsSetter)setter uniqueID:(NSString *)uniqueID {
|
||||
self.settersMap[uniqueID] = setter;
|
||||
}
|
||||
|
||||
- (LKS_InsetsSetter)getInsetsSetterWithID:(NSString *)uniqueID {
|
||||
return self.settersMap[uniqueID];
|
||||
}
|
||||
|
||||
@end
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
22
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomDisplayItemsMaker.h
generated
Normal file
22
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomDisplayItemsMaker.h
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_CustomDisplayItemsMaker.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/11/1.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class LookinDisplayItem;
|
||||
|
||||
@interface LKS_CustomDisplayItemsMaker : NSObject
|
||||
|
||||
- (instancetype)initWithLayer:(CALayer *)layer saveAttrSetter:(BOOL)saveAttrSetter;
|
||||
|
||||
- (NSArray<LookinDisplayItem *> *)make;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
144
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomDisplayItemsMaker.m
generated
Normal file
144
Pods/LookinServer/Src/Main/Server/Others/LKS_CustomDisplayItemsMaker.m
generated
Normal file
@@ -0,0 +1,144 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_CustomDisplayItemsMaker.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/11/1.
|
||||
//
|
||||
|
||||
#import "LKS_CustomDisplayItemsMaker.h"
|
||||
#import "CALayer+LookinServer.h"
|
||||
#import "LookinDisplayItem.h"
|
||||
#import "NSArray+Lookin.h"
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
|
||||
@interface LKS_CustomDisplayItemsMaker ()
|
||||
|
||||
@property(nonatomic, weak) CALayer *layer;
|
||||
@property(nonatomic, assign) BOOL saveAttrSetter;
|
||||
@property(nonatomic, strong) NSMutableArray *allSubitems;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_CustomDisplayItemsMaker
|
||||
|
||||
- (instancetype)initWithLayer:(CALayer *)layer saveAttrSetter:(BOOL)saveAttrSetter {
|
||||
if (self = [super init]) {
|
||||
self.layer = layer;
|
||||
self.saveAttrSetter = saveAttrSetter;
|
||||
self.allSubitems = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray<LookinDisplayItem *> *)make {
|
||||
if (!self.layer) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
NSMutableArray<NSString *> *selectors = [NSMutableArray array];
|
||||
[selectors addObject:@"lookin_customDebugInfos"];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
[selectors addObject:[NSString stringWithFormat:@"lookin_customDebugInfos_%@", @(i)]];
|
||||
}
|
||||
|
||||
for (NSString *name in selectors) {
|
||||
[self makeSubitemsForViewOrLayer:self.layer selectorName:name];
|
||||
|
||||
UIView *view = self.layer.lks_hostView;
|
||||
if (view) {
|
||||
[self makeSubitemsForViewOrLayer:view selectorName:name];
|
||||
}
|
||||
}
|
||||
|
||||
if (self.allSubitems.count) {
|
||||
return self.allSubitems;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)makeSubitemsForViewOrLayer:(id)viewOrLayer selectorName:(NSString *)selectorName {
|
||||
if (!viewOrLayer || !selectorName.length) {
|
||||
return;
|
||||
}
|
||||
if (![viewOrLayer isKindOfClass:[UIView class]] && ![viewOrLayer isKindOfClass:[CALayer class]]) {
|
||||
return;
|
||||
}
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
if (![viewOrLayer respondsToSelector:selector]) {
|
||||
return;
|
||||
}
|
||||
NSMethodSignature *signature = [viewOrLayer methodSignatureForSelector:selector];
|
||||
if (signature.numberOfArguments > 2) {
|
||||
NSAssert(NO, @"LookinServer - There should be no explicit parameters.");
|
||||
return;
|
||||
}
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setTarget:viewOrLayer];
|
||||
[invocation setSelector:selector];
|
||||
[invocation invoke];
|
||||
|
||||
// 小心这里的内存管理
|
||||
NSDictionary<NSString *, id> * __unsafe_unretained tempRawData;
|
||||
[invocation getReturnValue:&tempRawData];
|
||||
NSDictionary<NSString *, id> *rawData = tempRawData;
|
||||
|
||||
[self makeSubitemsFromRawData:rawData];
|
||||
}
|
||||
|
||||
- (void)makeSubitemsFromRawData:(NSDictionary<NSString *, id> *)data {
|
||||
if (!data || ![data isKindOfClass:[NSDictionary class]]) {
|
||||
return;
|
||||
}
|
||||
NSArray *rawSubviews = data[@"subviews"];
|
||||
NSArray<LookinDisplayItem *> *newSubitems = [self displayItemsFromRawArray:rawSubviews];
|
||||
if (newSubitems) {
|
||||
[self.allSubitems addObjectsFromArray:newSubitems];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<LookinDisplayItem *> *)displayItemsFromRawArray:(NSArray<NSDictionary *> *)rawArray {
|
||||
if (!rawArray || ![rawArray isKindOfClass:[NSArray class]]) {
|
||||
return nil;
|
||||
}
|
||||
NSArray *items = [rawArray lookin_map:^id(NSUInteger idx, NSDictionary *rawDict) {
|
||||
if (![rawDict isKindOfClass:[NSDictionary class]]) {
|
||||
return nil;
|
||||
}
|
||||
return [self displayItemFromRawDict:rawDict];
|
||||
}];
|
||||
return items;
|
||||
}
|
||||
|
||||
- (LookinDisplayItem *)displayItemFromRawDict:(NSDictionary<NSString *, id> *)dict {
|
||||
NSString *title = dict[@"title"];
|
||||
NSString *subtitle = dict[@"subtitle"];
|
||||
NSValue *frameValue = dict[@"frameInWindow"];
|
||||
NSArray *properties = dict[@"properties"];
|
||||
NSArray *subviews = dict[@"subviews"];
|
||||
NSString *danceSource = dict[@"lookin_source"];
|
||||
|
||||
if (![title isKindOfClass:[NSString class]]) {
|
||||
return nil;
|
||||
}
|
||||
LookinDisplayItem *newItem = [LookinDisplayItem new];
|
||||
if (subviews && [subviews isKindOfClass:[NSArray class]]) {
|
||||
newItem.subitems = [self displayItemsFromRawArray:subviews];
|
||||
}
|
||||
newItem.isHidden = NO;
|
||||
newItem.alpha = 1.0;
|
||||
newItem.customInfo = [LookinCustomDisplayItemInfo new];
|
||||
newItem.customInfo.title = title;
|
||||
newItem.customInfo.subtitle = subtitle;
|
||||
newItem.customInfo.frameInWindow = frameValue;
|
||||
newItem.customInfo.danceuiSource = danceSource;
|
||||
newItem.customAttrGroupList = [LKS_CustomAttrGroupsMaker makeGroupsFromRawProperties:properties saveCustomSetter:self.saveAttrSetter];
|
||||
|
||||
return newItem;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Others/LKS_EventHandlerMaker.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Others/LKS_EventHandlerMaker.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_EventHandlerMaker.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/8/7.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
@class LookinEventHandler;
|
||||
|
||||
@interface LKS_EventHandlerMaker : NSObject
|
||||
|
||||
+ (NSArray<LookinEventHandler *> *)makeForView:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
215
Pods/LookinServer/Src/Main/Server/Others/LKS_EventHandlerMaker.m
generated
Normal file
215
Pods/LookinServer/Src/Main/Server/Others/LKS_EventHandlerMaker.m
generated
Normal file
@@ -0,0 +1,215 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_EventHandlerMaker.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/8/7.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_EventHandlerMaker.h"
|
||||
#import "LookinTuple.h"
|
||||
#import "LookinEventHandler.h"
|
||||
#import "LookinObject.h"
|
||||
#import "LookinWeakContainer.h"
|
||||
#import "LookinIvarTrace.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_GestureTargetActionsSearcher.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
@implementation LKS_EventHandlerMaker
|
||||
|
||||
+ (NSArray<LookinEventHandler *> *)makeForView:(UIView *)view {
|
||||
if (!view) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray<LookinEventHandler *> *allHandlers = nil;
|
||||
|
||||
if ([view isKindOfClass:[UIControl class]]) {
|
||||
NSArray<LookinEventHandler *> *targetActionHandlers = [self _targetActionHandlersForControl:(UIControl *)view];
|
||||
if (targetActionHandlers.count) {
|
||||
if (!allHandlers) {
|
||||
allHandlers = [NSMutableArray array];
|
||||
}
|
||||
[allHandlers addObjectsFromArray:targetActionHandlers];
|
||||
}
|
||||
}
|
||||
|
||||
NSArray<LookinEventHandler *> *gestureHandlers = [self _gestureHandlersForView:view];
|
||||
if (gestureHandlers.count) {
|
||||
if (!allHandlers) {
|
||||
allHandlers = [NSMutableArray array];
|
||||
}
|
||||
[allHandlers addObjectsFromArray:gestureHandlers];
|
||||
}
|
||||
|
||||
return allHandlers.copy;
|
||||
}
|
||||
|
||||
+ (NSArray<LookinEventHandler *> *)_gestureHandlersForView:(UIView *)view {
|
||||
if (view.gestureRecognizers.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
NSArray<LookinEventHandler *> *handlers = [view.gestureRecognizers lookin_map:^id(NSUInteger idx, __kindof UIGestureRecognizer *recognizer) {
|
||||
LookinEventHandler *handler = [LookinEventHandler new];
|
||||
handler.handlerType = LookinEventHandlerTypeGesture;
|
||||
handler.eventName = NSStringFromClass([recognizer class]);
|
||||
|
||||
NSArray<LookinTwoTuple *> *targetActionInfos = [LKS_GestureTargetActionsSearcher getTargetActionsFromRecognizer:recognizer];
|
||||
handler.targetActions = [targetActionInfos lookin_map:^id(NSUInteger idx, LookinTwoTuple *rawTuple) {
|
||||
NSObject *target = ((LookinWeakContainer *)rawTuple.first).object;
|
||||
if (!target) {
|
||||
// 该 target 已被释放
|
||||
return nil;
|
||||
}
|
||||
LookinStringTwoTuple *newTuple = [LookinStringTwoTuple new];
|
||||
newTuple.first = [LKS_Helper descriptionOfObject:target];
|
||||
newTuple.second = (NSString *)rawTuple.second;
|
||||
return newTuple;
|
||||
}];
|
||||
handler.inheritedRecognizerName = [self _inheritedRecognizerNameForRecognizer:recognizer];
|
||||
handler.gestureRecognizerIsEnabled = recognizer.enabled;
|
||||
if (recognizer.delegate) {
|
||||
handler.gestureRecognizerDelegator = [LKS_Helper descriptionOfObject:recognizer.delegate];
|
||||
}
|
||||
handler.recognizerIvarTraces = [recognizer.lks_ivarTraces lookin_map:^id(NSUInteger idx, LookinIvarTrace *trace) {
|
||||
return [NSString stringWithFormat:@"(%@ *) -> %@", trace.hostClassName, trace.ivarName];
|
||||
}];
|
||||
|
||||
handler.recognizerOid = [recognizer lks_registerOid];
|
||||
return handler;
|
||||
}];
|
||||
return handlers;
|
||||
}
|
||||
|
||||
+ (NSString *)_inheritedRecognizerNameForRecognizer:(UIGestureRecognizer *)recognizer {
|
||||
if (!recognizer) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSArray<Class> *baseRecognizers;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// 注意这里 UIScreenEdgePanGestureRecognizer 在 UIPanGestureRecognizer 前面,因为 UIScreenEdgePanGestureRecognizer 继承于 UIPanGestureRecognizer
|
||||
#if TARGET_OS_TV
|
||||
baseRecognizers = @[[UILongPressGestureRecognizer class],
|
||||
[UIPanGestureRecognizer class],
|
||||
[UISwipeGestureRecognizer class],
|
||||
[UITapGestureRecognizer class]];
|
||||
#elif TARGET_OS_VISION
|
||||
baseRecognizers = @[[UILongPressGestureRecognizer class],
|
||||
[UIPanGestureRecognizer class],
|
||||
[UISwipeGestureRecognizer class],
|
||||
[UIRotationGestureRecognizer class],
|
||||
[UIPinchGestureRecognizer class],
|
||||
[UITapGestureRecognizer class]];
|
||||
#else
|
||||
baseRecognizers = @[[UILongPressGestureRecognizer class],
|
||||
[UIScreenEdgePanGestureRecognizer class],
|
||||
[UIPanGestureRecognizer class],
|
||||
[UISwipeGestureRecognizer class],
|
||||
[UIRotationGestureRecognizer class],
|
||||
[UIPinchGestureRecognizer class],
|
||||
[UITapGestureRecognizer class]];
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
__block NSString *result = @"UIGestureRecognizer";
|
||||
[baseRecognizers enumerateObjectsUsingBlock:^(Class _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if ([recognizer isMemberOfClass:obj]) {
|
||||
// 自身就是基本款,则直接置为 nil
|
||||
result = nil;
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
if ([recognizer isKindOfClass:obj]) {
|
||||
result = NSStringFromClass(obj);
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSArray<LookinEventHandler *> *)_targetActionHandlersForControl:(UIControl *)control {
|
||||
static dispatch_once_t onceToken;
|
||||
static NSArray<NSNumber *> *allEvents = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
allEvents = @[@(UIControlEventTouchDown), @(UIControlEventTouchDownRepeat), @(UIControlEventTouchDragInside), @(UIControlEventTouchDragOutside), @(UIControlEventTouchDragEnter), @(UIControlEventTouchDragExit), @(UIControlEventTouchUpInside), @(UIControlEventTouchUpOutside), @(UIControlEventTouchCancel), @(UIControlEventValueChanged), @(UIControlEventEditingDidBegin), @(UIControlEventEditingChanged), @(UIControlEventEditingDidEnd), @(UIControlEventEditingDidEndOnExit)];
|
||||
if (@available(iOS 9.0, *)) {
|
||||
allEvents = [allEvents arrayByAddingObject:@(UIControlEventPrimaryActionTriggered)];
|
||||
}
|
||||
});
|
||||
|
||||
NSSet *allTargets = control.allTargets;
|
||||
|
||||
if (!allTargets.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray<LookinEventHandler *> *handlers = [NSMutableArray array];
|
||||
|
||||
[allEvents enumerateObjectsUsingBlock:^(NSNumber * _Nonnull eventNum, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
UIControlEvents event = [eventNum unsignedIntegerValue];
|
||||
|
||||
NSMutableArray<LookinStringTwoTuple *> *targetActions = [NSMutableArray array];
|
||||
|
||||
[allTargets enumerateObjectsUsingBlock:^(id _Nonnull target, BOOL * _Nonnull stop) {
|
||||
NSArray<NSString *> *actions = [control actionsForTarget:target forControlEvent:event];
|
||||
[actions enumerateObjectsUsingBlock:^(NSString * _Nonnull action, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinStringTwoTuple *tuple = [LookinStringTwoTuple new];
|
||||
tuple.first = [LKS_Helper descriptionOfObject:target];
|
||||
tuple.second = action;
|
||||
[targetActions addObject:tuple];
|
||||
}];
|
||||
}];
|
||||
|
||||
if (targetActions.count) {
|
||||
LookinEventHandler *handler = [LookinEventHandler new];
|
||||
handler.handlerType = LookinEventHandlerTypeTargetAction;
|
||||
handler.eventName = [self _nameFromControlEvent:event];
|
||||
handler.targetActions = targetActions.copy;
|
||||
[handlers addObject:handler];
|
||||
}
|
||||
}];
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
+ (NSString *)_nameFromControlEvent:(UIControlEvents)event {
|
||||
static dispatch_once_t onceToken;
|
||||
static NSDictionary<NSNumber *, NSString *> *eventsAndNames = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
NSMutableDictionary<NSNumber *, NSString *> *eventsAndNames_m = @{
|
||||
@(UIControlEventTouchDown): @"UIControlEventTouchDown",
|
||||
@(UIControlEventTouchDownRepeat): @"UIControlEventTouchDownRepeat",
|
||||
@(UIControlEventTouchDragInside): @"UIControlEventTouchDragInside",
|
||||
@(UIControlEventTouchDragOutside): @"UIControlEventTouchDragOutside",
|
||||
@(UIControlEventTouchDragEnter): @"UIControlEventTouchDragEnter",
|
||||
@(UIControlEventTouchDragExit): @"UIControlEventTouchDragExit",
|
||||
@(UIControlEventTouchUpInside): @"UIControlEventTouchUpInside",
|
||||
@(UIControlEventTouchUpOutside): @"UIControlEventTouchUpOutside",
|
||||
@(UIControlEventTouchCancel): @"UIControlEventTouchCancel",
|
||||
@(UIControlEventValueChanged): @"UIControlEventValueChanged",
|
||||
@(UIControlEventEditingDidBegin): @"UIControlEventEditingDidBegin",
|
||||
@(UIControlEventEditingChanged): @"UIControlEventEditingChanged",
|
||||
@(UIControlEventEditingDidEnd): @"UIControlEventEditingDidEnd",
|
||||
@(UIControlEventEditingDidEndOnExit): @"UIControlEventEditingDidEndOnExit",
|
||||
}.mutableCopy;
|
||||
if (@available(iOS 9.0, *)) {
|
||||
eventsAndNames_m[@(UIControlEventPrimaryActionTriggered)] = @"UIControlEventPrimaryActionTriggered";
|
||||
}
|
||||
eventsAndNames = eventsAndNames_m.copy;
|
||||
});
|
||||
|
||||
NSString *name = eventsAndNames[@(event)];
|
||||
return name;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Others/LKS_ExportManager.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Others/LKS_ExportManager.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_ExportManager.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/5/13.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface LKS_ExportManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)exportAndShare;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
193
Pods/LookinServer/Src/Main/Server/Others/LKS_ExportManager.m
generated
Normal file
193
Pods/LookinServer/Src/Main/Server/Others/LKS_ExportManager.m
generated
Normal file
@@ -0,0 +1,193 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_ExportManager.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/5/13.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_ExportManager.h"
|
||||
#import "UIViewController+LookinServer.h"
|
||||
#import "LookinHierarchyInfo.h"
|
||||
#import "LookinHierarchyFile.h"
|
||||
#import "LookinAppInfo.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
@interface LKS_ExportManagerMaskView : UIView
|
||||
|
||||
@property(nonatomic, strong) UIView *tipsView;
|
||||
@property(nonatomic, strong) UILabel *firstLabel;
|
||||
@property(nonatomic, strong) UILabel *secondLabel;
|
||||
@property(nonatomic, strong) UILabel *thirdLabel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_ExportManagerMaskView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.35];
|
||||
|
||||
self.tipsView = [UIView new];
|
||||
self.tipsView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.88];
|
||||
self.tipsView.layer.cornerRadius = 6;
|
||||
self.tipsView.layer.masksToBounds = YES;
|
||||
[self addSubview:self.tipsView];
|
||||
|
||||
self.firstLabel = [UILabel new];
|
||||
self.firstLabel.text = LKS_Localized(@"Creating File…");
|
||||
self.firstLabel.textColor = [UIColor whiteColor];
|
||||
self.firstLabel.font = [UIFont boldSystemFontOfSize:14];
|
||||
self.firstLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.firstLabel.numberOfLines = 0;
|
||||
[self.tipsView addSubview:self.firstLabel];
|
||||
|
||||
self.secondLabel = [UILabel new];
|
||||
self.secondLabel.text = LKS_Localized(@"May take 8 or more seconds according to the UI complexity.");
|
||||
self.secondLabel.textColor = [UIColor colorWithRed:173/255.0 green:180/255.0 blue:190/255.0 alpha:1];
|
||||
self.secondLabel.font = [UIFont systemFontOfSize:12];
|
||||
self.secondLabel.textAlignment = NSTextAlignmentLeft;
|
||||
self.secondLabel.numberOfLines = 0;
|
||||
[self.tipsView addSubview:self.secondLabel];
|
||||
|
||||
self.thirdLabel = [UILabel new];
|
||||
self.thirdLabel.text = LKS_Localized(@"The file can be opend by Lookin.app in macOS.");
|
||||
self.thirdLabel.textColor = [UIColor colorWithRed:173/255.0 green:180/255.0 blue:190/255.0 alpha:1];
|
||||
self.thirdLabel.font = [UIFont systemFontOfSize:12];
|
||||
self.thirdLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.thirdLabel.numberOfLines = 0;
|
||||
[self.tipsView addSubview:self.thirdLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(8, 10, 8, 10);
|
||||
CGFloat maxLabelWidth = self.bounds.size.width * .8 - insets.left - insets.right;
|
||||
|
||||
CGSize firstSize = [self.firstLabel sizeThatFits:CGSizeMake(maxLabelWidth, CGFLOAT_MAX)];
|
||||
CGSize secondSize = [self.secondLabel sizeThatFits:CGSizeMake(maxLabelWidth, CGFLOAT_MAX)];
|
||||
CGSize thirdSize = [self.thirdLabel sizeThatFits:CGSizeMake(maxLabelWidth, CGFLOAT_MAX)];
|
||||
|
||||
CGFloat tipsWidth = MAX(MAX(firstSize.width, secondSize.width), thirdSize.width) + insets.left + insets.right;
|
||||
|
||||
self.firstLabel.frame = CGRectMake(tipsWidth / 2.0 - firstSize.width / 2.0, insets.top, firstSize.width, firstSize.height);
|
||||
self.secondLabel.frame = CGRectMake(tipsWidth / 2.0 - secondSize.width / 2.0, CGRectGetMaxY(self.firstLabel.frame) + 10, secondSize.width, secondSize.height);
|
||||
self.thirdLabel.frame = CGRectMake(tipsWidth / 2.0 - thirdSize.width / 2.0, CGRectGetMaxY(self.secondLabel.frame) + 5, thirdSize.width, thirdSize.height);
|
||||
|
||||
self.tipsView.frame = ({
|
||||
CGFloat height = CGRectGetMaxY(self.thirdLabel.frame) + insets.bottom;
|
||||
CGRectMake(self.bounds.size.width / 2.0 - tipsWidth / 2.0, self.bounds.size.height / 2.0 - height / 2.0, tipsWidth, height);
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface LKS_ExportManager ()
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#else
|
||||
@property(nonatomic, strong) UIDocumentInteractionController *documentController;
|
||||
#endif
|
||||
|
||||
@property(nonatomic, strong) LKS_ExportManagerMaskView *maskView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_ExportManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static LKS_ExportManager *instance = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone{
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
#if TARGET_OS_TV
|
||||
- (void)exportAndShare {
|
||||
NSAssert(NO, @"not supported");
|
||||
}
|
||||
#else
|
||||
- (void)exportAndShare {
|
||||
|
||||
UIViewController *visibleVc = [UIViewController lks_visibleViewController];
|
||||
if (!visibleVc) {
|
||||
NSLog(@"LookinServer - Failed to export because we didn't find any visible view controller.");
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"Lookin_WillExport" object:nil];
|
||||
|
||||
if (!self.maskView) {
|
||||
self.maskView = [LKS_ExportManagerMaskView new];
|
||||
}
|
||||
[visibleVc.view.window addSubview:self.maskView];
|
||||
self.maskView.frame = visibleVc.view.window.bounds;
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
LookinHierarchyInfo *info = [LookinHierarchyInfo exportedInfo];
|
||||
LookinHierarchyFile *file = [LookinHierarchyFile new];
|
||||
file.serverVersion = info.serverVersion;
|
||||
file.hierarchyInfo = info;
|
||||
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:file];
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *fileName = ({
|
||||
NSString *timeString = ({
|
||||
NSDate *date = [NSDate date];
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
[formatter setDateFormat:@"MMddHHmm"];
|
||||
[formatter stringFromDate:date];
|
||||
});
|
||||
NSString *iOSVersion = ({
|
||||
NSString *str = info.appInfo.osDescription;
|
||||
NSUInteger dotIdx = [str rangeOfString:@"."].location;
|
||||
if (dotIdx != NSNotFound) {
|
||||
str = [str substringToIndex:dotIdx];
|
||||
}
|
||||
str;
|
||||
});
|
||||
[NSString stringWithFormat:@"%@_ios%@_%@.lookin", info.appInfo.appName, iOSVersion, timeString];
|
||||
});
|
||||
NSString *path = [NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), fileName];
|
||||
[data writeToFile:path atomically:YES];
|
||||
|
||||
[self.maskView removeFromSuperview];
|
||||
|
||||
if (!self.documentController) {
|
||||
self.documentController = [UIDocumentInteractionController new];
|
||||
}
|
||||
self.documentController.URL = [NSURL fileURLWithPath:path];
|
||||
if ([LKS_MultiplatformAdapter isiPad]) {
|
||||
[self.documentController presentOpenInMenuFromRect:CGRectMake(0, 0, 1, 1) inView:visibleVc.view animated:YES];
|
||||
} else {
|
||||
[self.documentController presentOpenInMenuFromRect:visibleVc.view.bounds inView:visibleVc.view animated:YES];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"Lookin_DidFinishExport" object:nil];
|
||||
|
||||
// [self.documentController presentOptionsMenuFromRect:visibleVc.view.bounds inView:visibleVc.view animated:YES];
|
||||
|
||||
// CFTimeInterval endTime = CACurrentMediaTime();
|
||||
// CFTimeInterval consumingTime = endTime - startTime;
|
||||
// NSLog(@"LookinServer - 导出 UI 结构耗时:%@", @(consumingTime));
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
26
Pods/LookinServer/Src/Main/Server/Others/LKS_GestureTargetActionsSearcher.h
generated
Normal file
26
Pods/LookinServer/Src/Main/Server/Others/LKS_GestureTargetActionsSearcher.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_GestureTargetActionsSearcher.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/9/11.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class LookinTwoTuple;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface LKS_GestureTargetActionsSearcher : NSObject
|
||||
|
||||
/// 返回一个 UIGestureRecognizer 实例身上绑定的 target & action 信息
|
||||
/// tuple.first => LookinWeakContainer(包裹着 target),tuple.second => action(方法名字符串)
|
||||
+ (NSArray<LookinTwoTuple *> *)getTargetActionsFromRecognizer:(UIGestureRecognizer *)recognizer;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
52
Pods/LookinServer/Src/Main/Server/Others/LKS_GestureTargetActionsSearcher.m
generated
Normal file
52
Pods/LookinServer/Src/Main/Server/Others/LKS_GestureTargetActionsSearcher.m
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_GestureTargetActionsSearcher.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likai.123 on 2023/9/11.
|
||||
//
|
||||
|
||||
#import "LKS_GestureTargetActionsSearcher.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "NSArray+Lookin.h"
|
||||
#import "LookinTuple.h"
|
||||
#import "LookinWeakContainer.h"
|
||||
|
||||
@implementation LKS_GestureTargetActionsSearcher
|
||||
|
||||
+ (NSArray<LookinTwoTuple *> *)getTargetActionsFromRecognizer:(UIGestureRecognizer *)recognizer {
|
||||
if (!recognizer) {
|
||||
return @[];
|
||||
}
|
||||
// KVC 要放到 try catch 里面防止 Crash
|
||||
@try {
|
||||
NSArray* targetsList = [recognizer valueForKey:@"_targets"];
|
||||
if (!targetsList || targetsList.count == 0) {
|
||||
return @[];
|
||||
}
|
||||
// 数组元素对象是 UIGestureRecognizerTarget*
|
||||
// 这个元素有两个属性,一个是名为 _target 的属性指向某个实例,另一个是名为 _action 的属性保存一个 SEL
|
||||
NSArray<LookinTwoTuple *>* ret = [targetsList lookin_map:^id(NSUInteger idx, id targetBox) {
|
||||
id targetObj = [targetBox valueForKey:@"_target"];
|
||||
if (!targetObj) {
|
||||
return nil;
|
||||
}
|
||||
SEL action = ((SEL (*)(id, Ivar))object_getIvar)(targetBox, class_getInstanceVariable([targetBox class], "_action"));
|
||||
|
||||
LookinTwoTuple* tuple = [LookinTwoTuple new];
|
||||
tuple.first = [LookinWeakContainer containerWithObject:targetObj];
|
||||
tuple.second = (action == NULL ? @"NULL" : NSStringFromSelector(action));
|
||||
return tuple;
|
||||
}];
|
||||
return ret;
|
||||
}
|
||||
@catch (NSException * e) {
|
||||
NSLog(@"LookinServer - %@", e);
|
||||
return @[];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
29
Pods/LookinServer/Src/Main/Server/Others/LKS_Helper.h
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Others/LKS_Helper.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_Helper.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/7/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define LKS_Localized(stringKey) NSLocalizedStringFromTableInBundle(stringKey, nil, [NSBundle bundleForClass:self.class], nil)
|
||||
|
||||
@interface LKS_Helper : NSObject
|
||||
|
||||
/// 如果 object 为 nil 则返回字符串 “nil”,否则返回字符串格式类似于 (UIView *)
|
||||
+ (NSString *)descriptionOfObject:(id)object;
|
||||
|
||||
/// 返回当前的bundle
|
||||
+ (NSBundle *)bundle;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
38
Pods/LookinServer/Src/Main/Server/Others/LKS_Helper.m
generated
Normal file
38
Pods/LookinServer/Src/Main/Server/Others/LKS_Helper.m
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_Helper.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/7/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_Helper.h"
|
||||
#import "NSObject+LookinServer.h"
|
||||
|
||||
@implementation LKS_Helper
|
||||
|
||||
+ (NSString *)descriptionOfObject:(id)object {
|
||||
if (!object) {
|
||||
return @"nil";
|
||||
}
|
||||
NSString *className = NSStringFromClass([object class]);
|
||||
return [NSString stringWithFormat:@"(%@ *)", className];
|
||||
}
|
||||
|
||||
+ (NSBundle *)bundle {
|
||||
static id bundle = nil;
|
||||
if (bundle != nil) {
|
||||
#ifdef SPM_RESOURCE_BUNDLE_IDENTIFITER
|
||||
bundle = [NSBundle bundleWithIdentifier:SPM_RESOURCE_BUNDLE_IDENTIFITER];
|
||||
#else
|
||||
bundle = [NSBundle bundleForClass:self.class];
|
||||
#endif
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
31
Pods/LookinServer/Src/Main/Server/Others/LKS_HierarchyDisplayItemsMaker.h
generated
Normal file
31
Pods/LookinServer/Src/Main/Server/Others/LKS_HierarchyDisplayItemsMaker.h
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_HierarchyDisplayItemsMaker.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/19.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
@class LookinDisplayItem;
|
||||
|
||||
@interface LKS_HierarchyDisplayItemsMaker : NSObject
|
||||
|
||||
/// @param hasScreenshots 是否包含 soloScreenshots 和 groupScreenshot 属性
|
||||
/// @param hasAttrList 是否包含 attributesGroupList 属性
|
||||
/// @param lowQuality screenshots 是否为低质量(当 hasScreenshots 为 NO 时,该属性无意义)
|
||||
/// @param readCustomInfo 是否读取 lookin_customDebugInfos,比如低版本的 Lookin 发请求时,就无需读取(因为 Lookin 解析不了、还可能出 Bug)
|
||||
/// @param saveCustomSetter 是否要读取并保存用户给 attribute 配置的 custom setter
|
||||
+ (NSArray<LookinDisplayItem *> *)itemsWithScreenshots:(BOOL)hasScreenshots attrList:(BOOL)hasAttrList lowImageQuality:(BOOL)lowQuality readCustomInfo:(BOOL)readCustomInfo saveCustomSetter:(BOOL)saveCustomSetter;
|
||||
|
||||
/// 把 layer 的 sublayers 转换为 displayItem 数组并返回
|
||||
+ (NSArray<LookinDisplayItem *> *)subitemsOfLayer:(CALayer *)layer;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
162
Pods/LookinServer/Src/Main/Server/Others/LKS_HierarchyDisplayItemsMaker.m
generated
Normal file
162
Pods/LookinServer/Src/Main/Server/Others/LKS_HierarchyDisplayItemsMaker.m
generated
Normal file
@@ -0,0 +1,162 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_HierarchyDisplayItemsMaker.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/19.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_HierarchyDisplayItemsMaker.h"
|
||||
#import "LookinDisplayItem.h"
|
||||
#import "LKS_TraceManager.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LKS_EventHandlerMaker.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
#import "LKSConfigManager.h"
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
#import "LKS_CustomDisplayItemsMaker.h"
|
||||
#import "LKS_CustomAttrSetterManager.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
@implementation LKS_HierarchyDisplayItemsMaker
|
||||
|
||||
+ (NSArray<LookinDisplayItem *> *)itemsWithScreenshots:(BOOL)hasScreenshots attrList:(BOOL)hasAttrList lowImageQuality:(BOOL)lowQuality readCustomInfo:(BOOL)readCustomInfo saveCustomSetter:(BOOL)saveCustomSetter {
|
||||
|
||||
[[LKS_TraceManager sharedInstance] reload];
|
||||
|
||||
NSArray<UIWindow *> *windows = [LKS_MultiplatformAdapter allWindows];
|
||||
NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:windows.count];
|
||||
[windows enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinDisplayItem *item = [self _displayItemWithLayer:window.layer screenshots:hasScreenshots attrList:hasAttrList lowImageQuality:lowQuality readCustomInfo:readCustomInfo saveCustomSetter:saveCustomSetter];
|
||||
item.representedAsKeyWindow = window.isKeyWindow;
|
||||
if (item) {
|
||||
[resultArray addObject:item];
|
||||
}
|
||||
}];
|
||||
|
||||
return [resultArray copy];
|
||||
}
|
||||
|
||||
+ (LookinDisplayItem *)_displayItemWithLayer:(CALayer *)layer screenshots:(BOOL)hasScreenshots attrList:(BOOL)hasAttrList lowImageQuality:(BOOL)lowQuality readCustomInfo:(BOOL)readCustomInfo saveCustomSetter:(BOOL)saveCustomSetter {
|
||||
if (!layer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
LookinDisplayItem *item = [LookinDisplayItem new];
|
||||
CGRect layerFrame = layer.frame;
|
||||
UIView *hostView = layer.lks_hostView;
|
||||
if (hostView && hostView.superview) {
|
||||
layerFrame = [hostView.superview convertRect:layerFrame toView:nil];
|
||||
}
|
||||
if ([self validateFrame:layerFrame]) {
|
||||
item.frame = layer.frame;
|
||||
} else {
|
||||
NSLog(@"LookinServer - The layer frame(%@) seems really weird. Lookin will ignore it to avoid potential render error in Lookin.", NSStringFromCGRect(layer.frame));
|
||||
item.frame = CGRectZero;
|
||||
}
|
||||
item.bounds = layer.bounds;
|
||||
if (hasScreenshots) {
|
||||
item.soloScreenshot = [layer lks_soloScreenshotWithLowQuality:lowQuality];
|
||||
item.groupScreenshot = [layer lks_groupScreenshotWithLowQuality:lowQuality];
|
||||
item.screenshotEncodeType = LookinDisplayItemImageEncodeTypeNSData;
|
||||
}
|
||||
|
||||
if (hasAttrList) {
|
||||
item.attributesGroupList = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
LKS_CustomAttrGroupsMaker *maker = [[LKS_CustomAttrGroupsMaker alloc] initWithLayer:layer];
|
||||
[maker execute];
|
||||
item.customAttrGroupList = [maker getGroups];
|
||||
item.customDisplayTitle = [maker getCustomDisplayTitle];
|
||||
item.danceuiSource = [maker getDanceUISource];
|
||||
}
|
||||
|
||||
item.isHidden = layer.isHidden;
|
||||
item.alpha = layer.opacity;
|
||||
item.layerObject = [LookinObject instanceWithObject:layer];
|
||||
item.shouldCaptureImage = [LKSConfigManager shouldCaptureScreenshotOfLayer:layer];
|
||||
|
||||
if (layer.lks_hostView) {
|
||||
UIView *view = layer.lks_hostView;
|
||||
item.viewObject = [LookinObject instanceWithObject:view];
|
||||
item.eventHandlers = [LKS_EventHandlerMaker makeForView:view];
|
||||
item.backgroundColor = view.backgroundColor;
|
||||
|
||||
UIViewController* vc = [view lks_findHostViewController];
|
||||
if (vc) {
|
||||
item.hostViewControllerObject = [LookinObject instanceWithObject:vc];
|
||||
}
|
||||
} else {
|
||||
item.backgroundColor = [UIColor lks_colorWithCGColor:layer.backgroundColor];
|
||||
}
|
||||
|
||||
if (layer.sublayers.count) {
|
||||
NSArray<CALayer *> *sublayers = [layer.sublayers copy];
|
||||
NSMutableArray<LookinDisplayItem *> *allSubitems = [NSMutableArray arrayWithCapacity:sublayers.count];
|
||||
[sublayers enumerateObjectsUsingBlock:^(__kindof CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinDisplayItem *sublayer_item = [self _displayItemWithLayer:sublayer screenshots:hasScreenshots attrList:hasAttrList lowImageQuality:lowQuality readCustomInfo:readCustomInfo saveCustomSetter:saveCustomSetter];
|
||||
if (sublayer_item) {
|
||||
[allSubitems addObject:sublayer_item];
|
||||
}
|
||||
}];
|
||||
item.subitems = [allSubitems copy];
|
||||
}
|
||||
if (readCustomInfo) {
|
||||
NSArray<LookinDisplayItem *> *customSubitems = [[[LKS_CustomDisplayItemsMaker alloc] initWithLayer:layer saveAttrSetter:saveCustomSetter] make];
|
||||
if (customSubitems.count > 0) {
|
||||
if (item.subitems) {
|
||||
item.subitems = [item.subitems arrayByAddingObjectsFromArray:customSubitems];
|
||||
} else {
|
||||
item.subitems = customSubitems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
+ (NSArray<LookinDisplayItem *> *)subitemsOfLayer:(CALayer *)layer {
|
||||
if (!layer || layer.sublayers.count == 0) {
|
||||
return @[];
|
||||
}
|
||||
[[LKS_TraceManager sharedInstance] reload];
|
||||
|
||||
NSMutableArray<LookinDisplayItem *> *resultSubitems = [NSMutableArray array];
|
||||
|
||||
NSArray<CALayer *> *sublayers = [layer.sublayers copy];
|
||||
[sublayers enumerateObjectsUsingBlock:^(__kindof CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinDisplayItem *sublayer_item = [self _displayItemWithLayer:sublayer screenshots:NO attrList:NO lowImageQuality:NO readCustomInfo:YES saveCustomSetter:YES];
|
||||
if (sublayer_item) {
|
||||
[resultSubitems addObject:sublayer_item];
|
||||
}
|
||||
}];
|
||||
|
||||
NSArray<LookinDisplayItem *> *customSubitems = [[[LKS_CustomDisplayItemsMaker alloc] initWithLayer:layer saveAttrSetter:YES] make];
|
||||
if (customSubitems.count > 0) {
|
||||
[resultSubitems addObjectsFromArray:customSubitems];
|
||||
}
|
||||
|
||||
return resultSubitems;
|
||||
}
|
||||
|
||||
+ (BOOL)validateFrame:(CGRect)frame {
|
||||
return !CGRectIsNull(frame) && !CGRectIsInfinite(frame) && ![self cgRectIsNaN:frame] && ![self cgRectIsInf:frame] && ![self cgRectIsUnreasonable:frame];
|
||||
}
|
||||
|
||||
+ (BOOL)cgRectIsNaN:(CGRect)rect {
|
||||
return isnan(rect.origin.x) || isnan(rect.origin.y) || isnan(rect.size.width) || isnan(rect.size.height);
|
||||
}
|
||||
|
||||
+ (BOOL)cgRectIsInf:(CGRect)rect {
|
||||
return isinf(rect.origin.x) || isinf(rect.origin.y) || isinf(rect.size.width) || isinf(rect.size.height);
|
||||
}
|
||||
|
||||
+ (BOOL)cgRectIsUnreasonable:(CGRect)rect {
|
||||
return ABS(rect.origin.x) > 100000 || ABS(rect.origin.y) > 100000 || rect.size.width < 0 || rect.size.height < 0 || rect.size.width > 100000 || rect.size.height > 100000;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
30
Pods/LookinServer/Src/Main/Server/Others/LKS_MultiplatformAdapter.h
generated
Normal file
30
Pods/LookinServer/Src/Main/Server/Others/LKS_MultiplatformAdapter.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_MultiplatformAdapter.h
|
||||
//
|
||||
//
|
||||
// Created by nixjiang on 2024/3/12.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface LKS_MultiplatformAdapter : NSObject
|
||||
|
||||
+ (UIWindow *)keyWindow;
|
||||
|
||||
+ (NSArray<UIWindow *> *)allWindows;
|
||||
|
||||
+ (CGRect)mainScreenBounds;
|
||||
|
||||
+ (CGFloat)mainScreenScale;
|
||||
|
||||
+ (BOOL)isiPad;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
92
Pods/LookinServer/Src/Main/Server/Others/LKS_MultiplatformAdapter.m
generated
Normal file
92
Pods/LookinServer/Src/Main/Server/Others/LKS_MultiplatformAdapter.m
generated
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_MultiplatformAdapter.m
|
||||
//
|
||||
//
|
||||
// Created by nixjiang on 2024/3/12.
|
||||
//
|
||||
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@implementation LKS_MultiplatformAdapter
|
||||
|
||||
+ (BOOL)isiPad {
|
||||
static BOOL s_isiPad = NO;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSString *nsModel = [UIDevice currentDevice].model;
|
||||
s_isiPad = [nsModel hasPrefix:@"iPad"];
|
||||
});
|
||||
|
||||
return s_isiPad;
|
||||
}
|
||||
|
||||
+ (CGRect)mainScreenBounds {
|
||||
#if TARGET_OS_VISION
|
||||
return [LKS_MultiplatformAdapter getFirstActiveWindowScene].coordinateSpace.bounds;
|
||||
#else
|
||||
return [UIScreen mainScreen].bounds;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (CGFloat)mainScreenScale {
|
||||
#if TARGET_OS_VISION
|
||||
return 2.f;
|
||||
#else
|
||||
return [UIScreen mainScreen].scale;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_OS_VISION
|
||||
+ (UIWindowScene *)getFirstActiveWindowScene {
|
||||
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
if (![scene isKindOfClass:UIWindowScene.class]) {
|
||||
continue;
|
||||
}
|
||||
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||
if (windowScene.activationState == UISceneActivationStateForegroundActive) {
|
||||
return windowScene;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (UIWindow *)keyWindow {
|
||||
#if TARGET_OS_VISION
|
||||
return [self getFirstActiveWindowScene].keyWindow;
|
||||
#else
|
||||
return [UIApplication sharedApplication].keyWindow;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSArray<UIWindow *> *)allWindows {
|
||||
#if TARGET_OS_VISION
|
||||
NSMutableArray<UIWindow *> *windows = [NSMutableArray new];
|
||||
for (UIScene *scene in
|
||||
UIApplication.sharedApplication.connectedScenes) {
|
||||
if (![scene isKindOfClass:UIWindowScene.class]) {
|
||||
continue;
|
||||
}
|
||||
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
||||
[windows addObjectsFromArray:windowScene.windows];
|
||||
|
||||
// 以UIModalPresentationFormSheet形式展示的页面由系统私有window承载,不出现在scene.windows,不过可以从scene.keyWindow中获取
|
||||
if (![windows containsObject:windowScene.keyWindow]) {
|
||||
if (![NSStringFromClass(windowScene.keyWindow.class) containsString:@"HUD"]) {
|
||||
[windows addObject:windowScene.keyWindow];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [windows copy];
|
||||
#else
|
||||
return [[UIApplication sharedApplication].windows copy];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
23
Pods/LookinServer/Src/Main/Server/Others/LKS_ObjectRegistry.h
generated
Normal file
23
Pods/LookinServer/Src/Main/Server/Others/LKS_ObjectRegistry.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_ObjectRegistry.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/4/21.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface LKS_ObjectRegistry : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (unsigned long)addObject:(NSObject *)object;
|
||||
|
||||
- (NSObject *)objectWithOid:(unsigned long)oid;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
62
Pods/LookinServer/Src/Main/Server/Others/LKS_ObjectRegistry.m
generated
Normal file
62
Pods/LookinServer/Src/Main/Server/Others/LKS_ObjectRegistry.m
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_ObjectRegistry.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/4/21.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_ObjectRegistry.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface LKS_ObjectRegistry ()
|
||||
|
||||
@property(nonatomic, strong) NSPointerArray *data;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_ObjectRegistry
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static LKS_ObjectRegistry *instance = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone{
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.data = [NSPointerArray weakObjectsPointerArray];
|
||||
// index 为 0 用 Null 填充
|
||||
self.data.count = 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned long)addObject:(NSObject *)object {
|
||||
if (!object) {
|
||||
return 0;
|
||||
}
|
||||
[self.data addPointer:(void *)object];
|
||||
return self.data.count - 1;
|
||||
}
|
||||
|
||||
- (NSObject *)objectWithOid:(unsigned long)oid {
|
||||
if (self.data.count <= oid) {
|
||||
return nil;
|
||||
}
|
||||
id object = [self.data pointerAtIndex:oid];
|
||||
return object;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
27
Pods/LookinServer/Src/Main/Server/Others/LKS_TraceManager.h
generated
Normal file
27
Pods/LookinServer/Src/Main/Server/Others/LKS_TraceManager.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_TraceManager.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/5/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinIvarTrace;
|
||||
|
||||
@interface LKS_TraceManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)reload;
|
||||
|
||||
- (void)addSearchTarger:(id)target;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
310
Pods/LookinServer/Src/Main/Server/Others/LKS_TraceManager.m
generated
Normal file
310
Pods/LookinServer/Src/Main/Server/Others/LKS_TraceManager.m
generated
Normal file
@@ -0,0 +1,310 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_TraceManager.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/5/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_TraceManager.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "LookinIvarTrace.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LookinWeakContainer.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
#ifdef LOOKIN_SERVER_SWIFT_ENABLED
|
||||
|
||||
#if __has_include(<LookinServer/LookinServer-Swift.h>)
|
||||
#import <LookinServer/LookinServer-Swift.h>
|
||||
#define LOOKIN_SERVER_SWIFT_ENABLED_SUCCESSFULLY
|
||||
#elif __has_include("LookinServer-Swift.h")
|
||||
#import "LookinServer-Swift.h"
|
||||
#define LOOKIN_SERVER_SWIFT_ENABLED_SUCCESSFULLY
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SPM_LOOKIN_SERVER_ENABLED
|
||||
@import LookinServerSwift;
|
||||
#define LOOKIN_SERVER_SWIFT_ENABLED_SUCCESSFULLY
|
||||
#endif
|
||||
|
||||
@interface LKS_TraceManager ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<LookinWeakContainer *> *searchTargets;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_TraceManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static LKS_TraceManager *instance = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone {
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
- (void)addSearchTarger:(id)target {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
if (!self.searchTargets) {
|
||||
self.searchTargets = [NSMutableArray array];
|
||||
}
|
||||
LookinWeakContainer *container = [LookinWeakContainer containerWithObject:target];
|
||||
[self.searchTargets addObject:container];
|
||||
}
|
||||
|
||||
- (void)reload {
|
||||
// 把旧的先都清理掉
|
||||
[NSObject lks_clearAllObjectsTraces];
|
||||
|
||||
[self.searchTargets enumerateObjectsUsingBlock:^(LookinWeakContainer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (!obj.object) {
|
||||
return;
|
||||
}
|
||||
[self _markIVarsInAllClassLevelsOfObject:obj.object];
|
||||
}];
|
||||
|
||||
[[LKS_MultiplatformAdapter allWindows] enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self _addTraceForLayersRootedByLayer:window.layer];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_addTraceForLayersRootedByLayer:(CALayer *)layer {
|
||||
UIView *view = layer.lks_hostView;
|
||||
|
||||
if ([view.superview lks_isChildrenViewOfTabBar]) {
|
||||
view.lks_isChildrenViewOfTabBar = YES;
|
||||
} else if ([view isKindOfClass:[UITabBar class]]) {
|
||||
view.lks_isChildrenViewOfTabBar = YES;
|
||||
}
|
||||
|
||||
if (view) {
|
||||
[self _markIVarsInAllClassLevelsOfObject:view];
|
||||
UIViewController* vc = [view lks_findHostViewController];
|
||||
if (vc) {
|
||||
[self _markIVarsInAllClassLevelsOfObject:vc];
|
||||
}
|
||||
|
||||
[self _buildSpecialTraceForView:view];
|
||||
} else {
|
||||
[self _markIVarsInAllClassLevelsOfObject:layer];
|
||||
}
|
||||
|
||||
[[layer.sublayers copy] enumerateObjectsUsingBlock:^(__kindof CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self _addTraceForLayersRootedByLayer:sublayer];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_buildSpecialTraceForView:(UIView *)view {
|
||||
UIViewController* vc = [view lks_findHostViewController];
|
||||
if (vc) {
|
||||
view.lks_specialTrace = [NSString stringWithFormat:@"%@.view", NSStringFromClass(vc.class)];
|
||||
|
||||
} else if ([view isKindOfClass:[UIWindow class]]) {
|
||||
CGFloat currentWindowLevel = ((UIWindow *)view).windowLevel;
|
||||
|
||||
if (((UIWindow *)view).isKeyWindow) {
|
||||
view.lks_specialTrace = [NSString stringWithFormat:@"KeyWindow ( Level: %@ )", @(currentWindowLevel)];
|
||||
} else {
|
||||
view.lks_specialTrace = [NSString stringWithFormat:@"WindowLevel: %@", @(currentWindowLevel)];
|
||||
}
|
||||
} else if ([view isKindOfClass:[UITableViewCell class]]) {
|
||||
((UITableViewCell *)view).backgroundView.lks_specialTrace = @"cell.backgroundView";
|
||||
((UITableViewCell *)view).accessoryView.lks_specialTrace = @"cell.accessoryView";
|
||||
|
||||
} else if ([view isKindOfClass:[UITableView class]]) {
|
||||
UITableView *tableView = (UITableView *)view;
|
||||
|
||||
NSMutableArray<NSNumber *> *relatedSectionIdx = [NSMutableArray array];
|
||||
[[tableView visibleCells] enumerateObjectsUsingBlock:^(__kindof UITableViewCell * _Nonnull cell, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
|
||||
cell.lks_specialTrace = [NSString stringWithFormat:@"{ sec:%@, row:%@ }", @(indexPath.section), @(indexPath.row)];
|
||||
|
||||
if (![relatedSectionIdx containsObject:@(indexPath.section)]) {
|
||||
[relatedSectionIdx addObject:@(indexPath.section)];
|
||||
}
|
||||
}];
|
||||
|
||||
[relatedSectionIdx enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSUInteger secIdx = [obj unsignedIntegerValue];
|
||||
UIView *secHeaderView = [tableView headerViewForSection:secIdx];
|
||||
secHeaderView.lks_specialTrace = [NSString stringWithFormat:@"sectionHeader { sec: %@ }", @(secIdx)];
|
||||
|
||||
UIView *secFooterView = [tableView footerViewForSection:secIdx];
|
||||
secFooterView.lks_specialTrace = [NSString stringWithFormat:@"sectionFooter { sec: %@ }", @(secIdx)];
|
||||
}];
|
||||
|
||||
} else if ([view isKindOfClass:[UICollectionView class]]) {
|
||||
UICollectionView *collectionView = (UICollectionView *)view;
|
||||
collectionView.backgroundView.lks_specialTrace = @"collectionView.backgroundView";
|
||||
|
||||
if (@available(iOS 9.0, *)) {
|
||||
[[collectionView indexPathsForVisibleSupplementaryElementsOfKind:UICollectionElementKindSectionHeader] enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull indexPath, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
UIView *headerView = [collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
|
||||
headerView.lks_specialTrace = [NSString stringWithFormat:@"sectionHeader { sec:%@ }", @(indexPath.section)];
|
||||
}];
|
||||
[[collectionView indexPathsForVisibleSupplementaryElementsOfKind:UICollectionElementKindSectionFooter] enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull indexPath, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
UIView *footerView = [collectionView supplementaryViewForElementKind:UICollectionElementKindSectionFooter atIndexPath:indexPath];
|
||||
footerView.lks_specialTrace = [NSString stringWithFormat:@"sectionFooter { sec:%@ }", @(indexPath.section)];
|
||||
}];
|
||||
}
|
||||
|
||||
[[collectionView visibleCells] enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull cell, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSIndexPath *indexPath = [collectionView indexPathForCell:cell];
|
||||
cell.lks_specialTrace = [NSString stringWithFormat:@"{ item:%@, sec:%@ }", @(indexPath.item), @(indexPath.section)];
|
||||
}];
|
||||
|
||||
} else if ([view isKindOfClass:[UITableViewHeaderFooterView class]]) {
|
||||
UITableViewHeaderFooterView *headerFooterView = (UITableViewHeaderFooterView *)view;
|
||||
headerFooterView.textLabel.lks_specialTrace = @"sectionHeaderFooter.textLabel";
|
||||
headerFooterView.detailTextLabel.lks_specialTrace = @"sectionHeaderFooter.detailTextLabel";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_markIVarsInAllClassLevelsOfObject:(NSObject *)object {
|
||||
[self _markIVarsOfObject:object class:object.class];
|
||||
#ifdef LOOKIN_SERVER_SWIFT_ENABLED_SUCCESSFULLY
|
||||
[LKS_SwiftTraceManager swiftMarkIVarsOfObject:object];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)_markIVarsOfObject:(NSObject *)hostObject class:(Class)targetClass {
|
||||
if (!targetClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<NSString *> *prefixesToTerminateRecursion = @[@"NSObject", @"UIResponder", @"UIButton", @"UIButtonLabel"];
|
||||
BOOL hasPrefix = [prefixesToTerminateRecursion lookin_any:^BOOL(NSString *prefix) {
|
||||
return [NSStringFromClass(targetClass) hasPrefix:prefix];
|
||||
}];
|
||||
if (hasPrefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int outCount = 0;
|
||||
Ivar *ivars = class_copyIvarList(targetClass, &outCount);
|
||||
for (unsigned int i = 0; i < outCount; i ++) {
|
||||
Ivar ivar = ivars[i];
|
||||
NSString *ivarType = [[NSString alloc] lookin_safeInitWithUTF8String:ivar_getTypeEncoding(ivar)];
|
||||
if (![ivarType hasPrefix:@"@"] || ivarType.length <= 3) {
|
||||
continue;
|
||||
}
|
||||
NSString *ivarClassName = [ivarType substringWithRange:NSMakeRange(2, ivarType.length - 3)];
|
||||
Class ivarClass = NSClassFromString(ivarClassName);
|
||||
if (![ivarClass isSubclassOfClass:[UIView class]]
|
||||
&& ![ivarClass isSubclassOfClass:[CALayer class]]
|
||||
&& ![ivarClass isSubclassOfClass:[UIViewController class]]
|
||||
&& ![ivarClass isSubclassOfClass:[UIGestureRecognizer class]]) {
|
||||
continue;
|
||||
}
|
||||
const char * ivarNameChar = ivar_getName(ivar);
|
||||
if (!ivarNameChar) {
|
||||
continue;
|
||||
}
|
||||
// 这个 ivarObject 可能的类型:UIView, CALayer, UIViewController, UIGestureRecognizer
|
||||
NSObject *ivarObject = object_getIvar(hostObject, ivar);
|
||||
if (!ivarObject || ![ivarObject isKindOfClass:[NSObject class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LookinIvarTrace *ivarTrace = [LookinIvarTrace new];
|
||||
ivarTrace.hostObject = hostObject;
|
||||
ivarTrace.hostClassName = [self makeDisplayClassNameWithSuper:targetClass childClass:hostObject.class];
|
||||
ivarTrace.ivarName = [[NSString alloc] lookin_safeInitWithUTF8String:ivarNameChar];
|
||||
|
||||
if (hostObject == ivarObject) {
|
||||
ivarTrace.relation = LookinIvarTraceRelationValue_Self;
|
||||
} else if ([hostObject isKindOfClass:[UIView class]]) {
|
||||
CALayer *ivarLayer = nil;
|
||||
if ([ivarObject isKindOfClass:[CALayer class]]) {
|
||||
ivarLayer = (CALayer *)ivarObject;
|
||||
} else if ([ivarObject isKindOfClass:[UIView class]]) {
|
||||
ivarLayer = ((UIView *)ivarObject).layer;
|
||||
}
|
||||
if (ivarLayer && (ivarLayer.superlayer == ((UIView *)hostObject).layer)) {
|
||||
ivarTrace.relation = @"superview";
|
||||
}
|
||||
}
|
||||
|
||||
if ([LKS_InvalidIvarTraces() containsObject:ivarTrace]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![ivarObject respondsToSelector:@selector(lks_ivarTraces)] || ![ivarObject respondsToSelector:@selector(setLks_ivarTraces:)]) {
|
||||
continue;
|
||||
}
|
||||
if (!ivarObject.lks_ivarTraces) {
|
||||
ivarObject.lks_ivarTraces = [NSArray array];
|
||||
}
|
||||
if (![ivarObject.lks_ivarTraces containsObject:ivarTrace]) {
|
||||
ivarObject.lks_ivarTraces = [ivarObject.lks_ivarTraces arrayByAddingObject:ivarTrace];
|
||||
}
|
||||
}
|
||||
free(ivars);
|
||||
|
||||
Class superClass = [targetClass superclass];
|
||||
[self _markIVarsOfObject:hostObject class:superClass];
|
||||
}
|
||||
|
||||
// 比如 superClass 可能是 UIView,而 childClass 可能是 UIButton
|
||||
- (NSString *)makeDisplayClassNameWithSuper:(Class)superClass childClass:(Class)childClass {
|
||||
NSString *superName = NSStringFromClass(superClass);
|
||||
if (!childClass) {
|
||||
return superName;
|
||||
}
|
||||
NSString *childName = NSStringFromClass(childClass);
|
||||
if ([childName isEqualToString:superName]) {
|
||||
return superName;
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@ : %@", childName, superName];
|
||||
}
|
||||
|
||||
static NSSet<LookinIvarTrace *> *LKS_InvalidIvarTraces(void) {
|
||||
static NSSet *list;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSMutableSet *set = [NSMutableSet set];
|
||||
|
||||
[set addObject:({
|
||||
LookinIvarTrace *trace = [LookinIvarTrace new];
|
||||
trace.hostClassName = @"UIView";
|
||||
trace.ivarName = @"_window";
|
||||
trace;
|
||||
})];
|
||||
[set addObject:({
|
||||
LookinIvarTrace *trace = [LookinIvarTrace new];
|
||||
trace.hostClassName = @"UIViewController";
|
||||
trace.ivarName = @"_view";
|
||||
trace;
|
||||
})];
|
||||
[set addObject:({
|
||||
LookinIvarTrace *trace = [LookinIvarTrace new];
|
||||
trace.hostClassName = @"UIView";
|
||||
trace.ivarName = @"_viewDelegate";
|
||||
trace;
|
||||
})];
|
||||
[set addObject:({
|
||||
LookinIvarTrace *trace = [LookinIvarTrace new];
|
||||
trace.hostClassName = @"UIViewController";
|
||||
trace.ivarName = @"_parentViewController";
|
||||
trace;
|
||||
})];
|
||||
list = set.copy;
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
23
Pods/LookinServer/Src/Main/Server/Others/LookinServerDefines.h
generated
Normal file
23
Pods/LookinServer/Src/Main/Server/Others/LookinServerDefines.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinServer_PrefixHeader.pch
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2018/12/21.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "TargetConditionals.h"
|
||||
#import "LookinDefines.h"
|
||||
#import "LKS_Helper.h"
|
||||
#import "NSObject+LookinServer.h"
|
||||
#import "NSArray+Lookin.h"
|
||||
#import "NSSet+Lookin.h"
|
||||
#import "CALayer+Lookin.h"
|
||||
#import "UIView+LookinServer.h"
|
||||
#import "CALayer+LookinServer.h"
|
||||
#import "NSObject+Lookin.h"
|
||||
#import "NSString+Lookin.h"
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
Reference in New Issue
Block a user