487 lines
17 KiB
Objective-C
487 lines
17 KiB
Objective-C
#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 */
|