1
This commit is contained in:
@@ -6,11 +6,16 @@
|
||||
//
|
||||
|
||||
#import "KeyboardViewController.h"
|
||||
#import "KBToolBar.h"
|
||||
#import "KBKeyboardView.h"
|
||||
#import "KBKey.h"
|
||||
|
||||
static CGFloat KEYBOARDHEIGHT = 256;
|
||||
|
||||
@interface KeyboardViewController ()
|
||||
@property (nonatomic, strong) UIButton *nextKeyboardButton;
|
||||
@interface KeyboardViewController () <KBToolBarDelegate, KBKeyboardViewDelegate>
|
||||
@property (nonatomic, strong) UIButton *nextKeyboardButton; // 系统“下一个键盘”按钮(可选)
|
||||
@property (nonatomic, strong) KBToolBar *topBar;
|
||||
@property (nonatomic, strong) KBKeyboardView *keyboardView;
|
||||
@end
|
||||
|
||||
@implementation KeyboardViewController
|
||||
@@ -18,7 +23,7 @@ static CGFloat KEYBOARDHEIGHT = 256;
|
||||
- (void)updateViewConstraints {
|
||||
[super updateViewConstraints];
|
||||
|
||||
// Add custom view sizing constraints here
|
||||
// 可在此添加自定义尺寸约束
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -43,14 +48,29 @@ static CGFloat KEYBOARDHEIGHT = 256;
|
||||
|
||||
|
||||
- (void)setupUI {
|
||||
CGFloat toolBarHeight = 40;
|
||||
CGFloat bottom = 5;
|
||||
CGFloat buttonSpace = 8;
|
||||
CGFloat eachButtonHeight = (KEYBOARDHEIGHT - toolBarHeight - 10 - 8 * 3 - bottom) / 4;
|
||||
|
||||
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 30)];
|
||||
view.backgroundColor = [UIColor redColor];
|
||||
[self.view addSubview:view];
|
||||
// self.view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
// 固定键盘整体高度
|
||||
[self.view.heightAnchor constraintEqualToConstant:KEYBOARDHEIGHT].active = YES;
|
||||
|
||||
// 顶部栏
|
||||
self.topBar = [[KBToolBar alloc] init];
|
||||
self.topBar.delegate = self;
|
||||
[self.view addSubview:self.topBar];
|
||||
[self.topBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.view);
|
||||
make.top.equalTo(self.view.mas_top).offset(6);
|
||||
make.height.mas_equalTo(40);
|
||||
}];
|
||||
|
||||
// 键盘区域
|
||||
self.keyboardView = [[KBKeyboardView alloc] init];
|
||||
self.keyboardView.delegate = self;
|
||||
[self.view addSubview:self.keyboardView];
|
||||
[self.keyboardView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.view);
|
||||
make.top.equalTo(self.topBar.mas_bottom).offset(4);
|
||||
make.bottom.equalTo(self.view.mas_bottom).offset(-4);
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -61,11 +81,11 @@ static CGFloat KEYBOARDHEIGHT = 256;
|
||||
}
|
||||
|
||||
- (void)textWillChange:(id<UITextInput>)textInput {
|
||||
// The app is about to change the document's contents. Perform any preparation here.
|
||||
// 文档内容即将变化,可在此做准备
|
||||
}
|
||||
|
||||
- (void)textDidChange:(id<UITextInput>)textInput {
|
||||
// The app has just changed the document's contents, the document context has been updated.
|
||||
// 文档内容刚发生变化,环境已更新
|
||||
|
||||
UIColor *textColor = nil;
|
||||
if (self.textDocumentProxy.keyboardAppearance == UIKeyboardAppearanceDark) {
|
||||
@@ -76,4 +96,46 @@ static CGFloat KEYBOARDHEIGHT = 256;
|
||||
[self.nextKeyboardButton setTitleColor:textColor forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
#pragma mark - KBToolBarDelegate
|
||||
|
||||
- (void)toolBar:(KBToolBar *)toolBar didTapActionAtIndex:(NSInteger)index {
|
||||
// 可根据业务透传或处理。示例:插入占位标记 [A1]..[A4]
|
||||
NSString *placeholder = [NSString stringWithFormat:@"[A%ld]", (long)index+1];
|
||||
[self.textDocumentProxy insertText:placeholder];
|
||||
}
|
||||
|
||||
- (void)toolBarDidTapSettings:(KBToolBar *)toolBar {
|
||||
// 通常可通过 openURL 或宿主处理打开设置页。
|
||||
// 这里示例仅插入一个标记。
|
||||
[self.textDocumentProxy insertText:@"[settings]"];
|
||||
}
|
||||
|
||||
#pragma mark - KBKeyboardViewDelegate
|
||||
|
||||
- (void)keyboardView:(KBKeyboardView *)keyboard didTapKey:(KBKey *)key {
|
||||
switch (key.type) {
|
||||
case KBKeyTypeCharacter:
|
||||
[self.textDocumentProxy insertText:key.output ?: key.title ?: @""]; break;
|
||||
case KBKeyTypeBackspace:
|
||||
[self.textDocumentProxy deleteBackward]; break;
|
||||
case KBKeyTypeSpace:
|
||||
[self.textDocumentProxy insertText:@" "]; break;
|
||||
case KBKeyTypeReturn:
|
||||
[self.textDocumentProxy insertText:@"\n"]; break;
|
||||
case KBKeyTypeModeChange: {
|
||||
// 切换 字母 <-> 数字 布局
|
||||
keyboard.layoutStyle = (keyboard.layoutStyle == KBKeyboardLayoutStyleLetters) ? KBKeyboardLayoutStyleNumbers : KBKeyboardLayoutStyleLetters;
|
||||
[keyboard reloadKeys];
|
||||
} break;
|
||||
case KBKeyTypeGlobe:
|
||||
[self advanceToNextInputMode]; break;
|
||||
case KBKeyTypeCustom:
|
||||
// 自定义占位:切换语言或其它操作
|
||||
[self.textDocumentProxy insertText:@"[lang]"]; break;
|
||||
case KBKeyTypeShift:
|
||||
// Shift 已在 KBKeyboardView 内部处理
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
31
CustomKeyboard/Model/KBKey.h
Normal file
31
CustomKeyboard/Model/KBKey.h
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// KBKey.h
|
||||
// CustomKeyboard
|
||||
//
|
||||
// 简单的键位数据模型,用于描述键盘上的一个键。
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, KBKeyType) {
|
||||
KBKeyTypeCharacter = 0, // 普通字符输出
|
||||
KBKeyTypeBackspace, // 删除
|
||||
KBKeyTypeShift, // 大小写切换
|
||||
KBKeyTypeModeChange, // 模式切换(如 123/ABC)
|
||||
KBKeyTypeSpace, // 空格
|
||||
KBKeyTypeReturn, // 回车/发送
|
||||
KBKeyTypeGlobe, // 系统地球键
|
||||
KBKeyTypeCustom // 自定义功能占位
|
||||
};
|
||||
|
||||
@interface KBKey : NSObject
|
||||
|
||||
@property (nonatomic, assign) KBKeyType type;
|
||||
@property (nonatomic, copy) NSString *title; // 显示标题
|
||||
@property (nonatomic, copy) NSString *output; // 字符键插入的文本
|
||||
|
||||
+ (instancetype)keyWithTitle:(NSString *)title output:(NSString *)output;
|
||||
+ (instancetype)keyWithTitle:(NSString *)title type:(KBKeyType)type;
|
||||
|
||||
@end
|
||||
27
CustomKeyboard/Model/KBKey.m
Normal file
27
CustomKeyboard/Model/KBKey.m
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// KBKey.m
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import "KBKey.h"
|
||||
|
||||
@implementation KBKey
|
||||
|
||||
+ (instancetype)keyWithTitle:(NSString *)title output:(NSString *)output {
|
||||
KBKey *k = [[KBKey alloc] init];
|
||||
k.type = KBKeyTypeCharacter;
|
||||
k.title = title ?: @"";
|
||||
k.output = output ?: title ?: @"";
|
||||
return k;
|
||||
}
|
||||
|
||||
+ (instancetype)keyWithTitle:(NSString *)title type:(KBKeyType)type {
|
||||
KBKey *k = [[KBKey alloc] init];
|
||||
k.type = type;
|
||||
k.title = title ?: @"";
|
||||
k.output = @"";
|
||||
return k;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
|
||||
|
||||
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
|
||||
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.width
|
||||
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
|
||||
#define imageNamed(s) [UIImage imageNamed:s]
|
||||
|
||||
#import "Masonry.h"
|
||||
|
||||
17
CustomKeyboard/View/KBKeyButton.h
Normal file
17
CustomKeyboard/View/KBKeyButton.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// KBKeyButton.h
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class KBKey;
|
||||
|
||||
/// 自定义键按钮(UIButton 子类):圆角外观,按下高亮效果。
|
||||
@interface KBKeyButton : UIButton
|
||||
|
||||
@property (nonatomic, strong) KBKey *key;
|
||||
|
||||
/// 配置基础样式(背景、圆角等)。创建按钮时调用。
|
||||
- (void)applyDefaultStyle;
|
||||
|
||||
@end
|
||||
36
CustomKeyboard/View/KBKeyButton.m
Normal file
36
CustomKeyboard/View/KBKeyButton.m
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// KBKeyButton.m
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import "KBKeyButton.h"
|
||||
#import "KBKey.h"
|
||||
|
||||
@implementation KBKeyButton
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self applyDefaultStyle];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)applyDefaultStyle {
|
||||
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold]; // 字体样式
|
||||
[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
||||
[self setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
self.layer.cornerRadius = 6.0; // 圆角
|
||||
self.layer.masksToBounds = NO;
|
||||
self.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor; // 阴影效果
|
||||
self.layer.shadowOpacity = 1.0;
|
||||
self.layer.shadowOffset = CGSizeMake(0, 1);
|
||||
self.layer.shadowRadius = 1.5;
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted {
|
||||
[super setHighlighted:highlighted];
|
||||
self.alpha = highlighted ? 0.7 : 1.0; // 简单按压反馈
|
||||
}
|
||||
|
||||
@end
|
||||
31
CustomKeyboard/View/KBKeyboardView.h
Normal file
31
CustomKeyboard/View/KBKeyboardView.h
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// KBKeyboardView.h
|
||||
// CustomKeyboard
|
||||
//
|
||||
// 键盘主容器,内部管理按键行布局。
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class KBKeyboardView, KBKey;
|
||||
|
||||
typedef NS_ENUM(NSInteger, KBKeyboardLayoutStyle) {
|
||||
KBKeyboardLayoutStyleLetters = 0,
|
||||
KBKeyboardLayoutStyleNumbers
|
||||
};
|
||||
|
||||
@protocol KBKeyboardViewDelegate <NSObject>
|
||||
@optional
|
||||
/// 键被点击的回调
|
||||
- (void)keyboardView:(KBKeyboardView *)keyboard didTapKey:(KBKey *)key;
|
||||
@end
|
||||
|
||||
@interface KBKeyboardView : UIView
|
||||
|
||||
@property (nonatomic, weak) id<KBKeyboardViewDelegate> delegate;
|
||||
@property (nonatomic, assign) KBKeyboardLayoutStyle layoutStyle; // 布局样式(字母/数字)
|
||||
@property (nonatomic, assign, getter=isShiftOn) BOOL shiftOn; // 大小写状态
|
||||
|
||||
- (void)reloadKeys; // 当布局样式/大小写变化时调用
|
||||
|
||||
@end
|
||||
255
CustomKeyboard/View/KBKeyboardView.m
Normal file
255
CustomKeyboard/View/KBKeyboardView.m
Normal file
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// KBKeyboardView.m
|
||||
// CustomKeyboard
|
||||
//
|
||||
|
||||
#import "KBKeyboardView.h"
|
||||
#import "KBKeyButton.h"
|
||||
#import "KBKey.h"
|
||||
|
||||
@interface KBKeyboardView ()
|
||||
@property (nonatomic, strong) UIView *row1;
|
||||
@property (nonatomic, strong) UIView *row2;
|
||||
@property (nonatomic, strong) UIView *row3;
|
||||
@property (nonatomic, strong) UIView *row4;
|
||||
@property (nonatomic, strong) NSArray<NSArray<KBKey *> *> *keysForRows;
|
||||
@end
|
||||
|
||||
@implementation KBKeyboardView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
|
||||
_layoutStyle = KBKeyboardLayoutStyleLetters;
|
||||
_shiftOn = YES; // 初始使用大写,贴近截图效果
|
||||
[self buildBase];
|
||||
[self reloadKeys];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)buildBase {
|
||||
[self addSubview:self.row1];
|
||||
[self addSubview:self.row2];
|
||||
[self addSubview:self.row3];
|
||||
[self addSubview:self.row4];
|
||||
|
||||
CGFloat vSpacing = 8;
|
||||
[self.row1 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.mas_top).offset(8);
|
||||
make.left.right.equalTo(self);
|
||||
make.height.mas_equalTo(44);
|
||||
}];
|
||||
[self.row2 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.row1.mas_bottom).offset(vSpacing);
|
||||
make.left.right.equalTo(self);
|
||||
make.height.equalTo(self.row1);
|
||||
}];
|
||||
[self.row3 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.row2.mas_bottom).offset(vSpacing);
|
||||
make.left.right.equalTo(self);
|
||||
make.height.equalTo(self.row1);
|
||||
}];
|
||||
[self.row4 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.row3.mas_bottom).offset(vSpacing);
|
||||
make.left.right.equalTo(self);
|
||||
make.height.equalTo(self.row1);
|
||||
make.bottom.equalTo(self.mas_bottom).offset(-6);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)reloadKeys {
|
||||
// 移除旧按钮
|
||||
for (UIView *row in @[self.row1, self.row2, self.row3, self.row4]) {
|
||||
[row.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
}
|
||||
|
||||
self.keysForRows = [self buildKeysForCurrentLayout];
|
||||
[self buildRow:self.row1 withKeys:self.keysForRows[0]];
|
||||
// 第二行:字母布局时通过左右等宽占位让整行居中
|
||||
CGFloat row2Spacer = (self.layoutStyle == KBKeyboardLayoutStyleLetters) ? 0.5 : 0.0;
|
||||
[self buildRow:self.row2 withKeys:self.keysForRows[1] edgeSpacerMultiplier:row2Spacer];
|
||||
[self buildRow:self.row3 withKeys:self.keysForRows[2]];
|
||||
[self buildRow:self.row4 withKeys:self.keysForRows[3]];
|
||||
}
|
||||
|
||||
- (NSArray<NSArray<KBKey *> *> *)buildKeysForCurrentLayout {
|
||||
if (self.layoutStyle == KBKeyboardLayoutStyleNumbers) {
|
||||
// 数字/符号布局:3 行主键 + 底部控制行
|
||||
NSArray *r1 = @[
|
||||
[KBKey keyWithTitle:@"1" output:@"1"], [KBKey keyWithTitle:@"2" output:@"2"], [KBKey keyWithTitle:@"3" output:@"3"],
|
||||
[KBKey keyWithTitle:@"4" output:@"4"], [KBKey keyWithTitle:@"5" output:@"5"], [KBKey keyWithTitle:@"6" output:@"6"],
|
||||
[KBKey keyWithTitle:@"7" output:@"7"], [KBKey keyWithTitle:@"8" output:@"8"], [KBKey keyWithTitle:@"9" output:@"9"], [KBKey keyWithTitle:@"0" output:@"0"],
|
||||
];
|
||||
NSArray *r2 = @[
|
||||
[KBKey keyWithTitle:@"-" output:@"-"], [KBKey keyWithTitle:@"/" output:@"/"], [KBKey keyWithTitle:@":" output:@":"],
|
||||
[KBKey keyWithTitle:@";" output:@";"], [KBKey keyWithTitle:@"(" output:@"("], [KBKey keyWithTitle:@")" output:@")"],
|
||||
[KBKey keyWithTitle:@"$" output:@"$"], [KBKey keyWithTitle:@"&" output:@"&"], [KBKey keyWithTitle:@"@" output:@"@"], [KBKey keyWithTitle:@"\"" output:@"\""],
|
||||
];
|
||||
NSArray *r3 = @[
|
||||
[KBKey keyWithTitle:@"#+=" type:KBKeyTypeModeChange],
|
||||
[KBKey keyWithTitle:@"," output:@","], [KBKey keyWithTitle:@"." output:@"."], [KBKey keyWithTitle:@"?" output:@"?"],
|
||||
[KBKey keyWithTitle:@"!" output:@"!"], [KBKey keyWithTitle:@"'" output:@"'"],
|
||||
[KBKey keyWithTitle:@"⌫" type:KBKeyTypeBackspace],
|
||||
];
|
||||
NSArray *r4 = @[
|
||||
[KBKey keyWithTitle:@"ABC" type:KBKeyTypeModeChange],
|
||||
[KBKey keyWithTitle:@"," output:@","],
|
||||
[KBKey keyWithTitle:@"space" type:KBKeyTypeSpace],
|
||||
[KBKey keyWithTitle:@"中/英" type:KBKeyTypeCustom],
|
||||
[KBKey keyWithTitle:@"发送" type:KBKeyTypeReturn],
|
||||
];
|
||||
return @[r1, r2, r3, r4];
|
||||
}
|
||||
|
||||
// 字母布局(QWERTY)
|
||||
NSArray *r1 = @[ @"Q", @"W", @"E", @"R", @"T", @"Y", @"U", @"I", @"O", @"P" ];
|
||||
NSArray *r2 = @[ @"A", @"S", @"D", @"F", @"G", @"H", @"J", @"K", @"L" ];
|
||||
NSArray *r3chars = @[ @"Z", @"X", @"C", @"V", @"B", @"N", @"M" ];
|
||||
|
||||
NSMutableArray *row1 = [NSMutableArray arrayWithCapacity:r1.count];
|
||||
for (NSString *s in r1) { [row1 addObject:[KBKey keyWithTitle:s output:(self.shiftOn ? s : s.lowercaseString)]]; }
|
||||
|
||||
NSMutableArray *row2 = [NSMutableArray arrayWithCapacity:r2.count];
|
||||
for (NSString *s in r2) { [row2 addObject:[KBKey keyWithTitle:s output:(self.shiftOn ? s : s.lowercaseString)]]; }
|
||||
|
||||
NSMutableArray *row3 = [NSMutableArray array];
|
||||
[row3 addObject:[KBKey keyWithTitle:@"⇧" type:KBKeyTypeShift]];
|
||||
for (NSString *s in r3chars) { [row3 addObject:[KBKey keyWithTitle:s output:(self.shiftOn ? s : s.lowercaseString)]]; }
|
||||
[row3 addObject:[KBKey keyWithTitle:@"⌫" type:KBKeyTypeBackspace]];
|
||||
|
||||
NSArray *row4 = @[ [KBKey keyWithTitle:@"123" type:KBKeyTypeModeChange],
|
||||
[KBKey keyWithTitle:@"," output:@","],
|
||||
[KBKey keyWithTitle:@"space" type:KBKeyTypeSpace],
|
||||
[KBKey keyWithTitle:@"中/英" type:KBKeyTypeCustom],
|
||||
[KBKey keyWithTitle:@"发送" type:KBKeyTypeReturn] ];
|
||||
|
||||
return @[row1.copy, row2.copy, row3.copy, row4];
|
||||
}
|
||||
|
||||
- (void)buildRow:(UIView *)row withKeys:(NSArray<KBKey *> *)keys {
|
||||
[self buildRow:row withKeys:keys edgeSpacerMultiplier:0.0];
|
||||
}
|
||||
|
||||
- (void)buildRow:(UIView *)row withKeys:(NSArray<KBKey *> *)keys edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier {
|
||||
CGFloat hInset = 6; // 行左右内边距
|
||||
CGFloat spacing = 6; // 键与键之间的间距
|
||||
UIView *previous = nil;
|
||||
UIView *leftSpacer = nil;
|
||||
UIView *rightSpacer = nil;
|
||||
if (edgeSpacerMultiplier > 0.0) {
|
||||
leftSpacer = [UIView new];
|
||||
rightSpacer = [UIView new];
|
||||
leftSpacer.backgroundColor = [UIColor clearColor];
|
||||
rightSpacer.backgroundColor = [UIColor clearColor];
|
||||
[row addSubview:leftSpacer];
|
||||
[row addSubview:rightSpacer];
|
||||
[leftSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(row.mas_left).offset(hInset);
|
||||
make.centerY.equalTo(row);
|
||||
make.height.mas_equalTo(1);
|
||||
}];
|
||||
[rightSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(row.mas_right).offset(-hInset);
|
||||
make.centerY.equalTo(row);
|
||||
make.height.mas_equalTo(1);
|
||||
}];
|
||||
}
|
||||
for (NSInteger i = 0; i < keys.count; i++) {
|
||||
KBKey *key = keys[i];
|
||||
KBKeyButton *btn = [[KBKeyButton alloc] init];
|
||||
btn.key = key;
|
||||
[btn setTitle:key.title forState:UIControlStateNormal];
|
||||
[btn addTarget:self action:@selector(onKeyTapped:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[row addSubview:btn];
|
||||
|
||||
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.bottom.equalTo(row);
|
||||
if (previous) {
|
||||
make.left.equalTo(previous.mas_right).offset(spacing);
|
||||
} else {
|
||||
if (leftSpacer) {
|
||||
make.left.equalTo(leftSpacer.mas_right).offset(spacing);
|
||||
} else {
|
||||
make.left.equalTo(row.mas_left).offset(hInset);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
// 宽度规则:字符键等宽;特殊键按倍数放大
|
||||
if (key.type == KBKeyTypeCharacter) {
|
||||
if (previous && previous != nil) {
|
||||
if (((KBKeyButton *)previous).key.type == KBKeyTypeCharacter) {
|
||||
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(previous);
|
||||
}];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// special keys: give 1.5x of a character key by deferring constraint equalities after loop
|
||||
}
|
||||
|
||||
previous = btn;
|
||||
}
|
||||
// 右侧使用内边距或右占位
|
||||
[previous mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
if (rightSpacer) {
|
||||
make.right.equalTo(rightSpacer.mas_left).offset(-spacing);
|
||||
} else {
|
||||
make.right.equalTo(row.mas_right).offset(-hInset);
|
||||
}
|
||||
}];
|
||||
|
||||
// 第二遍:以首个字符键为基准,统一设置特殊键宽度倍数
|
||||
KBKeyButton *firstChar = nil;
|
||||
for (KBKeyButton *b in row.subviews) {
|
||||
if ([b isKindOfClass:[KBKeyButton class]] && b.key.type == KBKeyTypeCharacter) { firstChar = b; break; }
|
||||
}
|
||||
if (firstChar) {
|
||||
for (KBKeyButton *b in row.subviews) {
|
||||
if (![b isKindOfClass:[KBKeyButton class]]) continue;
|
||||
if (b.key.type == KBKeyTypeCharacter) continue;
|
||||
CGFloat multiplier = 1.5;
|
||||
if (b.key.type == KBKeyTypeSpace) multiplier = 4.0;
|
||||
if (b.key.type == KBKeyTypeReturn) multiplier = 1.8;
|
||||
if (b.key.type == KBKeyTypeModeChange || b.key.type == KBKeyTypeGlobe || b.key.type == KBKeyTypeShift || b.key.type == KBKeyTypeBackspace) {
|
||||
multiplier = 1.5;
|
||||
}
|
||||
[b mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(firstChar).multipliedBy(multiplier);
|
||||
}];
|
||||
}
|
||||
// 如果有左右占位,则把占位宽度设置为字符键宽度的一定倍数,以实现整体居中
|
||||
if (leftSpacer && rightSpacer) {
|
||||
[leftSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(firstChar).multipliedBy(edgeSpacerMultiplier);
|
||||
}];
|
||||
[rightSpacer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.equalTo(firstChar).multipliedBy(edgeSpacerMultiplier);
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)onKeyTapped:(KBKeyButton *)sender {
|
||||
KBKey *key = sender.key;
|
||||
if (key.type == KBKeyTypeShift) {
|
||||
self.shiftOn = !self.shiftOn;
|
||||
[self reloadKeys];
|
||||
return;
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(keyboardView:didTapKey:)]) {
|
||||
[self.delegate keyboardView:self didTapKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Lazy
|
||||
|
||||
- (UIView *)row1 { if (!_row1) _row1 = [UIView new]; return _row1; }
|
||||
- (UIView *)row2 { if (!_row2) _row2 = [UIView new]; return _row2; }
|
||||
- (UIView *)row3 { if (!_row3) _row3 = [UIView new]; return _row3; }
|
||||
- (UIView *)row4 { if (!_row4) _row4 = [UIView new]; return _row4; }
|
||||
|
||||
@end
|
||||
@@ -9,8 +9,28 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class KBToolBar;
|
||||
|
||||
@protocol KBToolBarDelegate <NSObject>
|
||||
@optional
|
||||
/// 左侧 4 个功能按钮点击(index: 0~3)
|
||||
- (void)toolBar:(KBToolBar *)toolBar didTapActionAtIndex:(NSInteger)index;
|
||||
/// 右侧设置按钮点击
|
||||
- (void)toolBarDidTapSettings:(KBToolBar *)toolBar;
|
||||
@end
|
||||
|
||||
/// 顶部工具栏:左侧 4 个按钮,右侧 1 个设置按钮。
|
||||
@interface KBToolBar : UIView
|
||||
|
||||
@property (nonatomic, weak, nullable) id<KBToolBarDelegate> delegate;
|
||||
|
||||
/// 左侧 4 个按钮的标题。默认值:@[@"Item1", @"Item2", @"Item3", @"Item4"]。
|
||||
@property (nonatomic, copy) NSArray<NSString *> *leftButtonTitles;
|
||||
|
||||
/// 暴露按钮以便外部定制(只读;首次访问时懒加载创建)
|
||||
@property (nonatomic, strong, readonly) NSArray<UIButton *> *leftButtons;
|
||||
@property (nonatomic, strong, readonly) UIButton *settingsButton;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -7,15 +7,139 @@
|
||||
|
||||
#import "KBToolBar.h"
|
||||
|
||||
@interface KBToolBar ()
|
||||
@property (nonatomic, strong) UIView *leftContainer;
|
||||
@property (nonatomic, strong) NSArray<UIButton *> *leftButtonsInternal;
|
||||
@property (nonatomic, strong) UIButton *settingsButtonInternal;
|
||||
@end
|
||||
|
||||
@implementation KBToolBar
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
_leftButtonTitles = @[@"Item1", @"Item2", @"Item3", @"Item4"]; // 默认标题
|
||||
[self setupUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (NSArray<UIButton *> *)leftButtons {
|
||||
return self.leftButtonsInternal;
|
||||
}
|
||||
|
||||
- (UIButton *)settingsButton {
|
||||
return self.settingsButtonInternal;
|
||||
}
|
||||
|
||||
- (void)setLeftButtonTitles:(NSArray<NSString *> *)leftButtonTitles {
|
||||
_leftButtonTitles = [leftButtonTitles copy];
|
||||
// Update titles if buttons already exist
|
||||
[self.leftButtonsInternal enumerateObjectsUsingBlock:^(UIButton * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (idx < self.leftButtonTitles.count) {
|
||||
[obj setTitle:self.leftButtonTitles[idx] forState:UIControlStateNormal];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - 视图搭建
|
||||
|
||||
- (void)setupUI {
|
||||
[self addSubview:self.leftContainer];
|
||||
[self addSubview:self.settingsButtonInternal];
|
||||
|
||||
// 右侧设置按钮
|
||||
[self.settingsButtonInternal mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.mas_right).offset(-12);
|
||||
make.centerY.equalTo(self.mas_centerY);
|
||||
make.width.height.mas_equalTo(32);
|
||||
}];
|
||||
|
||||
// 左侧容器占用剩余空间
|
||||
[self.leftContainer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.mas_left).offset(12);
|
||||
make.right.equalTo(self.settingsButtonInternal.mas_left).offset(-12);
|
||||
make.centerY.equalTo(self.mas_centerY);
|
||||
make.height.mas_equalTo(32);
|
||||
}];
|
||||
|
||||
// 在左侧容器中创建 4 个等宽按钮
|
||||
NSMutableArray<UIButton *> *buttons = [NSMutableArray arrayWithCapacity:4];
|
||||
UIView *previous = nil;
|
||||
for (NSInteger i = 0; i < 4; i++) {
|
||||
UIButton *btn = [self buildActionButtonAtIndex:i];
|
||||
[self.leftContainer addSubview:btn];
|
||||
[buttons addObject:btn];
|
||||
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
if (previous) {
|
||||
make.left.equalTo(previous.mas_right).offset(8);
|
||||
make.width.equalTo(previous);
|
||||
} else {
|
||||
make.left.equalTo(self.leftContainer.mas_left);
|
||||
}
|
||||
make.top.bottom.equalTo(self.leftContainer);
|
||||
}];
|
||||
previous = btn;
|
||||
}
|
||||
// 最后一个按钮贴右侧
|
||||
[previous mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.leftContainer.mas_right);
|
||||
}];
|
||||
self.leftButtonsInternal = buttons.copy;
|
||||
}
|
||||
|
||||
- (UIButton *)buildActionButtonAtIndex:(NSInteger)idx {
|
||||
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
btn.layer.cornerRadius = 16;
|
||||
btn.layer.masksToBounds = YES;
|
||||
btn.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
|
||||
btn.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||||
NSString *title = (idx < self.leftButtonTitles.count) ? self.leftButtonTitles[idx] : [NSString stringWithFormat:@"Item%ld", (long)(idx+1)];
|
||||
[btn setTitle:title forState:UIControlStateNormal];
|
||||
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
||||
btn.tag = idx;
|
||||
[btn addTarget:self action:@selector(onLeftAction:) forControlEvents:UIControlEventTouchUpInside];
|
||||
return btn;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)onLeftAction:(UIButton *)sender {
|
||||
if ([self.delegate respondsToSelector:@selector(toolBar:didTapActionAtIndex:)]) {
|
||||
[self.delegate toolBar:self didTapActionAtIndex:sender.tag];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onSettings {
|
||||
if ([self.delegate respondsToSelector:@selector(toolBarDidTapSettings:)]) {
|
||||
[self.delegate toolBarDidTapSettings:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Lazy
|
||||
|
||||
- (UIView *)leftContainer {
|
||||
if (!_leftContainer) {
|
||||
_leftContainer = [[UIView alloc] init];
|
||||
_leftContainer.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return _leftContainer;
|
||||
}
|
||||
|
||||
- (UIButton *)settingsButtonInternal {
|
||||
if (!_settingsButtonInternal) {
|
||||
_settingsButtonInternal = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
_settingsButtonInternal.layer.cornerRadius = 16;
|
||||
_settingsButtonInternal.layer.masksToBounds = YES;
|
||||
_settingsButtonInternal.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
|
||||
[_settingsButtonInternal setTitle:@"⚙︎" forState:UIControlStateNormal]; // 简单的齿轮符号
|
||||
[_settingsButtonInternal setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
||||
[_settingsButtonInternal addTarget:self action:@selector(onSettings) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _settingsButtonInternal;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
2
Podfile
2
Podfile
@@ -20,5 +20,5 @@ target 'keyBoard' do
|
||||
pod 'MJExtension', '3.4.2'
|
||||
pod 'MJRefresh', '3.7.9'
|
||||
pod 'SDWebImage', '5.21.1'
|
||||
|
||||
pod 'LookinServer', :configurations => ['Debug']
|
||||
end
|
||||
|
||||
@@ -15,6 +15,9 @@ PODS:
|
||||
- AFNetworking/UIKit (4.0.1):
|
||||
- AFNetworking/NSURLSession
|
||||
- Bugly (2.6.1)
|
||||
- LookinServer (1.2.8):
|
||||
- LookinServer/Core (= 1.2.8)
|
||||
- LookinServer/Core (1.2.8)
|
||||
- Masonry (1.1.0)
|
||||
- MJExtension (3.4.2)
|
||||
- MJRefresh (3.7.9)
|
||||
@@ -25,6 +28,7 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- AFNetworking (= 4.0.1)
|
||||
- Bugly (= 2.6.1)
|
||||
- LookinServer
|
||||
- Masonry (= 1.1.0)
|
||||
- MJExtension (= 3.4.2)
|
||||
- MJRefresh (= 3.7.9)
|
||||
@@ -34,6 +38,7 @@ SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- AFNetworking
|
||||
- Bugly
|
||||
- LookinServer
|
||||
- Masonry
|
||||
- MJExtension
|
||||
- MJRefresh
|
||||
@@ -42,11 +47,12 @@ SPEC REPOS:
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
|
||||
Bugly: 217ac2ce5f0f2626d43dbaa4f70764c953a26a31
|
||||
LookinServer: 1b2b61c6402ae29fa22182d48f5cd067b4e99e80
|
||||
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
|
||||
MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8
|
||||
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
||||
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||
|
||||
PODFILE CHECKSUM: b3c72fe500149c35040cdd73c1d91fe05777bc5f
|
||||
PODFILE CHECKSUM: 4f2fbf9a7c2f24c74f9f26aba9933db3ee43ff84
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
21
Pods/LookinServer/LICENSE
generated
Normal file
21
Pods/LookinServer/LICENSE
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [2023] [LI KAI]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
73
Pods/LookinServer/README.md
generated
Normal file
73
Pods/LookinServer/README.md
generated
Normal file
@@ -0,0 +1,73 @@
|
||||

|
||||
|
||||
# Introduction
|
||||
You can inspect and modify views in iOS app via Lookin, just like UI Inspector in Xcode, or another app called Reveal.
|
||||
|
||||
Official Website:https://lookin.work/
|
||||
|
||||
# Integration Guide
|
||||
To use Lookin macOS app, you need to integrate LookinServer (iOS Framework of Lookin) into your iOS project.
|
||||
|
||||
> **Warning**
|
||||
> 1. Never integrate LookinServer in Release building configuration.
|
||||
> 2. Do not use versions earlier than 1.0.6, as it contains a critical bug that could lead to online incidents in your project: https://qxh1ndiez2w.feishu.cn/wiki/Z9SpwT7zWiqvYvkBe7Lc6Disnab
|
||||
|
||||
## via CocoaPods:
|
||||
### Swift Project
|
||||
`pod 'LookinServer', :subspecs => ['Swift'], :configurations => ['Debug']`
|
||||
### Objective-C Project
|
||||
`pod 'LookinServer', :configurations => ['Debug']`
|
||||
## via Swift Package Manager:
|
||||
`https://github.com/QMUI/LookinServer/`
|
||||
|
||||
# Repository
|
||||
LookinServer: https://github.com/QMUI/LookinServer
|
||||
|
||||
macOS app: https://github.com/hughkli/Lookin/
|
||||
|
||||
# Tips
|
||||
- How to display custom information in Lookin: https://bytedance.larkoffice.com/docx/TRridRXeUoErMTxs94bcnGchnlb
|
||||
- How to display more member variables in Lookin: https://bytedance.larkoffice.com/docx/CKRndHqdeoub11xSqUZcMlFhnWe
|
||||
- How to turn on Swift optimization for Lookin: https://bytedance.larkoffice.com/docx/GFRLdzpeKoakeyxvwgCcZ5XdnTb
|
||||
- Documentation Collection: https://bytedance.larkoffice.com/docx/Yvv1d57XQoe5l0xZ0ZRc0ILfnWb
|
||||
|
||||
# Acknowledgements
|
||||
https://qxh1ndiez2w.feishu.cn/docx/YIFjdE4gIolp3hxn1tGckiBxnWf
|
||||
|
||||
---
|
||||
# 简介
|
||||
Lookin 可以查看与修改 iOS App 里的 UI 对象,类似于 Xcode 自带的 UI Inspector 工具,或另一款叫做 Reveal 的软件。
|
||||
|
||||
官网:https://lookin.work/
|
||||
|
||||
# 安装 LookinServer Framework
|
||||
如果这是你的 iOS 项目第一次使用 Lookin,则需要先把 LookinServer 这款 iOS Framework 集成到你的 iOS 项目中。
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> 1. 不要在 AppStore 模式下集成 LookinServer。
|
||||
> 2. 不要使用早于 1.0.6 的版本,因为它包含一个严重 Bug,可能导致线上事故: https://qxh1ndiez2w.feishu.cn/wiki/Z9SpwT7zWiqvYvkBe7Lc6Disnab
|
||||
## 通过 CocoaPods:
|
||||
|
||||
### Swift 项目
|
||||
`pod 'LookinServer', :subspecs => ['Swift'], :configurations => ['Debug']`
|
||||
### Objective-C 项目
|
||||
`pod 'LookinServer', :configurations => ['Debug']`
|
||||
|
||||
## 通过 Swift Package Manager:
|
||||
`https://github.com/QMUI/LookinServer/`
|
||||
|
||||
# 源代码仓库
|
||||
|
||||
iOS 端 LookinServer:https://github.com/QMUI/LookinServer
|
||||
|
||||
macOS 端软件:https://github.com/hughkli/Lookin/
|
||||
|
||||
# 技巧
|
||||
- 如何在 Lookin 中展示自定义信息: https://bytedance.larkoffice.com/docx/TRridRXeUoErMTxs94bcnGchnlb
|
||||
- 如何在 Lookin 中展示更多成员变量: https://bytedance.larkoffice.com/docx/CKRndHqdeoub11xSqUZcMlFhnWe
|
||||
- 如何为 Lookin 开启 Swift 优化: https://bytedance.larkoffice.com/docx/GFRLdzpeKoakeyxvwgCcZ5XdnTb
|
||||
- 文档汇总:https://bytedance.larkoffice.com/docx/Yvv1d57XQoe5l0xZ0ZRc0ILfnWb
|
||||
|
||||
# 鸣谢
|
||||
https://qxh1ndiez2w.feishu.cn/docx/YIFjdE4gIolp3hxn1tGckiBxnWf
|
||||
40
Pods/LookinServer/Src/Base/LookinIvarTrace.h
generated
Normal file
40
Pods/LookinServer/Src/Base/LookinIvarTrace.h
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinIvarTrace.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/4/30.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString *const LookinIvarTraceRelationValue_Self;
|
||||
|
||||
/// 如果 hostClassName 和 ivarName 均 equal,则认为两个 LookinIvarTrace 对象彼此 equal
|
||||
/// 比如 A 是 B 的 superview,且 A 的 "_stageView" 指向 B,则 B 会有一个 LookinIvarTrace:hostType 为 “superview”,hostClassName 为 A 的 class,ivarName 为 “_stageView”
|
||||
@interface LookinIvarTrace : NSObject <NSSecureCoding, NSCopying>
|
||||
|
||||
/// 该值可能是 "superview"、"superlayer"、“self” 或 nil
|
||||
@property(nonatomic, copy) NSString *relation;
|
||||
|
||||
@property(nonatomic, copy) NSString *hostClassName;
|
||||
|
||||
@property(nonatomic, copy) NSString *ivarName;
|
||||
|
||||
#pragma mark - No Coding
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
@property(nonatomic, weak) id hostObject;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@interface NSObject (LookinServerTrace)
|
||||
|
||||
@property(nonatomic, copy) NSArray<LookinIvarTrace *> *lks_ivarTraces;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
70
Pods/LookinServer/Src/Base/LookinIvarTrace.m
generated
Normal file
70
Pods/LookinServer/Src/Base/LookinIvarTrace.m
generated
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinIvarTrace.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/4/30.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinIvarTrace.h"
|
||||
|
||||
NSString *const LookinIvarTraceRelationValue_Self = @"self";
|
||||
|
||||
@implementation LookinIvarTrace
|
||||
|
||||
#pragma mark - Equal
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return self.hostClassName.hash ^ self.ivarName.hash;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
if (![object isKindOfClass:[LookinIvarTrace class]]) {
|
||||
return NO;
|
||||
}
|
||||
LookinIvarTrace *comparedObj = object;
|
||||
if ([self.hostClassName isEqualToString:comparedObj.hostClassName] && [self.ivarName isEqualToString:comparedObj.ivarName]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - <NSCopying>
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
LookinIvarTrace *newTrace = [[LookinIvarTrace allocWithZone:zone] init];
|
||||
newTrace.relation = self.relation;
|
||||
newTrace.hostClassName = self.hostClassName;
|
||||
newTrace.ivarName = self.ivarName;
|
||||
return newTrace;
|
||||
}
|
||||
|
||||
#pragma mark - <NSCoding>
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:self.relation forKey:@"relation"];
|
||||
[aCoder encodeObject:self.hostClassName forKey:@"hostClassName"];
|
||||
[aCoder encodeObject:self.ivarName forKey:@"ivarName"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
if (self = [super init]) {
|
||||
self.relation = [aDecoder decodeObjectForKey:@"relation"];
|
||||
self.hostClassName = [aDecoder decodeObjectForKey:@"hostClassName"];
|
||||
self.ivarName = [aDecoder decodeObjectForKey:@"ivarName"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
41
Pods/LookinServer/Src/Main/Server/Category/CALayer+LookinServer.h
generated
Normal file
41
Pods/LookinServer/Src/Main/Server/Category/CALayer+LookinServer.h
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIView+LookinMobile.h
|
||||
// WeRead
|
||||
//
|
||||
// Created by Li Kai on 2018/11/30.
|
||||
// Copyright © 2018 tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
#import "TargetConditionals.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CALayer (LookinServer)
|
||||
|
||||
/// 如果 myView.layer == myLayer,则 myLayer.lks_hostView 会返回 myView
|
||||
@property(nonatomic, readonly, weak) UIView *lks_hostView;
|
||||
|
||||
- (UIWindow *)lks_window;
|
||||
|
||||
- (CGRect)lks_frameInWindow:(UIWindow *)window;
|
||||
|
||||
- (UIImage *)lks_groupScreenshotWithLowQuality:(BOOL)lowQuality;
|
||||
/// 当没有 sublayers 时,该方法返回 nil
|
||||
- (UIImage *)lks_soloScreenshotWithLowQuality:(BOOL)lowQuality;
|
||||
|
||||
/// 获取和该对象有关的对象的 Class 层级树
|
||||
- (NSArray<NSArray<NSString *> *> *)lks_relatedClassChainList;
|
||||
|
||||
- (NSArray<NSString *> *)lks_selfRelation;
|
||||
|
||||
@property(nonatomic, strong) UIColor *lks_backgroundColor;
|
||||
@property(nonatomic, strong) UIColor *lks_borderColor;
|
||||
@property(nonatomic, strong) UIColor *lks_shadowColor;
|
||||
@property(nonatomic, assign) CGFloat lks_shadowOffsetWidth;
|
||||
@property(nonatomic, assign) CGFloat lks_shadowOffsetHeight;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
233
Pods/LookinServer/Src/Main/Server/Category/CALayer+LookinServer.m
generated
Normal file
233
Pods/LookinServer/Src/Main/Server/Category/CALayer+LookinServer.m
generated
Normal file
@@ -0,0 +1,233 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIView+LookinMobile.m
|
||||
// WeRead
|
||||
//
|
||||
// Created by Li Kai on 2018/11/30.
|
||||
// Copyright © 2018 tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CALayer+LookinServer.h"
|
||||
#import "LKS_HierarchyDisplayItemsMaker.h"
|
||||
#import "LookinDisplayItem.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "LookinIvarTrace.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
@implementation CALayer (LookinServer)
|
||||
|
||||
- (UIWindow *)lks_window {
|
||||
CALayer *layer = self;
|
||||
while (layer) {
|
||||
UIView *hostView = layer.lks_hostView;
|
||||
if (hostView.window) {
|
||||
return hostView.window;
|
||||
} else if ([hostView isKindOfClass:[UIWindow class]]) {
|
||||
return (UIWindow *)hostView;
|
||||
}
|
||||
layer = layer.superlayer;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGRect)lks_frameInWindow:(UIWindow *)window {
|
||||
UIWindow *selfWindow = [self lks_window];
|
||||
if (!selfWindow) {
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
CGRect rectInSelfWindow = [selfWindow.layer convertRect:self.frame fromLayer:self.superlayer];
|
||||
CGRect rectInWindow = [window convertRect:rectInSelfWindow fromWindow:selfWindow];
|
||||
return rectInWindow;
|
||||
}
|
||||
|
||||
#pragma mark - Host View
|
||||
|
||||
- (UIView *)lks_hostView {
|
||||
if (self.delegate && [self.delegate isKindOfClass:UIView.class]) {
|
||||
UIView *view = (UIView *)self.delegate;
|
||||
if (view.layer == self) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Screenshot
|
||||
|
||||
- (UIImage *)lks_groupScreenshotWithLowQuality:(BOOL)lowQuality {
|
||||
|
||||
CGFloat screenScale = [LKS_MultiplatformAdapter mainScreenScale];
|
||||
CGFloat pixelWidth = self.frame.size.width * screenScale;
|
||||
CGFloat pixelHeight = self.frame.size.height * screenScale;
|
||||
if (pixelWidth <= 0 || pixelHeight <= 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGFloat renderScale = lowQuality ? 1 : 0;
|
||||
CGFloat maxLength = MAX(pixelWidth, pixelHeight);
|
||||
if (maxLength > LookinNodeImageMaxLengthInPx) {
|
||||
// 确保最终绘制出的图片长和宽都不能超过 LookinNodeImageMaxLengthInPx
|
||||
// 如果算出的 renderScale 大于 1 则取 1,因为似乎用 1 渲染的速度要比一个别的奇怪的带小数点的数字要更快
|
||||
renderScale = MIN(screenScale * LookinNodeImageMaxLengthInPx / maxLength, 1);
|
||||
}
|
||||
|
||||
CGSize contextSize = self.frame.size;
|
||||
if (contextSize.width <= 0 || contextSize.height <= 0 || contextSize.width > 20000 || contextSize.height > 20000) {
|
||||
NSLog(@"LookinServer - Failed to capture screenshot. Invalid context size: %@ x %@", @(contextSize.width), @(contextSize.height));
|
||||
return nil;
|
||||
}
|
||||
UIGraphicsBeginImageContextWithOptions(contextSize, NO, renderScale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (self.lks_hostView && !self.lks_hostView.lks_isChildrenViewOfTabBar) {
|
||||
[self.lks_hostView drawViewHierarchyInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) afterScreenUpdates:YES];
|
||||
} else {
|
||||
[self renderInContext:context];
|
||||
}
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)lks_soloScreenshotWithLowQuality:(BOOL)lowQuality {
|
||||
if (!self.sublayers.count) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGFloat screenScale = [LKS_MultiplatformAdapter mainScreenScale];
|
||||
CGFloat pixelWidth = self.frame.size.width * screenScale;
|
||||
CGFloat pixelHeight = self.frame.size.height * screenScale;
|
||||
if (pixelWidth <= 0 || pixelHeight <= 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGFloat renderScale = lowQuality ? 1 : 0;
|
||||
CGFloat maxLength = MAX(pixelWidth, pixelHeight);
|
||||
if (maxLength > LookinNodeImageMaxLengthInPx) {
|
||||
// 确保最终绘制出的图片长和宽都不能超过 LookinNodeImageMaxLengthInPx
|
||||
// 如果算出的 renderScale 大于 1 则取 1,因为似乎用 1 渲染的速度要比一个别的奇怪的带小数点的数字要更快
|
||||
renderScale = MIN(screenScale * LookinNodeImageMaxLengthInPx / maxLength, 1);
|
||||
}
|
||||
|
||||
if (self.sublayers.count) {
|
||||
NSArray<CALayer *> *sublayers = [self.sublayers copy];
|
||||
NSMutableArray<CALayer *> *visibleSublayers = [NSMutableArray arrayWithCapacity:sublayers.count];
|
||||
[sublayers enumerateObjectsUsingBlock:^(__kindof CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (!sublayer.hidden) {
|
||||
sublayer.hidden = YES;
|
||||
[visibleSublayers addObject:sublayer];
|
||||
}
|
||||
}];
|
||||
|
||||
CGSize contextSize = self.frame.size;
|
||||
if (contextSize.width <= 0 || contextSize.height <= 0 || contextSize.width > 20000 || contextSize.height > 20000) {
|
||||
NSLog(@"LookinServer - Failed to capture screenshot. Invalid context size: %@ x %@", @(contextSize.width), @(contextSize.height));
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(contextSize, NO, renderScale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (self.lks_hostView && !self.lks_hostView.lks_isChildrenViewOfTabBar) {
|
||||
[self.lks_hostView drawViewHierarchyInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) afterScreenUpdates:YES];
|
||||
} else {
|
||||
[self renderInContext:context];
|
||||
}
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
[visibleSublayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
sublayer.hidden = NO;
|
||||
}];
|
||||
|
||||
return image;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray<NSArray<NSString *> *> *)lks_relatedClassChainList {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:2];
|
||||
if (self.lks_hostView) {
|
||||
[array addObject:[CALayer lks_getClassListOfObject:self.lks_hostView endingClass:@"UIView"]];
|
||||
UIViewController* vc = [self.lks_hostView lks_findHostViewController];
|
||||
if (vc) {
|
||||
[array addObject:[CALayer lks_getClassListOfObject:vc endingClass:@"UIViewController"]];
|
||||
}
|
||||
} else {
|
||||
[array addObject:[CALayer lks_getClassListOfObject:self endingClass:@"CALayer"]];
|
||||
}
|
||||
return array.copy;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)lks_getClassListOfObject:(id)object endingClass:(NSString *)endingClass {
|
||||
NSArray<NSString *> *completedList = [object lks_classChainList];
|
||||
NSUInteger endingIdx = [completedList indexOfObject:endingClass];
|
||||
if (endingIdx != NSNotFound) {
|
||||
completedList = [completedList subarrayWithRange:NSMakeRange(0, endingIdx + 1)];
|
||||
}
|
||||
return completedList;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)lks_selfRelation {
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
NSMutableArray<LookinIvarTrace *> *ivarTraces = [NSMutableArray array];
|
||||
if (self.lks_hostView) {
|
||||
UIViewController* vc = [self.lks_hostView lks_findHostViewController];
|
||||
if (vc) {
|
||||
[array addObject:[NSString stringWithFormat:@"(%@ *).view", NSStringFromClass(vc.class)]];
|
||||
|
||||
[ivarTraces addObjectsFromArray:vc.lks_ivarTraces];
|
||||
}
|
||||
[ivarTraces addObjectsFromArray:self.lks_hostView.lks_ivarTraces];
|
||||
} else {
|
||||
[ivarTraces addObjectsFromArray:self.lks_ivarTraces];
|
||||
}
|
||||
if (ivarTraces.count) {
|
||||
[array addObjectsFromArray:[ivarTraces lookin_map:^id(NSUInteger idx, LookinIvarTrace *value) {
|
||||
return [NSString stringWithFormat:@"(%@ *) -> %@", value.hostClassName, value.ivarName];
|
||||
}]];
|
||||
}
|
||||
return array.count ? array.copy : nil;
|
||||
}
|
||||
|
||||
- (UIColor *)lks_backgroundColor {
|
||||
return [UIColor lks_colorWithCGColor:self.backgroundColor];
|
||||
}
|
||||
- (void)setLks_backgroundColor:(UIColor *)lks_backgroundColor {
|
||||
self.backgroundColor = lks_backgroundColor.CGColor;
|
||||
}
|
||||
|
||||
- (UIColor *)lks_borderColor {
|
||||
return [UIColor lks_colorWithCGColor:self.borderColor];
|
||||
}
|
||||
- (void)setLks_borderColor:(UIColor *)lks_borderColor {
|
||||
self.borderColor = lks_borderColor.CGColor;
|
||||
}
|
||||
|
||||
- (UIColor *)lks_shadowColor {
|
||||
return [UIColor lks_colorWithCGColor:self.shadowColor];
|
||||
}
|
||||
- (void)setLks_shadowColor:(UIColor *)lks_shadowColor {
|
||||
self.shadowColor = lks_shadowColor.CGColor;
|
||||
}
|
||||
|
||||
- (CGFloat)lks_shadowOffsetWidth {
|
||||
return self.shadowOffset.width;
|
||||
}
|
||||
- (void)setLks_shadowOffsetWidth:(CGFloat)lks_shadowOffsetWidth {
|
||||
self.shadowOffset = CGSizeMake(lks_shadowOffsetWidth, self.shadowOffset.height);
|
||||
}
|
||||
|
||||
- (CGFloat)lks_shadowOffsetHeight {
|
||||
return self.shadowOffset.height;
|
||||
}
|
||||
- (void)setLks_shadowOffsetHeight:(CGFloat)lks_shadowOffsetHeight {
|
||||
self.shadowOffset = CGSizeMake(self.shadowOffset.width, lks_shadowOffsetHeight);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
41
Pods/LookinServer/Src/Main/Server/Category/NSObject+LookinServer.h
generated
Normal file
41
Pods/LookinServer/Src/Main/Server/Category/NSObject+LookinServer.h
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSObject+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/4/21.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinIvarTrace;
|
||||
|
||||
@interface NSObject (LookinServer)
|
||||
|
||||
#pragma mark - oid
|
||||
|
||||
/// 如果 oid 不存在则会创建新的 oid
|
||||
- (unsigned long)lks_registerOid;
|
||||
|
||||
/// 0 表示不存在
|
||||
@property(nonatomic, assign) unsigned long lks_oid;
|
||||
|
||||
+ (NSObject *)lks_objectWithOid:(unsigned long)oid;
|
||||
|
||||
#pragma mark - trace
|
||||
|
||||
@property(nonatomic, copy) NSString *lks_specialTrace;
|
||||
|
||||
+ (void)lks_clearAllObjectsTraces;
|
||||
|
||||
/**
|
||||
获取当前对象的 Class 层级树,如 @[@"UIView", @"UIResponder", @"NSObject"]。未 demangle,有 Swift Module Name
|
||||
*/
|
||||
- (NSArray<NSString *> *)lks_classChainList;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
99
Pods/LookinServer/Src/Main/Server/Category/NSObject+LookinServer.m
generated
Normal file
99
Pods/LookinServer/Src/Main/Server/Category/NSObject+LookinServer.m
generated
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSObject+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/4/21.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "NSObject+Lookin.h"
|
||||
#import "NSObject+LookinServer.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_ObjectRegistry.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation NSObject (LookinServer)
|
||||
|
||||
#pragma mark - oid
|
||||
|
||||
- (unsigned long)lks_registerOid {
|
||||
if (!self.lks_oid) {
|
||||
unsigned long oid = [[LKS_ObjectRegistry sharedInstance] addObject:self];
|
||||
self.lks_oid = oid;
|
||||
}
|
||||
return self.lks_oid;
|
||||
}
|
||||
|
||||
- (void)setLks_oid:(unsigned long)lks_oid {
|
||||
[self lookin_bindObject:@(lks_oid) forKey:@"lks_oid"];
|
||||
}
|
||||
|
||||
- (unsigned long)lks_oid {
|
||||
NSNumber *number = [self lookin_getBindObjectForKey:@"lks_oid"];
|
||||
return [number unsignedLongValue];
|
||||
}
|
||||
|
||||
+ (NSObject *)lks_objectWithOid:(unsigned long)oid {
|
||||
return [[LKS_ObjectRegistry sharedInstance] objectWithOid:oid];
|
||||
}
|
||||
|
||||
#pragma mark - trace
|
||||
|
||||
- (void)setLks_ivarTraces:(NSArray<LookinIvarTrace *> *)lks_ivarTraces {
|
||||
[self lookin_bindObject:lks_ivarTraces.copy forKey:@"lks_ivarTraces"];
|
||||
|
||||
if (lks_ivarTraces) {
|
||||
[[NSObject lks_allObjectsWithTraces] addPointer:(void *)self];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<LookinIvarTrace *> *)lks_ivarTraces {
|
||||
return [self lookin_getBindObjectForKey:@"lks_ivarTraces"];
|
||||
}
|
||||
|
||||
- (void)setLks_specialTrace:(NSString *)lks_specialTrace {
|
||||
[self lookin_bindObject:lks_specialTrace forKey:@"lks_specialTrace"];
|
||||
if (lks_specialTrace) {
|
||||
[[NSObject lks_allObjectsWithTraces] addPointer:(void *)self];
|
||||
}
|
||||
}
|
||||
- (NSString *)lks_specialTrace {
|
||||
return [self lookin_getBindObjectForKey:@"lks_specialTrace"];
|
||||
}
|
||||
|
||||
+ (void)lks_clearAllObjectsTraces {
|
||||
[[[NSObject lks_allObjectsWithTraces] allObjects] enumerateObjectsUsingBlock:^(NSObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
obj.lks_ivarTraces = nil;
|
||||
obj.lks_specialTrace = nil;
|
||||
}];
|
||||
[NSObject lks_allObjectsWithTraces].count = 0;
|
||||
}
|
||||
|
||||
+ (NSPointerArray *)lks_allObjectsWithTraces {
|
||||
static dispatch_once_t onceToken;
|
||||
static NSPointerArray *lks_allObjectsWithTraces = nil;
|
||||
dispatch_once(&onceToken,^{
|
||||
lks_allObjectsWithTraces = [NSPointerArray weakObjectsPointerArray];
|
||||
});
|
||||
return lks_allObjectsWithTraces;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)lks_classChainList {
|
||||
NSMutableArray<NSString *> *classChainList = [NSMutableArray array];
|
||||
Class currentClass = self.class;
|
||||
|
||||
while (currentClass) {
|
||||
NSString *currentClassName = NSStringFromClass(currentClass);
|
||||
if (currentClassName) {
|
||||
[classChainList addObject:currentClassName];
|
||||
}
|
||||
currentClass = [currentClass superclass];
|
||||
}
|
||||
return classChainList.copy;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Category/UIBlurEffect+LookinServer.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Category/UIBlurEffect+LookinServer.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIBlurEffect+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/10/8.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIBlurEffect (LookinServer)
|
||||
|
||||
/// 该 number 包装的对象是 UIBlurEffectStyle,之所以用 NSNumber 是因为想把 0 和 nil 区分开,毕竟这里是在 hook 系统,稳一点好。
|
||||
/// 该方法的实现需要 Hook,因此若定义了 LOOKIN_SERVER_DISABLE_HOOK 宏,则属性会返回 nil
|
||||
@property(nonatomic, strong) NSNumber *lks_effectStyleNumber;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
57
Pods/LookinServer/Src/Main/Server/Category/UIBlurEffect+LookinServer.m
generated
Normal file
57
Pods/LookinServer/Src/Main/Server/Category/UIBlurEffect+LookinServer.m
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIBlurEffect+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/10/8.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UIBlurEffect+LookinServer.h"
|
||||
#import "NSObject+Lookin.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIBlurEffect (LookinServer)
|
||||
|
||||
#ifdef LOOKIN_SERVER_DISABLE_HOOK
|
||||
|
||||
- (void)setLks_effectStyleNumber:(NSNumber *)lks_effectStyleNumber {
|
||||
}
|
||||
|
||||
- (NSNumber *)lks_effectStyleNumber {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
+ (void)load {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Method oriMethod = class_getClassMethod([self class], @selector(effectWithStyle:));
|
||||
Method newMethod = class_getClassMethod([self class], @selector(lks_effectWithStyle:));
|
||||
method_exchangeImplementations(oriMethod, newMethod);
|
||||
});
|
||||
}
|
||||
|
||||
+ (UIBlurEffect *)lks_effectWithStyle:(UIBlurEffectStyle)style {
|
||||
id effect = [self lks_effectWithStyle:style];
|
||||
if ([effect respondsToSelector:@selector(setLks_effectStyleNumber:)]) {
|
||||
[effect setLks_effectStyleNumber:@(style)];
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
- (void)setLks_effectStyleNumber:(NSNumber *)lks_effectStyleNumber {
|
||||
[self lookin_bindObject:lks_effectStyleNumber forKey:@"lks_effectStyleNumber"];
|
||||
}
|
||||
|
||||
- (NSNumber *)lks_effectStyleNumber {
|
||||
return [self lookin_getBindObjectForKey:@"lks_effectStyleNumber"];
|
||||
}
|
||||
|
||||
#endif /* LOOKIN_SERVER_DISABLE_HOOK */
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
26
Pods/LookinServer/Src/Main/Server/Category/UIColor+LookinServer.h
generated
Normal file
26
Pods/LookinServer/Src/Main/Server/Category/UIColor+LookinServer.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIColor+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIColor (LookinServer)
|
||||
|
||||
- (NSArray<NSNumber *> *)lks_rgbaComponents;
|
||||
+ (instancetype)lks_colorFromRGBAComponents:(NSArray<NSNumber *> *)components;
|
||||
|
||||
- (NSString *)lks_rgbaString;
|
||||
- (NSString *)lks_hexString;
|
||||
|
||||
/// will check if the argument is a real CGColor
|
||||
+ (UIColor *)lks_colorWithCGColor:(CGColorRef)cgColor;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
183
Pods/LookinServer/Src/Main/Server/Category/UIColor+LookinServer.m
generated
Normal file
183
Pods/LookinServer/Src/Main/Server/Category/UIColor+LookinServer.m
generated
Normal file
@@ -0,0 +1,183 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIColor+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UIColor+LookinServer.h"
|
||||
|
||||
@implementation UIColor (LookinServer)
|
||||
|
||||
- (NSArray<NSNumber *> *)lks_rgbaComponents {
|
||||
CGFloat r, g, b, a;
|
||||
CGColorRef cgColor = [self CGColor];
|
||||
const CGFloat *components = CGColorGetComponents(cgColor);
|
||||
if (CGColorGetNumberOfComponents(cgColor) == 4) {
|
||||
r = components[0];
|
||||
g = components[1];
|
||||
b = components[2];
|
||||
a = components[3];
|
||||
} else if (CGColorGetNumberOfComponents(cgColor) == 2) {
|
||||
r = components[0];
|
||||
g = components[0];
|
||||
b = components[0];
|
||||
a = components[1];
|
||||
} else if (CGColorGetNumberOfComponents(cgColor) == 1) {
|
||||
r = components[0];
|
||||
g = components[0];
|
||||
b = components[0];
|
||||
a = components[0];
|
||||
} else {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
NSArray<NSNumber *> *rgba = @[@(r), @(g), @(b), @(a)];
|
||||
return rgba;
|
||||
}
|
||||
|
||||
+ (instancetype)lks_colorFromRGBAComponents:(NSArray<NSNumber *> *)components {
|
||||
if (!components) {
|
||||
return nil;
|
||||
}
|
||||
if (components.count != 4) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
UIColor *color = [UIColor colorWithRed:components[0].doubleValue green:components[1].doubleValue blue:components[2].doubleValue alpha:components[3].doubleValue];
|
||||
return color;
|
||||
}
|
||||
|
||||
- (NSString *)lks_rgbaString {
|
||||
CGFloat r, g, b, a;
|
||||
CGColorRef cgColor = [self CGColor];
|
||||
const CGFloat *components = CGColorGetComponents(cgColor);
|
||||
if (CGColorGetNumberOfComponents(cgColor) == 4) {
|
||||
r = components[0];
|
||||
g = components[1];
|
||||
b = components[2];
|
||||
a = components[3];
|
||||
} else if (CGColorGetNumberOfComponents(cgColor) == 2) {
|
||||
r = components[0];
|
||||
g = components[0];
|
||||
b = components[0];
|
||||
a = components[1];
|
||||
} else {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
|
||||
if (a >= 1) {
|
||||
return [NSString stringWithFormat:@"(%.0f, %.0f, %.0f)", r * 255, g * 255, b * 255];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"(%.0f, %.0f, %.0f, %.2f)", r * 255, g * 255, b * 255, a];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)lks_hexString {
|
||||
CGFloat r, g, b, a;
|
||||
CGColorRef cgColor = [self CGColor];
|
||||
const CGFloat *components = CGColorGetComponents(cgColor);
|
||||
if (CGColorGetNumberOfComponents(cgColor) == 4) {
|
||||
r = components[0];
|
||||
g = components[1];
|
||||
b = components[2];
|
||||
a = components[3];
|
||||
} else if (CGColorGetNumberOfComponents(cgColor) == 2) {
|
||||
r = components[0];
|
||||
g = components[0];
|
||||
b = components[0];
|
||||
a = components[1];
|
||||
} else {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
|
||||
NSInteger red = r * 255;
|
||||
NSInteger green = g * 255;
|
||||
NSInteger blue = b * 255;
|
||||
NSInteger alpha = a * 255;
|
||||
|
||||
return [[NSString stringWithFormat:@"#%@%@%@%@",
|
||||
[UIColor _alignColorHexStringLength:[UIColor _hexStringWithInteger:alpha]],
|
||||
[UIColor _alignColorHexStringLength:[UIColor _hexStringWithInteger:red]],
|
||||
[UIColor _alignColorHexStringLength:[UIColor _hexStringWithInteger:green]],
|
||||
[UIColor _alignColorHexStringLength:[UIColor _hexStringWithInteger:blue]]] lowercaseString];
|
||||
}
|
||||
|
||||
// 对于色值只有单位数的,在前面补一个0,例如“F”会补齐为“0F”
|
||||
+ (NSString *)_alignColorHexStringLength:(NSString *)hexString {
|
||||
return hexString.length < 2 ? [@"0" stringByAppendingString:hexString] : hexString;
|
||||
}
|
||||
|
||||
+ (NSString *)_hexStringWithInteger:(NSInteger)integer {
|
||||
NSString *hexString = @"";
|
||||
NSInteger remainder = 0;
|
||||
for (NSInteger i = 0; i < 9; i++) {
|
||||
remainder = integer % 16;
|
||||
integer = integer / 16;
|
||||
NSString *letter = [self _hexLetterStringWithInteger:remainder];
|
||||
hexString = [letter stringByAppendingString:hexString];
|
||||
if (integer == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
+ (NSString *)_hexLetterStringWithInteger:(NSInteger)integer {
|
||||
NSAssert(integer < 16, @"要转换的数必须是16进制里的个位数,也即小于16,但你传给我是%@", @(integer));
|
||||
|
||||
NSString *letter = nil;
|
||||
switch (integer) {
|
||||
case 10:
|
||||
letter = @"A";
|
||||
break;
|
||||
case 11:
|
||||
letter = @"B";
|
||||
break;
|
||||
case 12:
|
||||
letter = @"C";
|
||||
break;
|
||||
case 13:
|
||||
letter = @"D";
|
||||
break;
|
||||
case 14:
|
||||
letter = @"E";
|
||||
break;
|
||||
case 15:
|
||||
letter = @"F";
|
||||
break;
|
||||
default:
|
||||
letter = [[NSString alloc]initWithFormat:@"%@", @(integer)];
|
||||
break;
|
||||
}
|
||||
return letter;
|
||||
}
|
||||
|
||||
+ (UIColor *)lks_colorWithCGColor:(CGColorRef)cgColor {
|
||||
if (!cgColor) {
|
||||
return nil;
|
||||
}
|
||||
if (CFGetTypeID(cgColor) != CGColorGetTypeID()) {
|
||||
return nil;
|
||||
}
|
||||
return [UIColor colorWithCGColor:cgColor];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
22
Pods/LookinServer/Src/Main/Server/Category/UIImage+LookinServer.h
generated
Normal file
22
Pods/LookinServer/Src/Main/Server/Category/UIImage+LookinServer.h
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIImage+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/5/14.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (LookinServer)
|
||||
|
||||
/// 该方法的实现需要 Hook,因此若定义了 LOOKIN_SERVER_DISABLE_HOOK 宏,则属性会返回 nil
|
||||
@property(nonatomic, copy) NSString *lks_imageSourceName;
|
||||
|
||||
- (NSData *)lookin_data;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
95
Pods/LookinServer/Src/Main/Server/Category/UIImage+LookinServer.m
generated
Normal file
95
Pods/LookinServer/Src/Main/Server/Category/UIImage+LookinServer.m
generated
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIImage+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/5/14.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import "UIImage+LookinServer.h"
|
||||
#import "LookinServerDefines.h"
|
||||
|
||||
@implementation UIImage (LookinServer)
|
||||
|
||||
#ifdef LOOKIN_SERVER_DISABLE_HOOK
|
||||
|
||||
- (void)setLks_imageSourceName:(NSString *)lks_imageSourceName {
|
||||
}
|
||||
|
||||
- (NSString *)lks_imageSourceName {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
+ (void)load {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Method oriMethod = class_getClassMethod([self class], @selector(imageNamed:));
|
||||
Method newMethod = class_getClassMethod([self class], @selector(lks_imageNamed:));
|
||||
method_exchangeImplementations(oriMethod, newMethod);
|
||||
|
||||
oriMethod = class_getClassMethod([self class], @selector(imageWithContentsOfFile:));
|
||||
newMethod = class_getClassMethod([self class], @selector(lks_imageWithContentsOfFile:));
|
||||
method_exchangeImplementations(oriMethod, newMethod);
|
||||
|
||||
oriMethod = class_getClassMethod([self class], @selector(imageNamed:inBundle:compatibleWithTraitCollection:));
|
||||
newMethod = class_getClassMethod([self class], @selector(lks_imageNamed:inBundle:compatibleWithTraitCollection:));
|
||||
method_exchangeImplementations(oriMethod, newMethod);
|
||||
|
||||
if (@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)) {
|
||||
oriMethod = class_getClassMethod([self class], @selector(imageNamed:inBundle:withConfiguration:));
|
||||
newMethod = class_getClassMethod([self class], @selector(lks_imageNamed:inBundle:withConfiguration:));
|
||||
method_exchangeImplementations(oriMethod, newMethod);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (nullable UIImage *)lks_imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle withConfiguration:(nullable UIImageConfiguration *)configuration API_AVAILABLE(ios(13.0),tvos(13.0),watchos(6.0))
|
||||
{
|
||||
UIImage *image = [self lks_imageNamed:name inBundle:bundle withConfiguration:configuration];
|
||||
image.lks_imageSourceName = name;
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (nullable UIImage *)lks_imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection API_AVAILABLE(ios(8.0))
|
||||
{
|
||||
UIImage *image = [self lks_imageNamed:name inBundle:bundle compatibleWithTraitCollection:traitCollection];
|
||||
image.lks_imageSourceName = name;
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (UIImage *)lks_imageNamed:(NSString *)name {
|
||||
UIImage *image = [self lks_imageNamed:name];
|
||||
image.lks_imageSourceName = name;
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (UIImage *)lks_imageWithContentsOfFile:(NSString *)path {
|
||||
UIImage *image = [self lks_imageWithContentsOfFile:path];
|
||||
|
||||
NSString *fileName = [[path componentsSeparatedByString:@"/"].lastObject componentsSeparatedByString:@"."].firstObject;
|
||||
image.lks_imageSourceName = fileName;
|
||||
return image;
|
||||
}
|
||||
|
||||
- (void)setLks_imageSourceName:(NSString *)lks_imageSourceName {
|
||||
[self lookin_bindObject:lks_imageSourceName.copy forKey:@"lks_imageSourceName"];
|
||||
}
|
||||
|
||||
- (NSString *)lks_imageSourceName {
|
||||
return [self lookin_getBindObjectForKey:@"lks_imageSourceName"];
|
||||
}
|
||||
|
||||
#endif /* LOOKIN_SERVER_DISABLE_HOOK */
|
||||
|
||||
- (NSData *)lookin_data {
|
||||
return UIImagePNGRepresentation(self);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
20
Pods/LookinServer/Src/Main/Server/Category/UIImageView+LookinServer.h
generated
Normal file
20
Pods/LookinServer/Src/Main/Server/Category/UIImageView+LookinServer.h
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIImageView+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/9/18.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImageView (LookinServer)
|
||||
|
||||
- (NSString *)lks_imageSourceName;
|
||||
- (NSNumber *)lks_imageViewOidIfHasImage;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
31
Pods/LookinServer/Src/Main/Server/Category/UIImageView+LookinServer.m
generated
Normal file
31
Pods/LookinServer/Src/Main/Server/Category/UIImageView+LookinServer.m
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIImageView+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/9/18.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UIImageView+LookinServer.h"
|
||||
#import "UIImage+LookinServer.h"
|
||||
#import "NSObject+LookinServer.h"
|
||||
|
||||
@implementation UIImageView (LookinServer)
|
||||
|
||||
- (NSString *)lks_imageSourceName {
|
||||
return self.image.lks_imageSourceName;
|
||||
}
|
||||
|
||||
- (NSNumber *)lks_imageViewOidIfHasImage {
|
||||
if (!self.image) {
|
||||
return nil;
|
||||
}
|
||||
unsigned long oid = [self lks_registerOid];
|
||||
return @(oid);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Category/UILabel+LookinServer.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Category/UILabel+LookinServer.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UILabel+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/26.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UILabel (LookinServer)
|
||||
|
||||
@property(nonatomic, assign) CGFloat lks_fontSize;
|
||||
|
||||
- (NSString *)lks_fontName;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
29
Pods/LookinServer/Src/Main/Server/Category/UILabel+LookinServer.m
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Category/UILabel+LookinServer.m
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UILabel+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/26.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UILabel+LookinServer.h"
|
||||
|
||||
@implementation UILabel (LookinServer)
|
||||
|
||||
- (CGFloat)lks_fontSize {
|
||||
return self.font.pointSize;
|
||||
}
|
||||
- (void)setLks_fontSize:(CGFloat)lks_fontSize {
|
||||
UIFont *font = [self.font fontWithSize:lks_fontSize];
|
||||
self.font = font;
|
||||
}
|
||||
|
||||
- (NSString *)lks_fontName {
|
||||
return self.font.fontName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
19
Pods/LookinServer/Src/Main/Server/Category/UITableView+LookinServer.h
generated
Normal file
19
Pods/LookinServer/Src/Main/Server/Category/UITableView+LookinServer.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UITableView+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/9/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UITableView (LookinServer)
|
||||
|
||||
- (NSArray<NSNumber *> *)lks_numberOfRows;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
29
Pods/LookinServer/Src/Main/Server/Category/UITableView+LookinServer.m
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Category/UITableView+LookinServer.m
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UITableView+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/9/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UITableView+LookinServer.h"
|
||||
#import "LookinServerDefines.h"
|
||||
|
||||
@implementation UITableView (LookinServer)
|
||||
|
||||
- (NSArray<NSNumber *> *)lks_numberOfRows {
|
||||
NSUInteger sectionsCount = MIN(self.numberOfSections, 10);
|
||||
NSArray<NSNumber *> *rowsCount = [NSArray lookin_arrayWithCount:sectionsCount block:^id(NSUInteger idx) {
|
||||
return @([self numberOfRowsInSection:idx]);
|
||||
}];
|
||||
if (rowsCount.count == 0) {
|
||||
return nil;
|
||||
}
|
||||
return rowsCount;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Category/UITextField+LookinServer.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Category/UITextField+LookinServer.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UITextField+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/26.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UITextField (LookinServer)
|
||||
|
||||
@property(nonatomic, assign) CGFloat lks_fontSize;
|
||||
|
||||
- (NSString *)lks_fontName;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
29
Pods/LookinServer/Src/Main/Server/Category/UITextField+LookinServer.m
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Category/UITextField+LookinServer.m
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UITextField+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/26.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UITextField+LookinServer.h"
|
||||
|
||||
@implementation UITextField (LookinServer)
|
||||
|
||||
- (CGFloat)lks_fontSize {
|
||||
return self.font.pointSize;
|
||||
}
|
||||
- (void)setLks_fontSize:(CGFloat)lks_fontSize {
|
||||
UIFont *font = [self.font fontWithSize:lks_fontSize];
|
||||
self.font = font;
|
||||
}
|
||||
|
||||
- (NSString *)lks_fontName {
|
||||
return self.font.fontName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Category/UITextView+LookinServer.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Category/UITextView+LookinServer.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UITextView+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/26.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UITextView (LookinServer)
|
||||
|
||||
@property(nonatomic, assign) CGFloat lks_fontSize;
|
||||
|
||||
- (NSString *)lks_fontName;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
29
Pods/LookinServer/Src/Main/Server/Category/UITextView+LookinServer.m
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Category/UITextView+LookinServer.m
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UITextView+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/2/26.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UITextView+LookinServer.h"
|
||||
|
||||
@implementation UITextView (LookinServer)
|
||||
|
||||
- (CGFloat)lks_fontSize {
|
||||
return self.font.pointSize;
|
||||
}
|
||||
- (void)setLks_fontSize:(CGFloat)lks_fontSize {
|
||||
UIFont *font = [self.font fontWithSize:lks_fontSize];
|
||||
self.font = font;
|
||||
}
|
||||
|
||||
- (NSString *)lks_fontName {
|
||||
return self.font.fontName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
44
Pods/LookinServer/Src/Main/Server/Category/UIView+LookinServer.h
generated
Normal file
44
Pods/LookinServer/Src/Main/Server/Category/UIView+LookinServer.h
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIView+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/3/19.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIView (LookinServer)
|
||||
|
||||
/// 如果 myViewController.view = myView,则可以通过 myView 的 lks_findHostViewController 方法找到 myViewController
|
||||
- (UIViewController *)lks_findHostViewController;
|
||||
|
||||
/// 是否是 UITabBar 的 childrenView,如果是的话,则截图时需要强制使用 renderInContext: 的方式而非 drawViewHierarchyInRect:afterScreenUpdates: 否则在 iOS 13 上获取到的图像是空的不知道为什么
|
||||
@property(nonatomic, assign) BOOL lks_isChildrenViewOfTabBar;
|
||||
|
||||
/// point 是相对于 receiver 自身的坐标系
|
||||
- (UIView *)lks_subviewAtPoint:(CGPoint)point preferredClasses:(NSArray<Class> *)preferredClasses;
|
||||
|
||||
- (CGFloat)lks_bestWidth;
|
||||
- (CGFloat)lks_bestHeight;
|
||||
- (CGSize)lks_bestSize;
|
||||
|
||||
@property(nonatomic, assign) float lks_horizontalContentHuggingPriority;
|
||||
@property(nonatomic, assign) float lks_verticalContentHuggingPriority;
|
||||
|
||||
@property(nonatomic, assign) float lks_horizontalContentCompressionResistancePriority;
|
||||
@property(nonatomic, assign) float lks_verticalContentCompressionResistancePriority;
|
||||
|
||||
/// 遍历全局的 view 并给他们设置 lks_involvedRawConstraints 属性
|
||||
+ (void)lks_rebuildGlobalInvolvedRawConstraints;
|
||||
/// 该属性保存了牵扯到当前 view 的所有 constraints,包括那些没有生效的
|
||||
@property(nonatomic, strong) NSMutableArray<NSLayoutConstraint *> *lks_involvedRawConstraints;
|
||||
|
||||
- (NSArray<NSDictionary<NSString *, id> *> *)lks_constraints;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
215
Pods/LookinServer/Src/Main/Server/Category/UIView+LookinServer.m
generated
Normal file
215
Pods/LookinServer/Src/Main/Server/Category/UIView+LookinServer.m
generated
Normal file
@@ -0,0 +1,215 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIView+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/3/19.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UIView+LookinServer.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "LookinObject.h"
|
||||
#import "LookinAutoLayoutConstraint.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
@implementation UIView (LookinServer)
|
||||
|
||||
- (UIViewController *)lks_findHostViewController {
|
||||
UIResponder *responder = [self nextResponder];
|
||||
if (!responder) {
|
||||
return nil;
|
||||
}
|
||||
if (![responder isKindOfClass:[UIViewController class]]) {
|
||||
return nil;
|
||||
}
|
||||
UIViewController *viewController = (UIViewController *)responder;
|
||||
if (viewController.view != self) {
|
||||
return nil;
|
||||
}
|
||||
return viewController;
|
||||
}
|
||||
|
||||
- (UIView *)lks_subviewAtPoint:(CGPoint)point preferredClasses:(NSArray<Class> *)preferredClasses {
|
||||
BOOL isPreferredClassForSelf = [preferredClasses lookin_any:^BOOL(Class obj) {
|
||||
return [self isKindOfClass:obj];
|
||||
}];
|
||||
if (isPreferredClassForSelf) {
|
||||
return self;
|
||||
}
|
||||
|
||||
UIView *targetView = [self.subviews lookin_lastFiltered:^BOOL(__kindof UIView *obj) {
|
||||
if (obj.hidden || obj.alpha <= 0.01) {
|
||||
return NO;
|
||||
}
|
||||
BOOL contains = CGRectContainsPoint(obj.frame, point);
|
||||
return contains;
|
||||
}];
|
||||
|
||||
if (!targetView) {
|
||||
return self;
|
||||
}
|
||||
|
||||
CGPoint newPoint = [targetView convertPoint:point fromView:self];
|
||||
targetView = [targetView lks_subviewAtPoint:newPoint preferredClasses:preferredClasses];
|
||||
return targetView;
|
||||
}
|
||||
|
||||
- (CGSize)lks_bestSize {
|
||||
return [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
|
||||
}
|
||||
|
||||
- (CGFloat)lks_bestWidth {
|
||||
return self.lks_bestSize.width;
|
||||
}
|
||||
|
||||
- (CGFloat)lks_bestHeight {
|
||||
return self.lks_bestSize.height;
|
||||
}
|
||||
|
||||
- (void)setLks_isChildrenViewOfTabBar:(BOOL)lks_isChildrenViewOfTabBar {
|
||||
[self lookin_bindBOOL:lks_isChildrenViewOfTabBar forKey:@"lks_isChildrenViewOfTabBar"];
|
||||
}
|
||||
- (BOOL)lks_isChildrenViewOfTabBar {
|
||||
return [self lookin_getBindBOOLForKey:@"lks_isChildrenViewOfTabBar"];
|
||||
}
|
||||
|
||||
- (void)setLks_verticalContentHuggingPriority:(float)lks_verticalContentHuggingPriority {
|
||||
[self setContentHuggingPriority:lks_verticalContentHuggingPriority forAxis:UILayoutConstraintAxisVertical];
|
||||
}
|
||||
- (float)lks_verticalContentHuggingPriority {
|
||||
return [self contentHuggingPriorityForAxis:UILayoutConstraintAxisVertical];
|
||||
}
|
||||
|
||||
- (void)setLks_horizontalContentHuggingPriority:(float)lks_horizontalContentHuggingPriority {
|
||||
[self setContentHuggingPriority:lks_horizontalContentHuggingPriority forAxis:UILayoutConstraintAxisHorizontal];
|
||||
}
|
||||
- (float)lks_horizontalContentHuggingPriority {
|
||||
return [self contentHuggingPriorityForAxis:UILayoutConstraintAxisHorizontal];
|
||||
}
|
||||
|
||||
- (void)setLks_verticalContentCompressionResistancePriority:(float)lks_verticalContentCompressionResistancePriority {
|
||||
[self setContentCompressionResistancePriority:lks_verticalContentCompressionResistancePriority forAxis:UILayoutConstraintAxisVertical];
|
||||
}
|
||||
- (float)lks_verticalContentCompressionResistancePriority {
|
||||
return [self contentCompressionResistancePriorityForAxis:UILayoutConstraintAxisVertical];
|
||||
}
|
||||
|
||||
- (void)setLks_horizontalContentCompressionResistancePriority:(float)lks_horizontalContentCompressionResistancePriority {
|
||||
[self setContentCompressionResistancePriority:lks_horizontalContentCompressionResistancePriority forAxis:UILayoutConstraintAxisHorizontal];
|
||||
}
|
||||
- (float)lks_horizontalContentCompressionResistancePriority {
|
||||
return [self contentCompressionResistancePriorityForAxis:UILayoutConstraintAxisHorizontal];
|
||||
}
|
||||
|
||||
+ (void)lks_rebuildGlobalInvolvedRawConstraints {
|
||||
[[LKS_MultiplatformAdapter allWindows] enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self lks_removeInvolvedRawConstraintsForViewsRootedByView:window];
|
||||
}];
|
||||
[[LKS_MultiplatformAdapter allWindows] enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull window, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self lks_addInvolvedRawConstraintsForViewsRootedByView:window];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)lks_addInvolvedRawConstraintsForViewsRootedByView:(UIView *)rootView {
|
||||
[rootView.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull constraint, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
UIView *firstView = constraint.firstItem;
|
||||
if ([firstView isKindOfClass:[UIView class]] && ![firstView.lks_involvedRawConstraints containsObject:constraint]) {
|
||||
if (!firstView.lks_involvedRawConstraints) {
|
||||
firstView.lks_involvedRawConstraints = [NSMutableArray array];
|
||||
}
|
||||
[firstView.lks_involvedRawConstraints addObject:constraint];
|
||||
}
|
||||
|
||||
UIView *secondView = constraint.secondItem;
|
||||
if ([secondView isKindOfClass:[UIView class]] && ![secondView.lks_involvedRawConstraints containsObject:constraint]) {
|
||||
if (!secondView.lks_involvedRawConstraints) {
|
||||
secondView.lks_involvedRawConstraints = [NSMutableArray array];
|
||||
}
|
||||
[secondView.lks_involvedRawConstraints addObject:constraint];
|
||||
}
|
||||
}];
|
||||
|
||||
[rootView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self lks_addInvolvedRawConstraintsForViewsRootedByView:subview];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)lks_removeInvolvedRawConstraintsForViewsRootedByView:(UIView *)rootView {
|
||||
[rootView.lks_involvedRawConstraints removeAllObjects];
|
||||
[rootView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[self lks_removeInvolvedRawConstraintsForViewsRootedByView:subview];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setLks_involvedRawConstraints:(NSMutableArray<NSLayoutConstraint *> *)lks_involvedRawConstraints {
|
||||
[self lookin_bindObject:lks_involvedRawConstraints forKey:@"lks_involvedRawConstraints"];
|
||||
}
|
||||
|
||||
- (NSMutableArray<NSLayoutConstraint *> *)lks_involvedRawConstraints {
|
||||
return [self lookin_getBindObjectForKey:@"lks_involvedRawConstraints"];
|
||||
}
|
||||
|
||||
- (NSArray<LookinAutoLayoutConstraint *> *)lks_constraints {
|
||||
/**
|
||||
- lks_involvedRawConstraints 保存了牵扯到了 self 的所有的 constraints(包括未生效的,但不包括 inactive 的,整个产品逻辑都是直接忽略 inactive 的 constraints)
|
||||
- 通过 constraintsAffectingLayoutForAxis 可以拿到会影响 self 布局的所有已生效的 constraints(这里称之为 effectiveConstraints)
|
||||
- 很多情况下,一条 constraint 会出现在 effectiveConstraints 里但不会出现在 lks_involvedRawConstraints 里,比如:
|
||||
· UIWindow 拥有 minX, minY, width, height 四个 effectiveConstraints,但 lks_involvedRawConstraints 为空,因为它的 constraints 属性为空(这一点不知道为啥,但 Xcode Inspector 和 Reveal 确实也不会显示这四个 constraints)
|
||||
· 如果设置了 View1 的 center 和 superview 的 center 保持一致,则 superview 的 width 和 height 也会出现在 effectiveConstraints 里,但不会出现在 lks_involvedRawConstraints 里(这点可以理解,毕竟这种场景下 superview 的 width 和 height 确实会影响到 View1)
|
||||
*/
|
||||
NSMutableArray<NSLayoutConstraint *> *effectiveConstraints = [NSMutableArray array];
|
||||
[effectiveConstraints addObjectsFromArray:[self constraintsAffectingLayoutForAxis:UILayoutConstraintAxisHorizontal]];
|
||||
[effectiveConstraints addObjectsFromArray:[self constraintsAffectingLayoutForAxis:UILayoutConstraintAxisVertical]];
|
||||
|
||||
NSArray<LookinAutoLayoutConstraint *> *lookinConstraints = [self.lks_involvedRawConstraints lookin_map:^id(NSUInteger idx, __kindof NSLayoutConstraint *constraint) {
|
||||
BOOL isEffective = [effectiveConstraints containsObject:constraint];
|
||||
if ([constraint isActive]) {
|
||||
// trying to get firstItem or secondItem of an inactive constraint may cause dangling-pointer crash
|
||||
// https://github.com/QMUI/LookinServer/issues/86
|
||||
LookinConstraintItemType firstItemType = [self _lks_constraintItemTypeForItem:constraint.firstItem];
|
||||
LookinConstraintItemType secondItemType = [self _lks_constraintItemTypeForItem:constraint.secondItem];
|
||||
LookinAutoLayoutConstraint *lookinConstraint = [LookinAutoLayoutConstraint instanceFromNSConstraint:constraint isEffective:isEffective firstItemType:firstItemType secondItemType:secondItemType];
|
||||
return lookinConstraint;
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
return lookinConstraints.count ? lookinConstraints : nil;
|
||||
}
|
||||
|
||||
- (LookinConstraintItemType)_lks_constraintItemTypeForItem:(id)item {
|
||||
if (!item) {
|
||||
return LookinConstraintItemTypeNil;
|
||||
}
|
||||
if (item == self) {
|
||||
return LookinConstraintItemTypeSelf;
|
||||
}
|
||||
if (item == self.superview) {
|
||||
return LookinConstraintItemTypeSuper;
|
||||
}
|
||||
|
||||
// 在 runtime 时,这里会遇到的 UILayoutGuide 和 _UILayoutGuide 居然是 UIView 的子类,不知道是看错了还是有什么玄机,所以在判断是否是 UIView 之前要先判断这个
|
||||
if (@available(iOS 9.0, *)) {
|
||||
if ([item isKindOfClass:[UILayoutGuide class]]) {
|
||||
return LookinConstraintItemTypeLayoutGuide;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *className = NSStringFromClass([item class]);
|
||||
if ([className hasSuffix:@"_UILayoutGuide"]) {
|
||||
return LookinConstraintItemTypeLayoutGuide;
|
||||
}
|
||||
|
||||
if ([item isKindOfClass:[UIView class]]) {
|
||||
return LookinConstraintItemTypeView;
|
||||
}
|
||||
|
||||
NSAssert(NO, @"");
|
||||
return LookinConstraintItemTypeUnknown;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
19
Pods/LookinServer/Src/Main/Server/Category/UIViewController+LookinServer.h
generated
Normal file
19
Pods/LookinServer/Src/Main/Server/Category/UIViewController+LookinServer.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIViewController+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/4/22.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIViewController (LookinServer)
|
||||
|
||||
+ (UIViewController *)lks_visibleViewController;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
48
Pods/LookinServer/Src/Main/Server/Category/UIViewController+LookinServer.m
generated
Normal file
48
Pods/LookinServer/Src/Main/Server/Category/UIViewController+LookinServer.m
generated
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIViewController+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/4/22.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UIViewController+LookinServer.h"
|
||||
#import "UIView+LookinServer.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
@implementation UIViewController (LookinServer)
|
||||
|
||||
+ (nullable UIViewController *)lks_visibleViewController {
|
||||
|
||||
UIViewController *rootViewController = [LKS_MultiplatformAdapter keyWindow].rootViewController;
|
||||
UIViewController *visibleViewController = [rootViewController lks_visibleViewControllerIfExist];
|
||||
return visibleViewController;
|
||||
}
|
||||
|
||||
- (UIViewController *)lks_visibleViewControllerIfExist {
|
||||
|
||||
if (self.presentedViewController) {
|
||||
return [self.presentedViewController lks_visibleViewControllerIfExist];
|
||||
}
|
||||
|
||||
if ([self isKindOfClass:[UINavigationController class]]) {
|
||||
return [((UINavigationController *)self).visibleViewController lks_visibleViewControllerIfExist];
|
||||
}
|
||||
|
||||
if ([self isKindOfClass:[UITabBarController class]]) {
|
||||
return [((UITabBarController *)self).selectedViewController lks_visibleViewControllerIfExist];
|
||||
}
|
||||
|
||||
if (self.isViewLoaded && !self.view.hidden && self.view.alpha > 0.01) {
|
||||
return self;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Category/UIVisualEffectView+LookinServer.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Category/UIVisualEffectView+LookinServer.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIVisualEffectView+LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/10/8.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIVisualEffectView (LookinServer)
|
||||
|
||||
- (void)setLks_blurEffectStyleNumber:(NSNumber *)lks_blurEffectStyleNumber;
|
||||
|
||||
- (NSNumber *)lks_blurEffectStyleNumber;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
33
Pods/LookinServer/Src/Main/Server/Category/UIVisualEffectView+LookinServer.m
generated
Normal file
33
Pods/LookinServer/Src/Main/Server/Category/UIVisualEffectView+LookinServer.m
generated
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// UIVisualEffectView+LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/10/8.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "UIVisualEffectView+LookinServer.h"
|
||||
#import "UIBlurEffect+LookinServer.h"
|
||||
|
||||
@implementation UIVisualEffectView (LookinServer)
|
||||
|
||||
- (void)setLks_blurEffectStyleNumber:(NSNumber *)lks_blurEffectStyleNumber {
|
||||
UIBlurEffectStyle style = [lks_blurEffectStyleNumber integerValue];
|
||||
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:style];
|
||||
self.effect = effect;
|
||||
}
|
||||
|
||||
- (NSNumber *)lks_blurEffectStyleNumber {
|
||||
UIVisualEffect *effect = self.effect;
|
||||
if (![effect isKindOfClass:[UIBlurEffect class]]) {
|
||||
return nil;
|
||||
}
|
||||
UIBlurEffect *blurEffect = (UIBlurEffect *)effect;
|
||||
return blurEffect.lks_effectStyleNumber;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
29
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.h
generated
Normal file
29
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.h
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/8/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern NSString *const LKS_ConnectionDidEndNotificationName;
|
||||
|
||||
@class LookinConnectionResponseAttachment;
|
||||
|
||||
@interface LKS_ConnectionManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@property(nonatomic, assign) BOOL applicationIsActive;
|
||||
|
||||
- (void)respond:(LookinConnectionResponseAttachment *)data requestType:(uint32_t)requestType tag:(uint32_t)tag;
|
||||
|
||||
- (void)pushData:(NSObject *)data type:(uint32_t)type;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
268
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.m
generated
Normal file
268
Pods/LookinServer/Src/Main/Server/Connection/LKS_ConnectionManager.m
generated
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinServer.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2018/8/5.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "Lookin_PTChannel.h"
|
||||
#import "LKS_RequestHandler.h"
|
||||
#import "LookinConnectionResponseAttachment.h"
|
||||
#import "LKS_ExportManager.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_TraceManager.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
NSString *const LKS_ConnectionDidEndNotificationName = @"LKS_ConnectionDidEndNotificationName";
|
||||
|
||||
@interface LKS_ConnectionManager () <Lookin_PTChannelDelegate>
|
||||
|
||||
@property(nonatomic, weak) Lookin_PTChannel *peerChannel_;
|
||||
|
||||
@property(nonatomic, strong) LKS_RequestHandler *requestHandler;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_ConnectionManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static LKS_ConnectionManager *sharedInstance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[LKS_ConnectionManager alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
+ (void)load {
|
||||
// 触发 init 方法
|
||||
[LKS_ConnectionManager sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
NSLog(@"LookinServer - Will launch. Framework version: %@", LOOKIN_SERVER_READABLE_VERSION);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleApplicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspect:) name:@"Lookin_2D" object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleLocalInspect:) name:@"Lookin_3D" object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_Export" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
|
||||
[[LKS_ExportManager sharedInstance] exportAndShare];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:@"Lookin_RelationSearch" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
|
||||
[[LKS_TraceManager sharedInstance] addSearchTarger:note.object];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleGetLookinInfo:) name:@"GetLookinInfo" object:nil];
|
||||
|
||||
self.requestHandler = [LKS_RequestHandler new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_handleWillResignActiveNotification {
|
||||
self.applicationIsActive = NO;
|
||||
|
||||
if (self.peerChannel_ && ![self.peerChannel_ isConnected]) {
|
||||
[self.peerChannel_ close];
|
||||
self.peerChannel_ = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleApplicationDidBecomeActive {
|
||||
self.applicationIsActive = YES;
|
||||
[self searchPortToListenIfNoConnection];
|
||||
}
|
||||
|
||||
- (void)searchPortToListenIfNoConnection {
|
||||
if ([self.peerChannel_ isConnected]) {
|
||||
NSLog(@"LookinServer - Abort to search ports. Already has connected channel.");
|
||||
return;
|
||||
}
|
||||
NSLog(@"LookinServer - Searching port to listen...");
|
||||
[self.peerChannel_ close];
|
||||
self.peerChannel_ = nil;
|
||||
|
||||
if ([self isiOSAppOnMac]) {
|
||||
[self _tryToListenOnPortFrom:LookinSimulatorIPv4PortNumberStart to:LookinSimulatorIPv4PortNumberEnd current:LookinSimulatorIPv4PortNumberStart];
|
||||
} else {
|
||||
[self _tryToListenOnPortFrom:LookinUSBDeviceIPv4PortNumberStart to:LookinUSBDeviceIPv4PortNumberEnd current:LookinUSBDeviceIPv4PortNumberStart];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isiOSAppOnMac {
|
||||
#if TARGET_OS_SIMULATOR
|
||||
return YES;
|
||||
#else
|
||||
if (@available(iOS 14.0, *)) {
|
||||
// isiOSAppOnMac 这个 API 看似在 iOS 14.0 上可用,但其实在 iOS 14 beta 上是不存在的、有 unrecognized selector 问题,因此这里要用 respondsToSelector 做一下保护
|
||||
NSProcessInfo *info = [NSProcessInfo processInfo];
|
||||
if ([info respondsToSelector:@selector(isiOSAppOnMac)]) {
|
||||
return [info isiOSAppOnMac];
|
||||
} else if ([info respondsToSelector:@selector(isMacCatalystApp)]) {
|
||||
return [info isMacCatalystApp];
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||
return [NSProcessInfo processInfo].isMacCatalystApp;
|
||||
}
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)_tryToListenOnPortFrom:(int)fromPort to:(int)toPort current:(int)currentPort {
|
||||
Lookin_PTChannel *channel = [Lookin_PTChannel channelWithDelegate:self];
|
||||
channel.targetPort = currentPort;
|
||||
[channel listenOnPort:currentPort IPv4Address:INADDR_LOOPBACK callback:^(NSError *error) {
|
||||
if (error) {
|
||||
if (error.code == 48) {
|
||||
// 该地址已被占用
|
||||
} else {
|
||||
// 未知失败
|
||||
}
|
||||
|
||||
if (currentPort < toPort) {
|
||||
// 尝试下一个端口
|
||||
NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@). Will try anothor address ...", currentPort, error);
|
||||
[self _tryToListenOnPortFrom:fromPort to:toPort current:(currentPort + 1)];
|
||||
} else {
|
||||
// 所有端口都尝试完毕,全部失败
|
||||
NSLog(@"LookinServer - 127.0.0.1:%d is unavailable(%@).", currentPort, error);
|
||||
NSLog(@"LookinServer - Connect failed in the end.");
|
||||
}
|
||||
|
||||
} else {
|
||||
// 成功
|
||||
NSLog(@"LookinServer - Connected successfully on 127.0.0.1:%d", currentPort);
|
||||
// 此时 peerChannel_ 状态为 listening
|
||||
self.peerChannel_ = channel;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.peerChannel_) {
|
||||
[self.peerChannel_ close];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)respond:(LookinConnectionResponseAttachment *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
|
||||
[self _sendData:data frameOfType:requestType tag:tag];
|
||||
}
|
||||
|
||||
- (void)pushData:(NSObject *)data type:(uint32_t)type {
|
||||
[self _sendData:data frameOfType:type tag:0];
|
||||
}
|
||||
|
||||
- (void)_sendData:(NSObject *)data frameOfType:(uint32_t)frameOfType tag:(uint32_t)tag {
|
||||
if (self.peerChannel_) {
|
||||
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject:data];
|
||||
dispatch_data_t payload = [archivedData createReferencingDispatchData];
|
||||
|
||||
[self.peerChannel_ sendFrameOfType:frameOfType tag:tag withPayload:payload callback:^(NSError *error) {
|
||||
if (error) {
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Lookin_PTChannelDelegate
|
||||
|
||||
- (BOOL)ioFrameChannel:(Lookin_PTChannel*)channel shouldAcceptFrameOfType:(uint32_t)type tag:(uint32_t)tag payloadSize:(uint32_t)payloadSize {
|
||||
if (channel != self.peerChannel_) {
|
||||
return NO;
|
||||
} else if ([self.requestHandler canHandleRequestType:type]) {
|
||||
return YES;
|
||||
} else {
|
||||
[channel close];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didReceiveFrameOfType:(uint32_t)type tag:(uint32_t)tag payload:(Lookin_PTData*)payload {
|
||||
id object = nil;
|
||||
if (payload) {
|
||||
id unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithContentsOfDispatchData:payload.dispatchData]];
|
||||
if ([unarchivedObject isKindOfClass:[LookinConnectionAttachment class]]) {
|
||||
LookinConnectionAttachment *attachment = (LookinConnectionAttachment *)unarchivedObject;
|
||||
object = attachment.data;
|
||||
} else {
|
||||
object = unarchivedObject;
|
||||
}
|
||||
}
|
||||
[self.requestHandler handleRequestType:type tag:tag object:object];
|
||||
}
|
||||
|
||||
/// 当 Client 端链接成功时,该方法会被调用,然后 channel 的状态会变成 connected
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didAcceptConnection:(Lookin_PTChannel*)otherChannel fromAddress:(Lookin_PTAddress*)address {
|
||||
NSLog(@"LookinServer - channel:%@, acceptConnection:%@", channel.debugTag, otherChannel.debugTag);
|
||||
|
||||
Lookin_PTChannel *previousChannel = self.peerChannel_;
|
||||
|
||||
otherChannel.targetPort = address.port;
|
||||
self.peerChannel_ = otherChannel;
|
||||
|
||||
[previousChannel cancel];
|
||||
}
|
||||
|
||||
/// 当连接过 Lookin 客户端,然后 Lookin 客户端又被关闭时,会走到这里
|
||||
- (void)ioFrameChannel:(Lookin_PTChannel*)channel didEndWithError:(NSError*)error {
|
||||
if (self.peerChannel_ != channel) {
|
||||
// Client 端第一次连接上时,之前 listen 的 port 会被 Peertalk 内部 cancel(并在 didAcceptConnection 方法里给业务抛一个新建的 connected 状态的 channel),那个被 cancel 的 channel 会走到这里
|
||||
NSLog(@"LookinServer - Ignore channel%@ end.", channel.debugTag);
|
||||
return;
|
||||
}
|
||||
// Client 端关闭时,会走到这里
|
||||
NSLog(@"LookinServer - channel%@ DidEndWithError:%@", channel.debugTag, error);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:LKS_ConnectionDidEndNotificationName object:self];
|
||||
[self searchPortToListenIfNoConnection];
|
||||
}
|
||||
|
||||
#pragma mark - Handler
|
||||
|
||||
- (void)_handleLocalInspect:(NSNotification *)note {
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Lookin" message:@"Failed to run local inspection. The feature has been removed. Please use the computer version of Lookin or consider SDKs like FLEX for similar functionality." preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
|
||||
[alertController addAction:okAction];
|
||||
UIWindow *keyWindow = [LKS_MultiplatformAdapter keyWindow];
|
||||
UIViewController *rootViewController = [keyWindow rootViewController];
|
||||
[rootViewController presentViewController:alertController animated:YES completion:nil];
|
||||
|
||||
NSLog(@"LookinServer - Failed to run local inspection. The feature has been removed. Please use the computer version of Lookin or consider SDKs like FLEX for similar functionality.");
|
||||
}
|
||||
|
||||
- (void)handleGetLookinInfo:(NSNotification *)note {
|
||||
NSDictionary* userInfo = note.userInfo;
|
||||
if (!userInfo) {
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary* infoWrapper = userInfo[@"infos"];
|
||||
if (![infoWrapper isKindOfClass:[NSMutableDictionary class]]) {
|
||||
NSLog(@"LookinServer - GetLookinInfo failed. Params invalid.");
|
||||
return;
|
||||
}
|
||||
infoWrapper[@"lookinServerVersion"] = LOOKIN_SERVER_READABLE_VERSION;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/// 这个类使得用户可以通过 NSClassFromString(@"Lookin") 来判断 LookinServer 是否被编译进了项目里
|
||||
|
||||
@interface Lookin : NSObject
|
||||
|
||||
@end
|
||||
|
||||
@implementation Lookin
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
21
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.h
generated
Normal file
21
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.h
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_RequestHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/1/15.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface LKS_RequestHandler : NSObject
|
||||
|
||||
- (BOOL)canHandleRequestType:(uint32_t)requestType;
|
||||
|
||||
- (void)handleRequestType:(uint32_t)requestType tag:(uint32_t)tag object:(id)object;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
558
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.m
generated
Normal file
558
Pods/LookinServer/Src/Main/Server/Connection/LKS_RequestHandler.m
generated
Normal file
@@ -0,0 +1,558 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_RequestHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/1/15.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_RequestHandler.h"
|
||||
#import "NSObject+LookinServer.h"
|
||||
#import "UIImage+LookinServer.h"
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "LookinConnectionResponseAttachment.h"
|
||||
#import "LookinAttributeModification.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LookinHierarchyInfo.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "LookinObject.h"
|
||||
#import "LookinAppInfo.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LKS_InbuiltAttrModificationHandler.h"
|
||||
#import "LKS_CustomAttrModificationHandler.h"
|
||||
#import "LKS_AttrModificationPatchHandler.h"
|
||||
#import "LKS_HierarchyDetailsHandler.h"
|
||||
#import "LookinStaticAsyncUpdateTask.h"
|
||||
|
||||
@interface LKS_RequestHandler ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableSet<LKS_HierarchyDetailsHandler *> *activeDetailHandlers;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_RequestHandler {
|
||||
NSSet *_validRequestTypes;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_validRequestTypes = [NSSet setWithObjects:@(LookinRequestTypePing),
|
||||
@(LookinRequestTypeApp),
|
||||
@(LookinRequestTypeHierarchy),
|
||||
@(LookinRequestTypeInbuiltAttrModification),
|
||||
@(LookinRequestTypeCustomAttrModification),
|
||||
@(LookinRequestTypeAttrModificationPatch),
|
||||
@(LookinRequestTypeHierarchyDetails),
|
||||
@(LookinRequestTypeFetchObject),
|
||||
@(LookinRequestTypeAllAttrGroups),
|
||||
@(LookinRequestTypeAllSelectorNames),
|
||||
@(LookinRequestTypeInvokeMethod),
|
||||
@(LookinRequestTypeFetchImageViewImage),
|
||||
@(LookinRequestTypeModifyRecognizerEnable),
|
||||
@(LookinPush_CanceHierarchyDetails),
|
||||
nil];
|
||||
|
||||
self.activeDetailHandlers = [NSMutableSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)canHandleRequestType:(uint32_t)requestType {
|
||||
if ([_validRequestTypes containsObject:@(requestType)]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)handleRequestType:(uint32_t)requestType tag:(uint32_t)tag object:(id)object {
|
||||
if (requestType == LookinRequestTypePing) {
|
||||
LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
|
||||
// 当 app 处于后台时,可能可以执行代码也可能不能执行代码,如果运气好了可以执行代码,则这里直接主动使用 appIsInBackground 标识 app 处于后台,不要让 Lookin 客户端傻傻地等待超时了
|
||||
if (![LKS_ConnectionManager sharedInstance].applicationIsActive) {
|
||||
responseAttachment.appIsInBackground = YES;
|
||||
}
|
||||
[[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeApp) {
|
||||
// 请求可用设备信息
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary<NSString *, id> *params = object;
|
||||
BOOL needImages = ((NSNumber *)params[@"needImages"]).boolValue;
|
||||
NSArray<NSNumber *> *localIdentifiers = params[@"local"];
|
||||
|
||||
LookinAppInfo *appInfo = [LookinAppInfo currentInfoWithScreenshot:needImages icon:needImages localIdentifiers:localIdentifiers];
|
||||
|
||||
LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
|
||||
responseAttachment.data = appInfo;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeHierarchy) {
|
||||
// 从 LookinClient 1.0.4 开始有这个参数,之前是 nil
|
||||
NSString *clientVersion = nil;
|
||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary<NSString *, id> *params = object;
|
||||
NSString *version = params[@"clientVersion"];
|
||||
if ([version isKindOfClass:[NSString class]]) {
|
||||
clientVersion = version;
|
||||
}
|
||||
}
|
||||
|
||||
LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
|
||||
responseAttachment.data = [LookinHierarchyInfo staticInfoWithLookinVersion:clientVersion];
|
||||
[[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeInbuiltAttrModification) {
|
||||
// 请求修改某个属性
|
||||
[LKS_InbuiltAttrModificationHandler handleModification:object completion:^(LookinDisplayItemDetail *data, NSError *error) {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
if (error) {
|
||||
attachment.error = error;
|
||||
} else {
|
||||
attachment.data = data;
|
||||
}
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
|
||||
}];
|
||||
|
||||
} else if (requestType == LookinRequestTypeCustomAttrModification) {
|
||||
BOOL succ = [LKS_CustomAttrModificationHandler handleModification:object];
|
||||
if (succ) {
|
||||
[self _submitResponseWithData:nil requestType:requestType tag:tag];
|
||||
} else {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
} else if (requestType == LookinRequestTypeAttrModificationPatch) {
|
||||
NSArray<LookinStaticAsyncUpdateTask *> *tasks = object;
|
||||
NSUInteger dataTotalCount = tasks.count;
|
||||
[LKS_InbuiltAttrModificationHandler handlePatchWithTasks:tasks block:^(LookinDisplayItemDetail *data) {
|
||||
LookinConnectionResponseAttachment *attrAttachment = [LookinConnectionResponseAttachment new];
|
||||
attrAttachment.data = data;
|
||||
attrAttachment.dataTotalCount = dataTotalCount;
|
||||
attrAttachment.currentDataCount = 1;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attrAttachment requestType:LookinRequestTypeAttrModificationPatch tag:tag];
|
||||
}];
|
||||
|
||||
} else if (requestType == LookinRequestTypeHierarchyDetails) {
|
||||
NSArray<LookinStaticAsyncUpdateTasksPackage *> *packages = object;
|
||||
NSUInteger responsesDataTotalCount = [packages lookin_reduceInteger:^NSInteger(NSInteger accumulator, NSUInteger idx, LookinStaticAsyncUpdateTasksPackage *package) {
|
||||
accumulator += package.tasks.count;
|
||||
return accumulator;
|
||||
} initialAccumlator:0];
|
||||
|
||||
LKS_HierarchyDetailsHandler *handler = [LKS_HierarchyDetailsHandler new];
|
||||
[self.activeDetailHandlers addObject:handler];
|
||||
|
||||
[handler startWithPackages:packages block:^(NSArray<LookinDisplayItemDetail *> *details) {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
attachment.data = details;
|
||||
attachment.dataTotalCount = responsesDataTotalCount;
|
||||
attachment.currentDataCount = details.count;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:LookinRequestTypeHierarchyDetails tag:tag];
|
||||
|
||||
} finishedBlock:^{
|
||||
[self.activeDetailHandlers removeObject:handler];
|
||||
}];
|
||||
|
||||
} else if (requestType == LookinRequestTypeFetchObject) {
|
||||
unsigned long oid = ((NSNumber *)object).unsignedLongValue;
|
||||
NSObject *object = [NSObject lks_objectWithOid:oid];
|
||||
LookinObject *lookinObj = [LookinObject instanceWithObject:object];
|
||||
|
||||
LookinConnectionResponseAttachment *attach = [LookinConnectionResponseAttachment new];
|
||||
attach.data = lookinObj;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attach requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeAllAttrGroups) {
|
||||
unsigned long oid = ((NSNumber *)object).unsignedLongValue;
|
||||
CALayer *layer = (CALayer *)[NSObject lks_objectWithOid:oid];
|
||||
if (![layer isKindOfClass:[CALayer class]]) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:LookinRequestTypeAllAttrGroups tag:tag];
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<LookinAttributesGroup *> *list = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
[self _submitResponseWithData:list requestType:LookinRequestTypeAllAttrGroups tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeAllSelectorNames) {
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary *params = object;
|
||||
Class targetClass = NSClassFromString(params[@"className"]);
|
||||
BOOL hasArg = [(NSNumber *)params[@"hasArg"] boolValue];
|
||||
if (!targetClass) {
|
||||
NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"Didn't find the class named \"%@\". Please input another class and try again."), object];
|
||||
[self _submitResponseWithError:LookinErrorMake(errorMsg, @"") requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<NSString *> *selNames = [self _methodNameListForClass:targetClass hasArg:hasArg];
|
||||
[self _submitResponseWithData:selNames requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeInvokeMethod) {
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary *param = object;
|
||||
unsigned long oid = [param[@"oid"] unsignedLongValue];
|
||||
NSString *text = param[@"text"];
|
||||
if (!text.length) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSObject *targerObj = [NSObject lks_objectWithOid:oid];
|
||||
if (!targerObj) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
|
||||
SEL targetSelector = NSSelectorFromString(text);
|
||||
if (targetSelector && [targerObj respondsToSelector:targetSelector]) {
|
||||
NSString *resultDescription;
|
||||
NSObject *resultObject;
|
||||
NSError *error;
|
||||
[self _handleInvokeWithObject:targerObj selector:targetSelector resultDescription:&resultDescription resultObject:&resultObject error:&error];
|
||||
if (error) {
|
||||
[self _submitResponseWithError:error requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary *responseData = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
if (resultDescription) {
|
||||
responseData[@"description"] = resultDescription;
|
||||
}
|
||||
if (resultObject) {
|
||||
responseData[@"object"] = resultObject;
|
||||
}
|
||||
[self _submitResponseWithData:responseData requestType:requestType tag:tag];
|
||||
} else {
|
||||
NSString *errMsg = [NSString stringWithFormat:LKS_Localized(@"%@ doesn't have an instance method called \"%@\"."), NSStringFromClass(targerObj.class), text];
|
||||
[self _submitResponseWithError:LookinErrorMake(errMsg, @"") requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
} else if (requestType == LookinPush_CanceHierarchyDetails) {
|
||||
[self.activeDetailHandlers enumerateObjectsUsingBlock:^(LKS_HierarchyDetailsHandler * _Nonnull handler, BOOL * _Nonnull stop) {
|
||||
[handler cancel];
|
||||
}];
|
||||
[self.activeDetailHandlers removeAllObjects];
|
||||
|
||||
} else if (requestType == LookinRequestTypeFetchImageViewImage) {
|
||||
if (![object isKindOfClass:[NSNumber class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
unsigned long imageViewOid = [(NSNumber *)object unsignedLongValue];
|
||||
UIImageView *imageView = (UIImageView *)[NSObject lks_objectWithOid:imageViewOid];
|
||||
if (!imageView) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
if (![imageView isKindOfClass:[UIImageView class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
UIImage *image = imageView.image;
|
||||
NSData *imageData = [image lookin_data];
|
||||
[self _submitResponseWithData:imageData requestType:requestType tag:tag];
|
||||
|
||||
} else if (requestType == LookinRequestTypeModifyRecognizerEnable) {
|
||||
if (![object isKindOfClass:[NSDictionary class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
NSDictionary<NSString *, NSNumber *> *params = object;
|
||||
unsigned long recognizerOid = ((NSNumber *)params[@"oid"]).unsignedLongValue;
|
||||
BOOL shouldBeEnabled = ((NSNumber *)params[@"enable"]).boolValue;
|
||||
|
||||
UIGestureRecognizer *recognizer = (UIGestureRecognizer *)[NSObject lks_objectWithOid:recognizerOid];
|
||||
if (!recognizer) {
|
||||
[self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
if (![recognizer isKindOfClass:[UIGestureRecognizer class]]) {
|
||||
[self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
|
||||
return;
|
||||
}
|
||||
recognizer.enabled = shouldBeEnabled;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
// dispatch 以确保拿到的 enabled 是比较新的
|
||||
[self _submitResponseWithData:@(recognizer.enabled) requestType:requestType tag:tag];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)_methodNameListForClass:(Class)aClass hasArg:(BOOL)hasArg {
|
||||
NSSet<NSString *> *prefixesToVoid = [NSSet setWithObjects:@"_", @"CA_", @"cpl", @"mf_", @"vs_", @"pep_", @"isNS", @"avkit_", @"PG_", @"px_", @"pl_", @"nsli_", @"pu_", @"pxg_", nil];
|
||||
NSMutableArray<NSString *> *array = [NSMutableArray array];
|
||||
|
||||
Class currentClass = aClass;
|
||||
while (currentClass) {
|
||||
NSString *className = NSStringFromClass(currentClass);
|
||||
BOOL isSystemClass = ([className hasPrefix:@"UI"] || [className hasPrefix:@"CA"] || [className hasPrefix:@"NS"]);
|
||||
|
||||
unsigned int methodCount = 0;
|
||||
Method *methods = class_copyMethodList(currentClass, &methodCount);
|
||||
for (unsigned int i = 0; i < methodCount; i++) {
|
||||
NSString *selName = NSStringFromSelector(method_getName(methods[i]));
|
||||
|
||||
if (!hasArg && [selName containsString:@":"]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSystemClass) {
|
||||
BOOL invalid = [prefixesToVoid lookin_any:^BOOL(NSString *prefix) {
|
||||
return [selName hasPrefix:prefix];
|
||||
}];
|
||||
if (invalid) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (selName.length && ![array containsObject:selName]) {
|
||||
[array addObject:selName];
|
||||
}
|
||||
}
|
||||
if (methods) free(methods);
|
||||
currentClass = [currentClass superclass];
|
||||
}
|
||||
|
||||
return [array lookin_sortedArrayByStringLength];
|
||||
}
|
||||
|
||||
- (void)_handleInvokeWithObject:(NSObject *)obj selector:(SEL)selector resultDescription:(NSString **)description resultObject:(LookinObject **)resultObject error:(NSError **)error {
|
||||
NSMethodSignature *signature = [obj methodSignatureForSelector:selector];
|
||||
if (signature.numberOfArguments > 2) {
|
||||
*error = LookinErrorMake(LKS_Localized(@"Lookin doesn't support invoking methods with arguments yet."), @"");
|
||||
return;
|
||||
}
|
||||
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setTarget:obj];
|
||||
[invocation setSelector:selector];
|
||||
[invocation invoke];
|
||||
|
||||
const char *returnType = [signature methodReturnType];
|
||||
|
||||
|
||||
if (strcmp(returnType, @encode(void)) == 0) {
|
||||
//void, do nothing
|
||||
*description = LookinStringFlag_VoidReturn;
|
||||
|
||||
} else if (strcmp(returnType, @encode(char)) == 0) {
|
||||
char charValue;
|
||||
[invocation getReturnValue:&charValue];
|
||||
*description = [NSString stringWithFormat:@"%@", @(charValue)];
|
||||
|
||||
} else if (strcmp(returnType, @encode(int)) == 0) {
|
||||
int intValue;
|
||||
[invocation getReturnValue:&intValue];
|
||||
if (intValue == INT_MAX) {
|
||||
*description = @"INT_MAX";
|
||||
} else if (intValue == INT_MIN) {
|
||||
*description = @"INT_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(intValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(short)) == 0) {
|
||||
short shortValue;
|
||||
[invocation getReturnValue:&shortValue];
|
||||
if (shortValue == SHRT_MAX) {
|
||||
*description = @"SHRT_MAX";
|
||||
} else if (shortValue == SHRT_MIN) {
|
||||
*description = @"SHRT_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(shortValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(long)) == 0) {
|
||||
long longValue;
|
||||
[invocation getReturnValue:&longValue];
|
||||
if (longValue == NSNotFound) {
|
||||
*description = @"NSNotFound";
|
||||
} else if (longValue == LONG_MAX) {
|
||||
*description = @"LONG_MAX";
|
||||
} else if (longValue == LONG_MIN) {
|
||||
*description = @"LONG_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(longValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(long long)) == 0) {
|
||||
long long longLongValue;
|
||||
[invocation getReturnValue:&longLongValue];
|
||||
if (longLongValue == LLONG_MAX) {
|
||||
*description = @"LLONG_MAX";
|
||||
} else if (longLongValue == LLONG_MIN) {
|
||||
*description = @"LLONG_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(longLongValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned char)) == 0) {
|
||||
unsigned char ucharValue;
|
||||
[invocation getReturnValue:&ucharValue];
|
||||
if (ucharValue == UCHAR_MAX) {
|
||||
*description = @"UCHAR_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ucharValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned int)) == 0) {
|
||||
unsigned int uintValue;
|
||||
[invocation getReturnValue:&uintValue];
|
||||
if (uintValue == UINT_MAX) {
|
||||
*description = @"UINT_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(uintValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned short)) == 0) {
|
||||
unsigned short ushortValue;
|
||||
[invocation getReturnValue:&ushortValue];
|
||||
if (ushortValue == USHRT_MAX) {
|
||||
*description = @"USHRT_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ushortValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned long)) == 0) {
|
||||
unsigned long ulongValue;
|
||||
[invocation getReturnValue:&ulongValue];
|
||||
if (ulongValue == ULONG_MAX) {
|
||||
*description = @"ULONG_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ulongValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
|
||||
unsigned long long ulongLongValue;
|
||||
[invocation getReturnValue:&ulongLongValue];
|
||||
if (ulongLongValue == ULONG_LONG_MAX) {
|
||||
*description = @"ULONG_LONG_MAX";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(ulongLongValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(float)) == 0) {
|
||||
float floatValue;
|
||||
[invocation getReturnValue:&floatValue];
|
||||
if (floatValue == FLT_MAX) {
|
||||
*description = @"FLT_MAX";
|
||||
} else if (floatValue == FLT_MIN) {
|
||||
*description = @"FLT_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(floatValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(double)) == 0) {
|
||||
double doubleValue;
|
||||
[invocation getReturnValue:&doubleValue];
|
||||
if (doubleValue == DBL_MAX) {
|
||||
*description = @"DBL_MAX";
|
||||
} else if (doubleValue == DBL_MIN) {
|
||||
*description = @"DBL_MIN";
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:@"%@", @(doubleValue)];
|
||||
}
|
||||
|
||||
} else if (strcmp(returnType, @encode(BOOL)) == 0) {
|
||||
BOOL boolValue;
|
||||
[invocation getReturnValue:&boolValue];
|
||||
*description = boolValue ? @"YES" : @"NO";
|
||||
|
||||
} else if (strcmp(returnType, @encode(SEL)) == 0) {
|
||||
SEL selValue;
|
||||
[invocation getReturnValue:&selValue];
|
||||
*description = [NSString stringWithFormat:@"SEL(%@)", NSStringFromSelector(selValue)];
|
||||
|
||||
} else if (strcmp(returnType, @encode(Class)) == 0) {
|
||||
Class classValue;
|
||||
[invocation getReturnValue:&classValue];
|
||||
*description = [NSString stringWithFormat:@"<%@>", NSStringFromClass(classValue)];
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGPoint)) == 0) {
|
||||
CGPoint targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromCGPoint(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGVector)) == 0) {
|
||||
CGVector targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromCGVector(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGSize)) == 0) {
|
||||
CGSize targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromCGSize(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGRect)) == 0) {
|
||||
CGRect rectValue;
|
||||
[invocation getReturnValue:&rectValue];
|
||||
*description = NSStringFromCGRect(rectValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(CGAffineTransform)) == 0) {
|
||||
CGAffineTransform rectValue;
|
||||
[invocation getReturnValue:&rectValue];
|
||||
*description = NSStringFromCGAffineTransform(rectValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(UIEdgeInsets)) == 0) {
|
||||
UIEdgeInsets targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromUIEdgeInsets(targetValue);
|
||||
|
||||
} else if (strcmp(returnType, @encode(UIOffset)) == 0) {
|
||||
UIOffset targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromUIOffset(targetValue);
|
||||
|
||||
} else {
|
||||
if (@available(iOS 11.0, tvOS 11.0, *)) {
|
||||
if (strcmp(returnType, @encode(NSDirectionalEdgeInsets)) == 0) {
|
||||
NSDirectionalEdgeInsets targetValue;
|
||||
[invocation getReturnValue:&targetValue];
|
||||
*description = NSStringFromDirectionalEdgeInsets(targetValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *argType_string = [[NSString alloc] lookin_safeInitWithUTF8String:returnType];
|
||||
if ([argType_string hasPrefix:@"@"] || [argType_string hasPrefix:@"^{"]) {
|
||||
__unsafe_unretained id returnObjValue;
|
||||
[invocation getReturnValue:&returnObjValue];
|
||||
|
||||
if (returnObjValue) {
|
||||
*description = [NSString stringWithFormat:@"%@", returnObjValue];
|
||||
|
||||
LookinObject *parsedLookinObj = [LookinObject instanceWithObject:returnObjValue];
|
||||
*resultObject = parsedLookinObj;
|
||||
} else {
|
||||
*description = @"nil";
|
||||
}
|
||||
} else {
|
||||
*description = [NSString stringWithFormat:LKS_Localized(@"%@ was invoked successfully, but Lookin can't parse the return value:%@"), NSStringFromSelector(selector), argType_string];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_submitResponseWithError:(NSError *)error requestType:(uint32_t)requestType tag:(uint32_t)tag {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
attachment.error = error;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
- (void)_submitResponseWithData:(NSObject *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
|
||||
LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
|
||||
attachment.data = data;
|
||||
[[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
26
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.h
generated
Normal file
26
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.h
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_AttrModificationPatchHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinDisplayItemDetail;
|
||||
|
||||
@interface LKS_AttrModificationPatchHandler : NSObject
|
||||
|
||||
/**
|
||||
@param oids 数组内 idx 较小的应该为 displayItems 里的 subItem,idx 较大的应该为 superItem
|
||||
@param lowImageQuality 是否采用低图像质量
|
||||
@param block 该 block 会被多次调用,其中 tasksTotalCount 是总的调用次数(即可被用来作为 TotalResponseCount)
|
||||
*/
|
||||
+ (void)handleLayerOids:(NSArray<NSNumber *> *)oids lowImageQuality:(BOOL)lowImageQuality block:(void (^)(LookinDisplayItemDetail *detail, NSUInteger tasksTotalCount, NSError *error))block;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
51
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.m
generated
Normal file
51
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_AttrModificationPatchHandler.m
generated
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_AttrModificationPatchHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_AttrModificationPatchHandler.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LookinServerDefines.h"
|
||||
|
||||
@implementation LKS_AttrModificationPatchHandler
|
||||
|
||||
+ (void)handleLayerOids:(NSArray<NSNumber *> *)oids lowImageQuality:(BOOL)lowImageQuality block:(void (^)(LookinDisplayItemDetail *detail, NSUInteger tasksTotalCount, NSError *error))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (![oids isKindOfClass:[NSArray class]]) {
|
||||
block(nil, 1, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
|
||||
[oids enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
unsigned long oid = [obj unsignedLongValue];
|
||||
LookinDisplayItemDetail *detail = [LookinDisplayItemDetail new];
|
||||
detail.displayItemOid = oid;
|
||||
|
||||
CALayer *layer = (CALayer *)[NSObject lks_objectWithOid:oid];
|
||||
if (![layer isKindOfClass:[CALayer class]]) {
|
||||
block(nil, idx + 1, LookinErr_ObjNotFound);
|
||||
*stop = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
detail.soloScreenshot = [layer lks_soloScreenshotWithLowQuality:lowImageQuality];
|
||||
detail.groupScreenshot = [layer lks_groupScreenshotWithLowQuality:lowImageQuality];
|
||||
} else {
|
||||
detail.groupScreenshot = [layer lks_groupScreenshotWithLowQuality:lowImageQuality];
|
||||
}
|
||||
block(detail, oids.count, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
19
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.h
generated
Normal file
19
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.h
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrModificationHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likaimacbookhome on 2023/11/4.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "LookinCustomAttrModification.h"
|
||||
|
||||
@interface LKS_CustomAttrModificationHandler : NSObject
|
||||
|
||||
/// 返回值表示是否修改成功(有成功调用 setter block 就算成功)
|
||||
+ (BOOL)handleModification:(LookinCustomAttrModification *)modification;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
155
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.m
generated
Normal file
155
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_CustomAttrModificationHandler.m
generated
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
//
|
||||
// LKS_CustomAttrModificationHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by likaimacbookhome on 2023/11/4.
|
||||
//
|
||||
|
||||
#import "LKS_CustomAttrModificationHandler.h"
|
||||
#import "LKS_CustomAttrSetterManager.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
|
||||
@implementation LKS_CustomAttrModificationHandler
|
||||
|
||||
+ (BOOL)handleModification:(LookinCustomAttrModification *)modification {
|
||||
if (!modification || modification.customSetterID.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
switch (modification.attrType) {
|
||||
case LookinAttrTypeNSString: {
|
||||
NSString *newValue = modification.value;
|
||||
if (newValue != nil && ![newValue isKindOfClass:[NSString class]]) {
|
||||
// nil 是合法的
|
||||
return NO;
|
||||
}
|
||||
LKS_StringSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getStringSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeDouble: {
|
||||
NSNumber *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSNumber class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_NumberSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getNumberSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeBOOL: {
|
||||
NSNumber *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSNumber class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_BoolSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getBoolSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.boolValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeUIColor: {
|
||||
LKS_ColorSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getColorSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray<NSNumber *> *newValue = modification.value;
|
||||
if (newValue == nil) {
|
||||
// nil 是合法的
|
||||
setter(nil);
|
||||
return YES;
|
||||
}
|
||||
if (![newValue isKindOfClass:[NSArray class]]) {
|
||||
return NO;
|
||||
}
|
||||
UIColor *color = [UIColor lks_colorFromRGBAComponents:newValue];
|
||||
if (!color) {
|
||||
return NO;
|
||||
}
|
||||
setter(color);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeEnumString: {
|
||||
NSString *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSString class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_EnumSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getEnumSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeCGRect: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_RectSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getRectSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.CGRectValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeCGSize: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_SizeSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getSizeSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.CGSizeValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeCGPoint: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_PointSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getPointSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.CGPointValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
case LookinAttrTypeUIEdgeInsets: {
|
||||
NSValue *newValue = modification.value;
|
||||
if (![newValue isKindOfClass:[NSValue class]]) {
|
||||
return NO;
|
||||
}
|
||||
LKS_InsetsSetter setter = [[LKS_CustomAttrSetterManager sharedInstance] getInsetsSetterWithID:modification.customSetterID];
|
||||
if (!setter) {
|
||||
return NO;
|
||||
}
|
||||
setter(newValue.UIEdgeInsetsValue);
|
||||
return YES;
|
||||
}
|
||||
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
30
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.h
generated
Normal file
30
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.h
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_HierarchyDetailsHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinDisplayItemDetail, LookinStaticAsyncUpdateTasksPackage;
|
||||
|
||||
typedef void (^LKS_HierarchyDetailsHandler_ProgressBlock)(NSArray<LookinDisplayItemDetail *> *details);
|
||||
typedef void (^LKS_HierarchyDetailsHandler_FinishBlock)(void);
|
||||
|
||||
@interface LKS_HierarchyDetailsHandler : NSObject
|
||||
|
||||
/// packages 会按照 idx 从小到大的顺序被执行
|
||||
/// 全部任务完成时,finishBlock 会被调用
|
||||
/// 如果调用了 cancel,则 finishBlock 不会被执行
|
||||
- (void)startWithPackages:(NSArray<LookinStaticAsyncUpdateTasksPackage *> *)packages block:(LKS_HierarchyDetailsHandler_ProgressBlock)progressBlock finishedBlock:(LKS_HierarchyDetailsHandler_FinishBlock)finishBlock;
|
||||
|
||||
/// 取消所有任务
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
148
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.m
generated
Normal file
148
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_HierarchyDetailsHandler.m
generated
Normal file
@@ -0,0 +1,148 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_HierarchyDetailsHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_HierarchyDetailsHandler.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LookinStaticAsyncUpdateTask.h"
|
||||
#import "LKS_ConnectionManager.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
#import "LKS_HierarchyDisplayItemsMaker.h"
|
||||
|
||||
@interface LKS_HierarchyDetailsHandler ()
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<LookinStaticAsyncUpdateTasksPackage *> *taskPackages;
|
||||
/// 标识哪些 oid 已经拉取过 attrGroups 了
|
||||
@property(nonatomic, strong) NSMutableSet<NSNumber *> *attrGroupsSyncedOids;
|
||||
|
||||
@property(nonatomic, copy) LKS_HierarchyDetailsHandler_ProgressBlock progressBlock;
|
||||
@property(nonatomic, copy) LKS_HierarchyDetailsHandler_FinishBlock finishBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKS_HierarchyDetailsHandler
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleConnectionDidEnd:) name:LKS_ConnectionDidEndNotificationName object:nil];
|
||||
|
||||
self.attrGroupsSyncedOids = [NSMutableSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startWithPackages:(NSArray<LookinStaticAsyncUpdateTasksPackage *> *)packages block:(LKS_HierarchyDetailsHandler_ProgressBlock)progressBlock finishedBlock:(LKS_HierarchyDetailsHandler_FinishBlock)finishBlock {
|
||||
if (!progressBlock || !finishBlock) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (!packages.count) {
|
||||
finishBlock();
|
||||
return;
|
||||
}
|
||||
self.taskPackages = [packages mutableCopy];
|
||||
self.progressBlock = progressBlock;
|
||||
self.finishBlock = finishBlock;
|
||||
|
||||
[UIView lks_rebuildGlobalInvolvedRawConstraints];
|
||||
|
||||
[self _dequeueAndHandlePackage];
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[self.taskPackages removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)_dequeueAndHandlePackage {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
LookinStaticAsyncUpdateTasksPackage *package = self.taskPackages.firstObject;
|
||||
if (!package) {
|
||||
self.finishBlock();
|
||||
return;
|
||||
}
|
||||
// NSLog(@"LookinServer - will handle tasks, count: %@", @(tasks.count));
|
||||
NSArray<LookinDisplayItemDetail *> *details = [package.tasks lookin_map:^id(NSUInteger idx, LookinStaticAsyncUpdateTask *task) {
|
||||
LookinDisplayItemDetail *itemDetail = [LookinDisplayItemDetail new];
|
||||
itemDetail.displayItemOid = task.oid;
|
||||
|
||||
id object = [NSObject lks_objectWithOid:task.oid];
|
||||
if (!object || ![object isKindOfClass:[CALayer class]]) {
|
||||
itemDetail.failureCode = -1;
|
||||
return itemDetail;
|
||||
}
|
||||
|
||||
CALayer *layer = object;
|
||||
|
||||
if (task.taskType == LookinStaticAsyncUpdateTaskTypeSoloScreenshot) {
|
||||
UIImage *image = [layer lks_soloScreenshotWithLowQuality:NO];
|
||||
itemDetail.soloScreenshot = image;
|
||||
} else if (task.taskType == LookinStaticAsyncUpdateTaskTypeGroupScreenshot) {
|
||||
UIImage *image = [layer lks_groupScreenshotWithLowQuality:NO];
|
||||
itemDetail.groupScreenshot = image;
|
||||
}
|
||||
|
||||
BOOL shouldMakeAttr = [self queryIfShouldMakeAttrsFromTask:task];
|
||||
if (shouldMakeAttr) {
|
||||
itemDetail.attributesGroupList = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
|
||||
NSString *version = task.clientReadableVersion;
|
||||
if (version.length > 0 && [version lookin_numbericOSVersion] >= 10004) {
|
||||
LKS_CustomAttrGroupsMaker *maker = [[LKS_CustomAttrGroupsMaker alloc] initWithLayer:layer];
|
||||
[maker execute];
|
||||
itemDetail.customAttrGroupList = [maker getGroups];
|
||||
itemDetail.customDisplayTitle = [maker getCustomDisplayTitle];
|
||||
itemDetail.danceUISource = [maker getDanceUISource];
|
||||
}
|
||||
[self.attrGroupsSyncedOids addObject:@(task.oid)];
|
||||
}
|
||||
|
||||
if (task.needBasisVisualInfo) {
|
||||
itemDetail.frameValue = [NSValue valueWithCGRect:layer.frame];
|
||||
itemDetail.boundsValue = [NSValue valueWithCGRect:layer.bounds];
|
||||
itemDetail.hiddenValue = [NSNumber numberWithBool:layer.isHidden];
|
||||
itemDetail.alphaValue = @(layer.opacity);
|
||||
}
|
||||
|
||||
if (task.needSubitems) {
|
||||
itemDetail.subitems = [LKS_HierarchyDisplayItemsMaker subitemsOfLayer:layer];
|
||||
}
|
||||
|
||||
return itemDetail;
|
||||
}];
|
||||
self.progressBlock(details);
|
||||
|
||||
[self.taskPackages removeObjectAtIndex:0];
|
||||
[self _dequeueAndHandlePackage];
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)queryIfShouldMakeAttrsFromTask:(LookinStaticAsyncUpdateTask *)task {
|
||||
switch (task.attrRequest) {
|
||||
case LookinDetailUpdateTaskAttrRequest_Automatic: {
|
||||
BOOL alreadyMadeBefore = [self.attrGroupsSyncedOids containsObject:@(task.oid)];
|
||||
return !alreadyMadeBefore;
|
||||
}
|
||||
case LookinDetailUpdateTaskAttrRequest_Need:
|
||||
return YES;
|
||||
case LookinDetailUpdateTaskAttrRequest_NotNeed:
|
||||
return NO;
|
||||
}
|
||||
NSAssert(NO, @"");
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_handleConnectionDidEnd:(id)obj {
|
||||
[self cancel];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
23
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.h
generated
Normal file
23
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_InbuiltAttrModificationHandler.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class LookinAttributeModification, LookinDisplayItemDetail, LookinStaticAsyncUpdateTask;
|
||||
|
||||
@interface LKS_InbuiltAttrModificationHandler : NSObject
|
||||
|
||||
+ (void)handleModification:(LookinAttributeModification *)modification completion:(void (^)(LookinDisplayItemDetail *data, NSError *error))completion;
|
||||
|
||||
+ (void)handlePatchWithTasks:(NSArray<LookinStaticAsyncUpdateTask *> *)tasks block:(void (^)(LookinDisplayItemDetail *data))block;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
255
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.m
generated
Normal file
255
Pods/LookinServer/Src/Main/Server/Connection/RequestHandler/LKS_InbuiltAttrModificationHandler.m
generated
Normal file
@@ -0,0 +1,255 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LKS_InbuiltAttrModificationHandler.m
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/6/12.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LKS_InbuiltAttrModificationHandler.h"
|
||||
#import "UIColor+LookinServer.h"
|
||||
#import "LookinAttributeModification.h"
|
||||
#import "LKS_AttrGroupsMaker.h"
|
||||
#import "LookinDisplayItemDetail.h"
|
||||
#import "LookinStaticAsyncUpdateTask.h"
|
||||
#import "LookinServerDefines.h"
|
||||
#import "LKS_CustomAttrGroupsMaker.h"
|
||||
|
||||
@implementation LKS_InbuiltAttrModificationHandler
|
||||
|
||||
+ (void)handleModification:(LookinAttributeModification *)modification completion:(void (^)(LookinDisplayItemDetail *data, NSError *error))completion {
|
||||
if (!completion) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (!modification || ![modification isKindOfClass:[LookinAttributeModification class]]) {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
|
||||
NSObject *receiver = [NSObject lks_objectWithOid:modification.targetOid];
|
||||
if (!receiver) {
|
||||
completion(nil, LookinErr_ObjNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
NSMethodSignature *setterSignature = [receiver methodSignatureForSelector:modification.setterSelector];
|
||||
NSInvocation *setterInvocation = [NSInvocation invocationWithMethodSignature:setterSignature];
|
||||
setterInvocation.target = receiver;
|
||||
setterInvocation.selector = modification.setterSelector;
|
||||
|
||||
if (setterSignature.numberOfArguments != 3 || ![receiver respondsToSelector:modification.setterSelector]) {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (modification.attrType) {
|
||||
case LookinAttrTypeNone:
|
||||
case LookinAttrTypeVoid: {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
case LookinAttrTypeChar: {
|
||||
char expectedValue = [(NSNumber *)modification.value charValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeInt:
|
||||
case LookinAttrTypeEnumInt: {
|
||||
int expectedValue = [(NSNumber *)modification.value intValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeShort: {
|
||||
short expectedValue = [(NSNumber *)modification.value shortValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeLong:
|
||||
case LookinAttrTypeEnumLong: {
|
||||
long expectedValue = [(NSNumber *)modification.value longValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeLongLong: {
|
||||
long long expectedValue = [(NSNumber *)modification.value longLongValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedChar: {
|
||||
unsigned char expectedValue = [(NSNumber *)modification.value unsignedCharValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedInt: {
|
||||
unsigned int expectedValue = [(NSNumber *)modification.value unsignedIntValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedShort: {
|
||||
unsigned short expectedValue = [(NSNumber *)modification.value unsignedShortValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedLong: {
|
||||
unsigned long expectedValue = [(NSNumber *)modification.value unsignedLongValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUnsignedLongLong: {
|
||||
unsigned long long expectedValue = [(NSNumber *)modification.value unsignedLongLongValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeFloat: {
|
||||
float expectedValue = [(NSNumber *)modification.value floatValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeDouble: {
|
||||
double expectedValue = [(NSNumber *)modification.value doubleValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeBOOL: {
|
||||
BOOL expectedValue = [(NSNumber *)modification.value boolValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeSel: {
|
||||
SEL expectedValue = NSSelectorFromString(modification.value);
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeClass: {
|
||||
Class expectedValue = NSClassFromString(modification.value);
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGPoint: {
|
||||
CGPoint expectedValue = [(NSValue *)modification.value CGPointValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGVector: {
|
||||
CGVector expectedValue = [(NSValue *)modification.value CGVectorValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGSize: {
|
||||
CGSize expectedValue = [(NSValue *)modification.value CGSizeValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGRect: {
|
||||
CGRect expectedValue = [(NSValue *)modification.value CGRectValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCGAffineTransform: {
|
||||
CGAffineTransform expectedValue = [(NSValue *)modification.value CGAffineTransformValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUIEdgeInsets: {
|
||||
UIEdgeInsets expectedValue = [(NSValue *)modification.value UIEdgeInsetsValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUIOffset: {
|
||||
UIOffset expectedValue = [(NSValue *)modification.value UIOffsetValue];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeCustomObj:
|
||||
case LookinAttrTypeNSString: {
|
||||
NSObject *expectedValue = modification.value;
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
[setterInvocation retainArguments];
|
||||
break;
|
||||
}
|
||||
case LookinAttrTypeUIColor: {
|
||||
NSArray<NSNumber *> *rgba = modification.value;
|
||||
UIColor *expectedValue = [UIColor lks_colorFromRGBAComponents:rgba];
|
||||
[setterInvocation setArgument:&expectedValue atIndex:2];
|
||||
[setterInvocation retainArguments];
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
completion(nil, LookinErr_Inner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
@try {
|
||||
[setterInvocation invoke];
|
||||
} @catch (NSException *exception) {
|
||||
NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"<%@: %p>: an exception was raised when invoking %@. (%@)"), NSStringFromClass(receiver.class), receiver, NSStringFromSelector(modification.setterSelector), exception.reason];
|
||||
error = [NSError errorWithDomain:LookinErrorDomain code:LookinErrCode_Exception userInfo:@{NSLocalizedDescriptionKey:LKS_Localized(@"The modification may failed."), NSLocalizedRecoverySuggestionErrorKey:errorMsg}];
|
||||
} @finally {
|
||||
|
||||
}
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
CALayer *layer = nil;
|
||||
if ([receiver isKindOfClass:[CALayer class]]) {
|
||||
layer = (CALayer *)receiver;
|
||||
} else if ([receiver isKindOfClass:[UIView class]]) {
|
||||
layer = ((UIView *)receiver).layer;
|
||||
} else {
|
||||
completion(nil, LookinErr_ObjNotFound);
|
||||
return;
|
||||
}
|
||||
// 比如试图更改 frame 时,这个改动很有可能触发用户业务的 relayout,因此这时 dispatch 一下以确保拿到的 attrGroups 数据是最新的
|
||||
LookinDisplayItemDetail *detail = [LookinDisplayItemDetail new];
|
||||
detail.displayItemOid = modification.targetOid;
|
||||
detail.attributesGroupList = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
|
||||
|
||||
NSString *version = modification.clientReadableVersion;
|
||||
if (version.length > 0 && [version lookin_numbericOSVersion] >= 10004) {
|
||||
LKS_CustomAttrGroupsMaker *maker = [[LKS_CustomAttrGroupsMaker alloc] initWithLayer:layer];
|
||||
[maker execute];
|
||||
detail.customAttrGroupList = [maker getGroups];
|
||||
}
|
||||
|
||||
detail.frameValue = [NSValue valueWithCGRect:layer.frame];
|
||||
detail.boundsValue = [NSValue valueWithCGRect:layer.bounds];
|
||||
detail.hiddenValue = [NSNumber numberWithBool:layer.isHidden];
|
||||
detail.alphaValue = @(layer.opacity);
|
||||
completion(detail, error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+ (void)handlePatchWithTasks:(NSArray<LookinStaticAsyncUpdateTask *> *)tasks block:(void (^)(LookinDisplayItemDetail *data))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
[tasks enumerateObjectsUsingBlock:^(LookinStaticAsyncUpdateTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
LookinDisplayItemDetail *itemDetail = [LookinDisplayItemDetail new];
|
||||
itemDetail.displayItemOid = task.oid;
|
||||
id object = [NSObject lks_objectWithOid:task.oid];
|
||||
if (!object || ![object isKindOfClass:[CALayer class]]) {
|
||||
block(itemDetail);
|
||||
return;
|
||||
}
|
||||
|
||||
CALayer *layer = object;
|
||||
if (task.taskType == LookinStaticAsyncUpdateTaskTypeSoloScreenshot) {
|
||||
UIImage *image = [layer lks_soloScreenshotWithLowQuality:NO];
|
||||
itemDetail.soloScreenshot = image;
|
||||
} else if (task.taskType == LookinStaticAsyncUpdateTaskTypeGroupScreenshot) {
|
||||
UIImage *image = [layer lks_groupScreenshotWithLowQuality:NO];
|
||||
itemDetail.groupScreenshot = image;
|
||||
}
|
||||
block(itemDetail);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
17
Pods/LookinServer/Src/Main/Server/LookinServer.h
generated
Normal file
17
Pods/LookinServer/Src/Main/Server/LookinServer.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinServer.h
|
||||
// LookinServer
|
||||
//
|
||||
// Created by Li Kai on 2019/7/20.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#ifndef LookinServer_h
|
||||
#define LookinServer_h
|
||||
|
||||
|
||||
#endif /* LookinServer_h */
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
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 */
|
||||
23
Pods/LookinServer/Src/Main/Shared/Category/CALayer+Lookin.h
generated
Normal file
23
Pods/LookinServer/Src/Main/Shared/Category/CALayer+Lookin.h
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// CALayer+Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/8/4.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
@interface CALayer (Lookin)
|
||||
|
||||
- (void)lookin_removeImplicitAnimations;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
70
Pods/LookinServer/Src/Main/Shared/Category/CALayer+Lookin.m
generated
Normal file
70
Pods/LookinServer/Src/Main/Shared/Category/CALayer+Lookin.m
generated
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// CALayer+Lookin.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/8/4.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "CALayer+Lookin.h"
|
||||
|
||||
@implementation CALayer (Lookin)
|
||||
|
||||
- (void)lookin_removeImplicitAnimations {
|
||||
NSMutableDictionary<NSString *, id<CAAction>> *actions = @{NSStringFromSelector(@selector(bounds)): [NSNull null],
|
||||
NSStringFromSelector(@selector(position)): [NSNull null],
|
||||
NSStringFromSelector(@selector(zPosition)): [NSNull null],
|
||||
NSStringFromSelector(@selector(anchorPoint)): [NSNull null],
|
||||
NSStringFromSelector(@selector(anchorPointZ)): [NSNull null],
|
||||
NSStringFromSelector(@selector(transform)): [NSNull null],
|
||||
NSStringFromSelector(@selector(sublayerTransform)): [NSNull null],
|
||||
NSStringFromSelector(@selector(masksToBounds)): [NSNull null],
|
||||
NSStringFromSelector(@selector(contents)): [NSNull null],
|
||||
NSStringFromSelector(@selector(contentsRect)): [NSNull null],
|
||||
NSStringFromSelector(@selector(contentsScale)): [NSNull null],
|
||||
NSStringFromSelector(@selector(contentsCenter)): [NSNull null],
|
||||
NSStringFromSelector(@selector(minificationFilterBias)): [NSNull null],
|
||||
NSStringFromSelector(@selector(backgroundColor)): [NSNull null],
|
||||
NSStringFromSelector(@selector(cornerRadius)): [NSNull null],
|
||||
NSStringFromSelector(@selector(borderWidth)): [NSNull null],
|
||||
NSStringFromSelector(@selector(borderColor)): [NSNull null],
|
||||
NSStringFromSelector(@selector(opacity)): [NSNull null],
|
||||
NSStringFromSelector(@selector(compositingFilter)): [NSNull null],
|
||||
NSStringFromSelector(@selector(filters)): [NSNull null],
|
||||
NSStringFromSelector(@selector(backgroundFilters)): [NSNull null],
|
||||
NSStringFromSelector(@selector(shouldRasterize)): [NSNull null],
|
||||
NSStringFromSelector(@selector(rasterizationScale)): [NSNull null],
|
||||
NSStringFromSelector(@selector(shadowColor)): [NSNull null],
|
||||
NSStringFromSelector(@selector(shadowOpacity)): [NSNull null],
|
||||
NSStringFromSelector(@selector(shadowOffset)): [NSNull null],
|
||||
NSStringFromSelector(@selector(shadowRadius)): [NSNull null],
|
||||
NSStringFromSelector(@selector(shadowPath)): [NSNull null]}.mutableCopy;
|
||||
|
||||
if ([self isKindOfClass:[CAShapeLayer class]]) {
|
||||
[actions addEntriesFromDictionary:@{NSStringFromSelector(@selector(path)): [NSNull null],
|
||||
NSStringFromSelector(@selector(fillColor)): [NSNull null],
|
||||
NSStringFromSelector(@selector(strokeColor)): [NSNull null],
|
||||
NSStringFromSelector(@selector(strokeStart)): [NSNull null],
|
||||
NSStringFromSelector(@selector(strokeEnd)): [NSNull null],
|
||||
NSStringFromSelector(@selector(lineWidth)): [NSNull null],
|
||||
NSStringFromSelector(@selector(miterLimit)): [NSNull null],
|
||||
NSStringFromSelector(@selector(lineDashPhase)): [NSNull null]}];
|
||||
}
|
||||
|
||||
if ([self isKindOfClass:[CAGradientLayer class]]) {
|
||||
[actions addEntriesFromDictionary:@{NSStringFromSelector(@selector(colors)): [NSNull null],
|
||||
NSStringFromSelector(@selector(locations)): [NSNull null],
|
||||
NSStringFromSelector(@selector(startPoint)): [NSNull null],
|
||||
NSStringFromSelector(@selector(endPoint)): [NSNull null]}];
|
||||
}
|
||||
|
||||
self.actions = actions;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
27
Pods/LookinServer/Src/Main/Shared/Category/Color+Lookin.h
generated
Normal file
27
Pods/LookinServer/Src/Main/Shared/Category/Color+Lookin.h
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Color+Lookin.h
|
||||
// LookinShared
|
||||
//
|
||||
// Created by 李凯 on 2022/4/2.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#elif TARGET_OS_MAC
|
||||
|
||||
@interface NSColor (Lookin)
|
||||
|
||||
+ (instancetype)lookin_colorFromRGBAComponents:(NSArray<NSNumber *> *)components;
|
||||
|
||||
- (NSArray<NSNumber *> *)lookin_rgbaComponents;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
42
Pods/LookinServer/Src/Main/Shared/Category/Color+Lookin.m
generated
Normal file
42
Pods/LookinServer/Src/Main/Shared/Category/Color+Lookin.m
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Color+Lookin.m
|
||||
// LookinShared
|
||||
//
|
||||
// Created by 李凯 on 2022/4/2.
|
||||
//
|
||||
|
||||
#import "Image+Lookin.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#elif TARGET_OS_MAC
|
||||
|
||||
@implementation NSColor (Lookin)
|
||||
|
||||
+ (instancetype)lookin_colorFromRGBAComponents:(NSArray<NSNumber *> *)components {
|
||||
if (!components) {
|
||||
return nil;
|
||||
}
|
||||
if (components.count != 4) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
NSColor *color = [NSColor colorWithRed:components[0].doubleValue green:components[1].doubleValue blue:components[2].doubleValue alpha:components[3].doubleValue];
|
||||
return color;
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)lookin_rgbaComponents {
|
||||
NSColor *rgbColor = [self colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
|
||||
CGFloat r, g, b, a;
|
||||
[rgbColor getRed:&r green:&g blue:&b alpha:&a];
|
||||
NSArray<NSNumber *> *rgba = @[@(r), @(g), @(b), @(a)];
|
||||
return rgba;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
25
Pods/LookinServer/Src/Main/Shared/Category/Image+Lookin.h
generated
Normal file
25
Pods/LookinServer/Src/Main/Shared/Category/Image+Lookin.h
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Image+Lookin.h
|
||||
// LookinShared
|
||||
//
|
||||
// Created by 李凯 on 2022/4/2.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#elif TARGET_OS_MAC
|
||||
|
||||
@interface NSImage (LookinClient)
|
||||
|
||||
- (NSData *)lookin_data;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
26
Pods/LookinServer/Src/Main/Shared/Category/Image+Lookin.m
generated
Normal file
26
Pods/LookinServer/Src/Main/Shared/Category/Image+Lookin.m
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// Image+Lookin.m
|
||||
// LookinShared
|
||||
//
|
||||
// Created by 李凯 on 2022/4/2.
|
||||
//
|
||||
|
||||
#import "Image+Lookin.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#elif TARGET_OS_MAC
|
||||
|
||||
@implementation NSImage (LookinClient)
|
||||
|
||||
- (NSData *)lookin_data {
|
||||
return [self TIFFRepresentation];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
72
Pods/LookinServer/Src/Main/Shared/Category/NSArray+Lookin.h
generated
Normal file
72
Pods/LookinServer/Src/Main/Shared/Category/NSArray+Lookin.h
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSArray+Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/9/3.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
@interface NSArray<__covariant ValueType> (Lookin)
|
||||
|
||||
/**
|
||||
初始化一个新的 NSArray 并返回,新数组的长度为 count,如果当前数组长度比 count 小则会补充新元素(被补充的元素由 addBlock 返回),如果当前数组长度比 count 大则会舍弃多余的元素,被舍弃的元素会作为参数传入 removeBlock。最终,新数组的所有元素均会作为参数被传入 doBlock。
|
||||
*/
|
||||
- (NSArray<ValueType> *)lookin_resizeWithCount:(NSUInteger)count add:(ValueType (^)(NSUInteger idx))addBlock remove:(void (^)(NSUInteger idx, ValueType obj))removeBlock doNext:(void (^)(NSUInteger idx, ValueType obj))doBlock __attribute__((warn_unused_result));
|
||||
|
||||
+ (NSArray *)lookin_arrayWithCount:(NSUInteger)count block:(id (^)(NSUInteger idx))block;
|
||||
|
||||
/**
|
||||
检查 index 位置是否有元素存在
|
||||
*/
|
||||
- (BOOL)lookin_hasIndex:(NSInteger)index;
|
||||
|
||||
- (NSArray *)lookin_map:(id (^)(NSUInteger idx, ValueType value))block;
|
||||
|
||||
- (NSArray<ValueType> *)lookin_filter:(BOOL (^)( ValueType obj))block;
|
||||
|
||||
- (ValueType)lookin_firstFiltered:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
/// 返回最后一个 block 返回 YES 的元素
|
||||
- (ValueType)lookin_lastFiltered:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
- (id)lookin_reduce:(id (^)(id accumulator, NSUInteger idx, ValueType obj))block;
|
||||
|
||||
- (CGFloat)lookin_reduceCGFloat:(CGFloat (^)(CGFloat accumulator, NSUInteger idx, ValueType obj))block initialAccumlator:(CGFloat)initialAccumlator;
|
||||
- (NSInteger)lookin_reduceInteger:(NSInteger (^)(NSInteger accumulator, NSUInteger idx, ValueType obj))block initialAccumlator:(NSInteger)initialAccumlator;
|
||||
|
||||
- (BOOL)lookin_all:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
- (BOOL)lookin_any:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
- (NSArray<ValueType> *)lookin_arrayByRemovingObject:(ValueType)obj;
|
||||
|
||||
- (NSArray<ValueType> *)lookin_nonredundantArray;
|
||||
|
||||
- (ValueType)lookin_safeObjectAtIndex:(NSInteger)idx;
|
||||
|
||||
/// 字符串长度从短到长,即 length 小的字符串的 idx 更小
|
||||
- (NSArray<ValueType> *)lookin_sortedArrayByStringLength;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSMutableArray<ValueType> (Lookin)
|
||||
|
||||
/**
|
||||
如果当前数组长度比 count 小则会补充新元素(被补充的元素由 addBlock 返回),如果当前数组长度比 count 大则多余的元素会被作为参数传入 notDequeued。然后从 idx 为 0 算起,前 count 个元素会被作为参数传入 doBlock
|
||||
*/
|
||||
- (void)lookin_dequeueWithCount:(NSUInteger)count add:(ValueType (^)(NSUInteger idx))addBlock notDequeued:(void (^)(NSUInteger idx, ValueType obj))notDequeuedBlock doNext:(void (^)(NSUInteger idx, ValueType obj))doBlock;
|
||||
|
||||
- (void)lookin_removeObjectsPassingTest:(BOOL (^)(NSUInteger idx, ValueType obj))block;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
300
Pods/LookinServer/Src/Main/Shared/Category/NSArray+Lookin.m
generated
Normal file
300
Pods/LookinServer/Src/Main/Shared/Category/NSArray+Lookin.m
generated
Normal file
@@ -0,0 +1,300 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSArray+Lookin.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/9/3.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "NSArray+Lookin.h"
|
||||
|
||||
@implementation NSArray (Lookin)
|
||||
|
||||
- (NSArray *)lookin_resizeWithCount:(NSUInteger)count add:(id (^)(NSUInteger idx))addBlock remove:(void (^)(NSUInteger idx, id obj))removeBlock doNext:(void (^)(NSUInteger idx, id obj))doBlock {
|
||||
NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:count];
|
||||
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
if (self.count > i) {
|
||||
id obj = [self objectAtIndex:i];
|
||||
[resultArray addObject:obj];
|
||||
if (doBlock) {
|
||||
doBlock(i, obj);
|
||||
}
|
||||
} else {
|
||||
if (addBlock) {
|
||||
id newObj = addBlock(i);
|
||||
if (newObj) {
|
||||
[resultArray addObject:newObj];
|
||||
if (doBlock) {
|
||||
doBlock(i, newObj);
|
||||
}
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removeBlock) {
|
||||
if (self.count > count) {
|
||||
for (NSUInteger i = count; i < self.count; i++) {
|
||||
id obj = [self objectAtIndex:i];
|
||||
removeBlock(i, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [resultArray copy];
|
||||
}
|
||||
|
||||
+ (NSArray *)lookin_arrayWithCount:(NSUInteger)count block:(id (^)(NSUInteger idx))block {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
id obj = block(i);
|
||||
if (obj) {
|
||||
[array addObject:obj];
|
||||
}
|
||||
}
|
||||
return [array copy];
|
||||
}
|
||||
|
||||
- (BOOL)lookin_hasIndex:(NSInteger)index {
|
||||
if (index == NSNotFound || index < 0) {
|
||||
return NO;
|
||||
}
|
||||
return self.count > index;
|
||||
}
|
||||
|
||||
- (NSArray *)lookin_map:(id (^)(NSUInteger , id))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:self.count];
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
id newObj = block(idx, obj);
|
||||
if (newObj) {
|
||||
[array addObject:newObj];
|
||||
}
|
||||
}];
|
||||
return [array copy];
|
||||
}
|
||||
|
||||
- (NSArray *)lookin_filter:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *mArray = [NSMutableArray array];
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (block(obj)) {
|
||||
[mArray addObject:obj];
|
||||
}
|
||||
}];
|
||||
return [mArray copy];
|
||||
}
|
||||
|
||||
- (id)lookin_firstFiltered:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block id targetObj = nil;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (block(obj)) {
|
||||
targetObj = obj;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return targetObj;
|
||||
}
|
||||
|
||||
- (id)lookin_lastFiltered:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block id targetObj = nil;
|
||||
[self enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (block(obj)) {
|
||||
targetObj = obj;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return targetObj;
|
||||
}
|
||||
|
||||
- (id)lookin_reduce:(id (^)(id accumulator, NSUInteger idx, id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block id accumulator = nil;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
accumulator = block(accumulator, idx, obj);
|
||||
}];
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
- (CGFloat)lookin_reduceCGFloat:(CGFloat (^)(CGFloat accumulator, NSUInteger idx, id obj))block initialAccumlator:(CGFloat)initialAccumlator {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return initialAccumlator;
|
||||
}
|
||||
|
||||
__block CGFloat accumulator = initialAccumlator;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
accumulator = block(accumulator, idx, obj);
|
||||
}];
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
- (NSInteger)lookin_reduceInteger:(NSInteger (^)(NSInteger, NSUInteger, id))block initialAccumlator:(NSInteger)initialAccumlator {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return initialAccumlator;
|
||||
}
|
||||
|
||||
__block NSInteger accumulator = initialAccumlator;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
accumulator = block(accumulator, idx, obj);
|
||||
}];
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
- (BOOL)lookin_all:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return NO;
|
||||
}
|
||||
__block BOOL allPass = YES;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
BOOL boolValue = block(obj);
|
||||
if (!boolValue) {
|
||||
allPass = NO;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return allPass;
|
||||
}
|
||||
|
||||
- (BOOL)lookin_any:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return NO;
|
||||
}
|
||||
__block BOOL anyPass = NO;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
BOOL boolValue = block(obj);
|
||||
if (boolValue) {
|
||||
anyPass = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return anyPass;
|
||||
}
|
||||
|
||||
- (NSArray *)lookin_arrayByRemovingObject:(id)obj {
|
||||
if (!obj || ![self containsObject:obj]) {
|
||||
return self;
|
||||
}
|
||||
NSMutableArray *mutableArray = self.mutableCopy;
|
||||
[mutableArray removeObject:obj];
|
||||
return mutableArray.copy;
|
||||
}
|
||||
|
||||
- (NSArray *)lookin_nonredundantArray {
|
||||
NSSet *set = [NSSet setWithArray:self];
|
||||
NSArray *newArray = [set allObjects];
|
||||
return newArray;
|
||||
}
|
||||
|
||||
- (id)lookin_safeObjectAtIndex:(NSInteger)idx {
|
||||
if (idx == NSNotFound || idx < 0) {
|
||||
return nil;
|
||||
}
|
||||
if (self.count <= idx) {
|
||||
return nil;
|
||||
}
|
||||
return [self objectAtIndex:idx];
|
||||
}
|
||||
|
||||
- (NSArray *)lookin_sortedArrayByStringLength {
|
||||
NSArray<NSString *> *sortedArray = [self sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
|
||||
if (obj1.length > obj2.length) {
|
||||
return NSOrderedDescending;
|
||||
} else if (obj1.length == obj2.length) {
|
||||
return NSOrderedSame;
|
||||
} else {
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
}];
|
||||
return sortedArray;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSMutableArray (Lookin)
|
||||
|
||||
- (void)lookin_dequeueWithCount:(NSUInteger)count add:(id (^)(NSUInteger idx))addBlock notDequeued:(void (^)(NSUInteger idx, id obj))notDequeuedBlock doNext:(void (^)(NSUInteger idx, id obj))doBlock {
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
if ([self lookin_hasIndex:i]) {
|
||||
id obj = [self objectAtIndex:i];
|
||||
if (doBlock) {
|
||||
doBlock(i, obj);
|
||||
}
|
||||
} else {
|
||||
if (addBlock) {
|
||||
id newObj = addBlock(i);
|
||||
if (newObj) {
|
||||
[self addObject:newObj];
|
||||
if (doBlock) {
|
||||
doBlock(i, newObj);
|
||||
}
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notDequeuedBlock) {
|
||||
if (self.count > count) {
|
||||
for (NSUInteger i = count; i < self.count; i++) {
|
||||
id obj = [self objectAtIndex:i];
|
||||
notDequeuedBlock(i, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_removeObjectsPassingTest:(BOOL (^)(NSUInteger idx, id obj))block {
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull currentObj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
BOOL boolValue = block(idx, currentObj);
|
||||
if (boolValue) {
|
||||
[indexSet addIndex:idx];
|
||||
}
|
||||
}];
|
||||
[self removeObjectsAtIndexes:indexSet];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
108
Pods/LookinServer/Src/Main/Shared/Category/NSObject+Lookin.h
generated
Normal file
108
Pods/LookinServer/Src/Main/Shared/Category/NSObject+Lookin.h
generated
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSObject+Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/12/22.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "LookinCodingValueType.h"
|
||||
|
||||
@interface NSObject (Lookin)
|
||||
|
||||
#pragma mark - Data Bind
|
||||
|
||||
/**
|
||||
给对象绑定上另一个对象以供后续取出使用,如果 object 传入 nil 则会清除该 key 之前绑定的对象
|
||||
|
||||
@attention 被绑定的对象会被 strong 强引用
|
||||
@note 内部是使用 objc_setAssociatedObject / objc_getAssociatedObject 来实现
|
||||
|
||||
@code
|
||||
- (UITableViewCell *)cellForIndexPath:(NSIndexPath *)indexPath {
|
||||
// 1)在这里给 button 绑定上 indexPath 对象
|
||||
[cell lookin_bindObject:indexPath forKey:@"indexPath"];
|
||||
}
|
||||
|
||||
- (void)didTapButton:(UIButton *)button {
|
||||
// 2)在这里取出被点击的 button 的 indexPath 对象
|
||||
NSIndexPath *indexPathTapped = [button lookin_getBindObjectForKey:@"indexPath"];
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
- (void)lookin_bindObject:(id)object forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
给对象绑定上另一个对象以供后续取出使用,但相比于 lookin_bindObject:forKey:,该方法不会 strong 强引用传入的 object
|
||||
*/
|
||||
- (void)lookin_bindObjectWeakly:(id)object forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
取出之前使用 bind 方法绑定的对象
|
||||
*/
|
||||
- (id)lookin_getBindObjectForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
给对象绑定上一个 double 值以供后续取出使用
|
||||
*/
|
||||
- (void)lookin_bindDouble:(double)doubleValue forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
取出之前用 lookin_bindDouble:forKey: 绑定的值
|
||||
*/
|
||||
- (double)lookin_getBindDoubleForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
给对象绑定上一个 BOOL 值以供后续取出使用
|
||||
*/
|
||||
- (void)lookin_bindBOOL:(BOOL)boolValue forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
取出之前用 lookin_bindBOOL:forKey: 绑定的值
|
||||
*/
|
||||
- (BOOL)lookin_getBindBOOLForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
给对象绑定上一个 long 值以供后续取出使用
|
||||
*/
|
||||
- (void)lookin_bindLong:(long)longValue forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
取出之前用 lookin_bindLong:forKey: 绑定的值
|
||||
*/
|
||||
- (long)lookin_getBindLongForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
给对象绑定上一个 CGPoint 值以供后续取出使用
|
||||
*/
|
||||
- (void)lookin_bindPoint:(CGPoint)pointValue forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
取出之前用 lookin_bindPoint:forKey: 绑定的值
|
||||
*/
|
||||
- (CGPoint)lookin_getBindPointForKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
移除之前使用 bind 方法绑定的对象
|
||||
*/
|
||||
- (void)lookin_clearBindForKey:(NSString *)key;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSObject (Lookin_Coding)
|
||||
|
||||
/// 会把 NSImage/UIImage 转换为 NSData,把 NSColor/UIColor 转换回 NSNumber 数组(rgba)
|
||||
- (id)lookin_encodedObjectWithType:(LookinCodingValueType)type;
|
||||
/// 会把 NSData 转换回 NSImage/UIImage,把 NSNumber 数组(rgba) 转换为 NSColor/UIColor
|
||||
- (id)lookin_decodedObjectWithType:(LookinCodingValueType)type;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
238
Pods/LookinServer/Src/Main/Shared/Category/NSObject+Lookin.m
generated
Normal file
238
Pods/LookinServer/Src/Main/Shared/Category/NSObject+Lookin.m
generated
Normal file
@@ -0,0 +1,238 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSObject+Lookin.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2018/12/22.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "NSObject+Lookin.h"
|
||||
#import <objc/runtime.h>
|
||||
#import "TargetConditionals.h"
|
||||
#import "LookinWeakContainer.h"
|
||||
|
||||
@implementation NSObject (Lookin)
|
||||
|
||||
#pragma mark - Data Bind
|
||||
|
||||
static char kAssociatedObjectKey_LookinAllBindObjects;
|
||||
- (NSMutableDictionary<id, id> *)lookin_allBindObjects {
|
||||
NSMutableDictionary<id, id> *dict = objc_getAssociatedObject(self, &kAssociatedObjectKey_LookinAllBindObjects);
|
||||
if (!dict) {
|
||||
dict = [NSMutableDictionary dictionary];
|
||||
objc_setAssociatedObject(self, &kAssociatedObjectKey_LookinAllBindObjects, dict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
- (void)lookin_bindObject:(id)object forKey:(NSString *)key {
|
||||
if (!key.length) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
@synchronized (self) {
|
||||
if (object) {
|
||||
[[self lookin_allBindObjects] setObject:object forKey:key];
|
||||
} else {
|
||||
[[self lookin_allBindObjects] removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id)lookin_getBindObjectForKey:(NSString *)key {
|
||||
if (!key.length) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
@synchronized (self) {
|
||||
id storedObj = [[self lookin_allBindObjects] objectForKey:key];
|
||||
if ([storedObj isKindOfClass:[LookinWeakContainer class]]) {
|
||||
storedObj = [(LookinWeakContainer *)storedObj object];
|
||||
}
|
||||
return storedObj;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_bindObjectWeakly:(id)object forKey:(NSString *)key {
|
||||
if (!key.length) {
|
||||
NSAssert(NO, @"");
|
||||
return;
|
||||
}
|
||||
if (object) {
|
||||
LookinWeakContainer *container = [[LookinWeakContainer alloc] init];
|
||||
container.object = object;
|
||||
[self lookin_bindObject:container forKey:key];
|
||||
} else {
|
||||
[self lookin_bindObject:nil forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_bindDouble:(double)doubleValue forKey:(NSString *)key {
|
||||
[self lookin_bindObject:@(doubleValue) forKey:key];
|
||||
}
|
||||
|
||||
- (double)lookin_getBindDoubleForKey:(NSString *)key {
|
||||
id object = [self lookin_getBindObjectForKey:key];
|
||||
if ([object isKindOfClass:[NSNumber class]]) {
|
||||
double doubleValue = [(NSNumber *)object doubleValue];
|
||||
return doubleValue;
|
||||
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_bindBOOL:(BOOL)boolValue forKey:(NSString *)key {
|
||||
[self lookin_bindObject:@(boolValue) forKey:key];
|
||||
}
|
||||
|
||||
- (BOOL)lookin_getBindBOOLForKey:(NSString *)key {
|
||||
id object = [self lookin_getBindObjectForKey:key];
|
||||
if ([object isKindOfClass:[NSNumber class]]) {
|
||||
BOOL boolValue = [(NSNumber *)object boolValue];
|
||||
return boolValue;
|
||||
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_bindLong:(long)longValue forKey:(NSString *)key {
|
||||
[self lookin_bindObject:@(longValue) forKey:key];
|
||||
}
|
||||
|
||||
- (long)lookin_getBindLongForKey:(NSString *)key {
|
||||
id object = [self lookin_getBindObjectForKey:key];
|
||||
if ([object isKindOfClass:[NSNumber class]]) {
|
||||
long longValue = [(NSNumber *)object longValue];
|
||||
return longValue;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_bindPoint:(CGPoint)pointValue forKey:(NSString *)key {
|
||||
#if TARGET_OS_IPHONE
|
||||
[self lookin_bindObject:[NSValue valueWithCGPoint:pointValue] forKey:key];
|
||||
#elif TARGET_OS_MAC
|
||||
NSPoint nsPoint = NSMakePoint(pointValue.x, pointValue.y);
|
||||
[self lookin_bindObject:[NSValue valueWithPoint:nsPoint] forKey:key];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (CGPoint)lookin_getBindPointForKey:(NSString *)key {
|
||||
id object = [self lookin_getBindObjectForKey:key];
|
||||
if ([object isKindOfClass:[NSValue class]]) {
|
||||
#if TARGET_OS_IPHONE
|
||||
CGPoint pointValue = [(NSValue *)object CGPointValue];
|
||||
#elif TARGET_OS_MAC
|
||||
NSPoint nsPointValue = [(NSValue *)object pointValue];
|
||||
CGPoint pointValue = CGPointMake(nsPointValue.x, nsPointValue.y);
|
||||
#endif
|
||||
return pointValue;
|
||||
} else {
|
||||
return CGPointZero;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)lookin_clearBindForKey:(NSString *)key {
|
||||
[self lookin_bindObject:nil forKey:key];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSObject (Lookin_Coding)
|
||||
|
||||
- (id)lookin_encodedObjectWithType:(LookinCodingValueType)type {
|
||||
if (type == LookinCodingValueTypeColor) {
|
||||
if ([self isKindOfClass:[LookinColor class]]) {
|
||||
CGFloat r, g, b, a;
|
||||
#if TARGET_OS_IPHONE
|
||||
CGFloat white;
|
||||
if ([(UIColor *)self getRed:&r green:&g blue:&b alpha:&a]) {
|
||||
// valid
|
||||
} else if ([(UIColor *)self getWhite:&white alpha:&a]) {
|
||||
r = white;
|
||||
g = white;
|
||||
b = white;
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
}
|
||||
#elif TARGET_OS_MAC
|
||||
NSColor *color = [((NSColor *)self) colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
|
||||
[color getRed:&r green:&g blue:&b alpha:&a];
|
||||
#endif
|
||||
NSArray<NSNumber *> *rgba = @[@(r), @(g), @(b), @(a)];
|
||||
return rgba;
|
||||
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
} else if (type == LookinCodingValueTypeImage) {
|
||||
#if TARGET_OS_IPHONE
|
||||
if ([self isKindOfClass:[UIImage class]]) {
|
||||
UIImage *image = (UIImage *)self;
|
||||
return UIImagePNGRepresentation(image);
|
||||
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
#elif TARGET_OS_MAC
|
||||
if ([self isKindOfClass:[NSImage class]]) {
|
||||
NSImage *image = (NSImage *)self;
|
||||
return [image TIFFRepresentation];
|
||||
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
} else {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)lookin_decodedObjectWithType:(LookinCodingValueType)type {
|
||||
if (type == LookinCodingValueTypeColor) {
|
||||
if ([self isKindOfClass:[NSArray class]]) {
|
||||
NSArray<NSNumber *> *rgba = (NSArray *)self;
|
||||
CGFloat r = [rgba[0] doubleValue];
|
||||
CGFloat g = [rgba[1] doubleValue];
|
||||
CGFloat b = [rgba[2] doubleValue];
|
||||
CGFloat a = [rgba[3] doubleValue];
|
||||
LookinColor *color = [LookinColor colorWithRed:r green:g blue:b alpha:a];
|
||||
return color;
|
||||
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
} else if (type == LookinCodingValueTypeImage) {
|
||||
if ([self isKindOfClass:[NSData class]]) {
|
||||
LookinImage *image = [[LookinImage alloc] initWithData:(NSData *)self];
|
||||
return image;
|
||||
} else {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
} else {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
39
Pods/LookinServer/Src/Main/Shared/Category/NSSet+Lookin.h
generated
Normal file
39
Pods/LookinServer/Src/Main/Shared/Category/NSSet+Lookin.h
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSSet+Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/1/13.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
|
||||
|
||||
#import "TargetConditionals.h"
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#elif TARGET_OS_MAC
|
||||
#import <Appkit/Appkit.h>
|
||||
#endif
|
||||
|
||||
@interface NSSet<__covariant ValueType> (Lookin)
|
||||
|
||||
- (NSSet *)lookin_map:(id (^)(ValueType obj))block;
|
||||
|
||||
- (ValueType)lookin_firstFiltered:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
- (NSSet<ValueType> *)lookin_filter:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
|
||||
/**
|
||||
是否有任何一个元素满足某条件
|
||||
@note 元素将被依次传入 block 里,如果任何一个 block 返回 YES,则该方法返回 YES。如果所有 block 均返回 NO,则该方法返回 NO。
|
||||
*/
|
||||
- (BOOL)lookin_any:(BOOL (^)(ValueType obj))block;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
81
Pods/LookinServer/Src/Main/Shared/Category/NSSet+Lookin.m
generated
Normal file
81
Pods/LookinServer/Src/Main/Shared/Category/NSSet+Lookin.m
generated
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSSet+Lookin.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/1/13.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "NSSet+Lookin.h"
|
||||
|
||||
@implementation NSSet (Lookin)
|
||||
|
||||
- (NSSet *)lookin_map:(id (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableSet *newSet = [NSMutableSet setWithCapacity:self.count];
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
id newObj = block(obj);
|
||||
if (newObj) {
|
||||
[newSet addObject:newObj];
|
||||
}
|
||||
}];
|
||||
return [newSet copy];
|
||||
}
|
||||
|
||||
- (id)lookin_firstFiltered:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block id targetObj = nil;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if (block(obj)) {
|
||||
targetObj = obj;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return targetObj;
|
||||
}
|
||||
|
||||
- (NSSet *)lookin_filter:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableSet *mSet = [NSMutableSet set];
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if (block(obj)) {
|
||||
[mSet addObject:obj];
|
||||
}
|
||||
}];
|
||||
return [mSet copy];
|
||||
}
|
||||
|
||||
- (BOOL)lookin_any:(BOOL (^)(id obj))block {
|
||||
if (!block) {
|
||||
NSAssert(NO, @"");
|
||||
return NO;
|
||||
}
|
||||
__block BOOL boolValue = NO;
|
||||
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if (block(obj)) {
|
||||
boolValue = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return boolValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
42
Pods/LookinServer/Src/Main/Shared/Category/NSString+Lookin.h
generated
Normal file
42
Pods/LookinServer/Src/Main/Shared/Category/NSString+Lookin.h
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSString+Lookin.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/5/11.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSString (Lookin)
|
||||
|
||||
/**
|
||||
把 CGFloat 转成字符串,最多保留 3 位小数,转换后末尾的 0 会被删除
|
||||
如:1.2341 => @"1.234", 2.1002 => @"2.1", 3.000 => @"3"
|
||||
*/
|
||||
+ (NSString *)lookin_stringFromDouble:(double)doubleValue decimal:(NSUInteger)decimal;
|
||||
|
||||
+ (NSString *)lookin_stringFromRect:(CGRect)rect;
|
||||
|
||||
+ (NSString *)lookin_stringFromInset:(LookinInsets)insets;
|
||||
|
||||
+ (NSString *)lookin_stringFromSize:(CGSize)size;
|
||||
|
||||
+ (NSString *)lookin_stringFromPoint:(CGPoint)point;
|
||||
|
||||
+ (NSString *)lookin_rgbaStringFromColor:(LookinColor *)color;
|
||||
|
||||
- (NSString *)lookin_safeInitWithUTF8String:(const char *)string;
|
||||
|
||||
/// 把 1.2.3 这种 String 版本号转换成数字,可用于大小比较,如 110205 代表 11.2.5 版本
|
||||
- (NSInteger)lookin_numbericOSVersion;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
117
Pods/LookinServer/Src/Main/Shared/Category/NSString+Lookin.m
generated
Normal file
117
Pods/LookinServer/Src/Main/Shared/Category/NSString+Lookin.m
generated
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// NSString+Lookin.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/5/11.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "NSString+Lookin.h"
|
||||
|
||||
@implementation NSString (Lookin)
|
||||
|
||||
+ (NSString *)lookin_stringFromDouble:(double)doubleValue decimal:(NSUInteger)decimal {
|
||||
NSString *formatString = [NSString stringWithFormat:@"%%.%@f", @(decimal)];
|
||||
NSString *string = [NSString stringWithFormat:formatString, doubleValue];
|
||||
for (int i = 0; i < decimal; i++) {
|
||||
if ([[string substringFromIndex:string.length - 1] isEqualToString:@"0"]) {
|
||||
string = [string substringToIndex:string.length - 1];
|
||||
}
|
||||
}
|
||||
if ([[string substringFromIndex:string.length - 1] isEqualToString:@"."]) {
|
||||
string = [string substringToIndex:string.length - 1];
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
+ (NSString *)lookin_stringFromInset:(LookinInsets)insets {
|
||||
return [NSString stringWithFormat:@"{%@, %@, %@, %@}",
|
||||
[NSString lookin_stringFromDouble:insets.top decimal:2],
|
||||
[NSString lookin_stringFromDouble:insets.left decimal:2],
|
||||
[NSString lookin_stringFromDouble:insets.bottom decimal:2],
|
||||
[NSString lookin_stringFromDouble:insets.right decimal:2]];
|
||||
}
|
||||
|
||||
+ (NSString *)lookin_stringFromSize:(CGSize)size {
|
||||
return [NSString stringWithFormat:@"{%@, %@}",
|
||||
[NSString lookin_stringFromDouble:size.width decimal:2],
|
||||
[NSString lookin_stringFromDouble:size.height decimal:2]];
|
||||
}
|
||||
|
||||
|
||||
+ (NSString *)lookin_stringFromPoint:(CGPoint)point {
|
||||
return [NSString stringWithFormat:@"{%@, %@}",
|
||||
[NSString lookin_stringFromDouble:point.x decimal:2],
|
||||
[NSString lookin_stringFromDouble:point.y decimal:2]];
|
||||
}
|
||||
|
||||
+ (NSString *)lookin_stringFromRect:(CGRect)rect {
|
||||
return [NSString stringWithFormat:@"{%@, %@, %@, %@}",
|
||||
[NSString lookin_stringFromDouble:rect.origin.x decimal:2],
|
||||
[NSString lookin_stringFromDouble:rect.origin.y decimal:2],
|
||||
[NSString lookin_stringFromDouble:rect.size.width decimal:2],
|
||||
[NSString lookin_stringFromDouble:rect.size.height decimal:2]];
|
||||
}
|
||||
|
||||
+ (NSString *)lookin_rgbaStringFromColor:(LookinColor *)color {
|
||||
if (!color) {
|
||||
return @"nil";
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
UIColor *rgbColor = color;
|
||||
#elif TARGET_OS_MAC
|
||||
NSColor *rgbColor = [color colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
|
||||
#endif
|
||||
|
||||
CGFloat r, g, b, a;
|
||||
[rgbColor getRed:&r green:&g blue:&b alpha:&a];
|
||||
|
||||
NSString *colorDesc;
|
||||
if (a >= 1) {
|
||||
colorDesc = [NSString stringWithFormat:@"(%.0f, %.0f, %.0f)", r * 255, g * 255, b * 255];
|
||||
} else {
|
||||
colorDesc = [NSString stringWithFormat:@"(%.0f, %.0f, %.0f, %@)", r * 255, g * 255, b * 255, [NSString lookin_stringFromDouble:a decimal:2]];
|
||||
|
||||
}
|
||||
|
||||
return colorDesc;
|
||||
}
|
||||
|
||||
- (NSString *)lookin_safeInitWithUTF8String:(const char *)string {
|
||||
if (NULL != string) {
|
||||
return [self initWithUTF8String:string];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSInteger)lookin_numbericOSVersion {
|
||||
if (self.length == 0) {
|
||||
NSAssert(NO, @"");
|
||||
return 0;
|
||||
}
|
||||
NSArray *versionArr = [self componentsSeparatedByString:@"."];
|
||||
if (versionArr.count != 3) {
|
||||
NSAssert(NO, @"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSInteger numbericOSVersion = 0;
|
||||
NSInteger pos = 0;
|
||||
|
||||
while ([versionArr count] > pos && pos < 3) {
|
||||
numbericOSVersion += ([[versionArr objectAtIndex:pos] integerValue] * pow(10, (4 - pos * 2)));
|
||||
pos++;
|
||||
}
|
||||
|
||||
return numbericOSVersion;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
72
Pods/LookinServer/Src/Main/Shared/LookinAppInfo.h
generated
Normal file
72
Pods/LookinServer/Src/Main/Shared/LookinAppInfo.h
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinAppInfo.h
|
||||
// qmuidemo
|
||||
//
|
||||
// Created by Li Kai on 2018/11/3.
|
||||
// Copyright © 2018 QMUI Team. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "LookinDefines.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, LookinAppInfoDevice) {
|
||||
LookinAppInfoDeviceSimulator, // 模拟器
|
||||
LookinAppInfoDeviceIPad, // iPad 真机
|
||||
LookinAppInfoDeviceOthers // 应该视为 iPhone 真机
|
||||
};
|
||||
|
||||
@interface LookinAppInfo : NSObject <NSSecureCoding, NSCopying>
|
||||
|
||||
/// 每次启动 app 时都会随机生成一个 appInfoIdentifier 直到 app 被 kill 掉
|
||||
@property(nonatomic, assign) NSUInteger appInfoIdentifier;
|
||||
/// mac 端应该先读取该属性,如果为 YES 则表示应该使用之前保存的旧 appInfo 对象即可
|
||||
@property(nonatomic, assign) BOOL shouldUseCache;
|
||||
/// LookinServer 的版本
|
||||
@property(nonatomic, assign) int serverVersion;
|
||||
/// 类似 "1.1.9",只在 1.2.3 以及之后的 LookinServer 版本里有值
|
||||
@property(nonatomic, assign) NSString *serverReadableVersion;
|
||||
/// 如果 iOS 侧使用了 SPM 或引入了 Swift Subspec,则该属性为 1
|
||||
/// 如果 iOS 侧没使用,则该属性为 -1
|
||||
/// 如果不知道,则该属性为 0
|
||||
@property(nonatomic, assign) int swiftEnabledInLookinServer;
|
||||
/// app 的当前截图
|
||||
@property(nonatomic, strong) LookinImage *screenshot;
|
||||
/// 可能为 nil,比如新建的 iOS 空项目
|
||||
@property(nonatomic, strong) LookinImage *appIcon;
|
||||
/// @"微信读书"
|
||||
@property(nonatomic, copy) NSString *appName;
|
||||
/// hughkli.lookin
|
||||
@property(nonatomic, copy) NSString *appBundleIdentifier;
|
||||
/// @"iPhone X"
|
||||
@property(nonatomic, copy) NSString *deviceDescription;
|
||||
/// @"12.1"
|
||||
@property(nonatomic, copy) NSString *osDescription;
|
||||
/// 返回 os 的主版本号,比如 iOS 12.1 的设备将返回 12,iOS 13.2.1 的设备将返回 13
|
||||
@property(nonatomic, assign) NSUInteger osMainVersion;
|
||||
/// 设备类型
|
||||
@property(nonatomic, assign) LookinAppInfoDevice deviceType;
|
||||
/// 屏幕的宽度
|
||||
@property(nonatomic, assign) double screenWidth;
|
||||
/// 屏幕的高度
|
||||
@property(nonatomic, assign) double screenHeight;
|
||||
/// 是几倍的屏幕
|
||||
@property(nonatomic, assign) double screenScale;
|
||||
|
||||
- (BOOL)isEqualToAppInfo:(LookinAppInfo *)info;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
+ (LookinAppInfo *)currentInfoWithScreenshot:(BOOL)hasScreenshot icon:(BOOL)hasIcon localIdentifiers:(NSArray<NSNumber *> *)localIdentifiers;
|
||||
|
||||
#else
|
||||
|
||||
@property(nonatomic, assign) NSTimeInterval cachedTimestamp;
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
242
Pods/LookinServer/Src/Main/Shared/LookinAppInfo.m
generated
Normal file
242
Pods/LookinServer/Src/Main/Shared/LookinAppInfo.m
generated
Normal file
@@ -0,0 +1,242 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinAppInfo.m
|
||||
// qmuidemo
|
||||
//
|
||||
// Created by Li Kai on 2018/11/3.
|
||||
// Copyright © 2018 QMUI Team. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "LookinAppInfo.h"
|
||||
#import "LKS_MultiplatformAdapter.h"
|
||||
|
||||
static NSString * const CodingKey_AppIcon = @"1";
|
||||
static NSString * const CodingKey_Screenshot = @"2";
|
||||
static NSString * const CodingKey_DeviceDescription = @"3";
|
||||
static NSString * const CodingKey_OsDescription = @"4";
|
||||
static NSString * const CodingKey_AppName = @"5";
|
||||
static NSString * const CodingKey_ScreenWidth = @"6";
|
||||
static NSString * const CodingKey_ScreenHeight = @"7";
|
||||
static NSString * const CodingKey_DeviceType = @"8";
|
||||
|
||||
@implementation LookinAppInfo
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
LookinAppInfo *newAppInfo = [[LookinAppInfo allocWithZone:zone] init];
|
||||
newAppInfo.appIcon = self.appIcon;
|
||||
newAppInfo.appName = self.appName;
|
||||
newAppInfo.deviceDescription = self.deviceDescription;
|
||||
newAppInfo.osDescription = self.osDescription;
|
||||
newAppInfo.osMainVersion = self.osMainVersion;
|
||||
newAppInfo.deviceType = self.deviceType;
|
||||
newAppInfo.screenWidth = self.screenWidth;
|
||||
newAppInfo.screenHeight = self.screenHeight;
|
||||
newAppInfo.screenScale = self.screenScale;
|
||||
newAppInfo.appInfoIdentifier = self.appInfoIdentifier;
|
||||
return newAppInfo;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
if (self = [super init]) {
|
||||
|
||||
self.serverVersion = [aDecoder decodeIntForKey:@"serverVersion"];
|
||||
self.serverReadableVersion = [aDecoder decodeObjectForKey:@"serverReadableVersion"];
|
||||
self.swiftEnabledInLookinServer = [aDecoder decodeIntForKey:@"swiftEnabledInLookinServer"];
|
||||
NSData *screenshotData = [aDecoder decodeObjectForKey:CodingKey_Screenshot];
|
||||
self.screenshot = [[LookinImage alloc] initWithData:screenshotData];
|
||||
|
||||
NSData *appIconData = [aDecoder decodeObjectForKey:CodingKey_AppIcon];
|
||||
self.appIcon = [[LookinImage alloc] initWithData:appIconData];
|
||||
|
||||
self.appName = [aDecoder decodeObjectForKey:CodingKey_AppName];
|
||||
self.appBundleIdentifier = [aDecoder decodeObjectForKey:@"appBundleIdentifier"];
|
||||
self.deviceDescription = [aDecoder decodeObjectForKey:CodingKey_DeviceDescription];
|
||||
self.osDescription = [aDecoder decodeObjectForKey:CodingKey_OsDescription];
|
||||
self.osMainVersion = [aDecoder decodeIntegerForKey:@"osMainVersion"];
|
||||
self.deviceType = [aDecoder decodeIntegerForKey:CodingKey_DeviceType];
|
||||
self.screenWidth = [aDecoder decodeDoubleForKey:CodingKey_ScreenWidth];
|
||||
self.screenHeight = [aDecoder decodeDoubleForKey:CodingKey_ScreenHeight];
|
||||
self.screenScale = [aDecoder decodeDoubleForKey:@"screenScale"];
|
||||
self.appInfoIdentifier = [aDecoder decodeIntegerForKey:@"appInfoIdentifier"];
|
||||
self.shouldUseCache = [aDecoder decodeBoolForKey:@"shouldUseCache"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeInt:self.serverVersion forKey:@"serverVersion"];
|
||||
[aCoder encodeObject:self.serverReadableVersion forKey:@"serverReadableVersion"];
|
||||
[aCoder encodeInt:self.swiftEnabledInLookinServer forKey:@"swiftEnabledInLookinServer"];
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
NSData *screenshotData = UIImagePNGRepresentation(self.screenshot);
|
||||
[aCoder encodeObject:screenshotData forKey:CodingKey_Screenshot];
|
||||
|
||||
NSData *appIconData = UIImagePNGRepresentation(self.appIcon);
|
||||
[aCoder encodeObject:appIconData forKey:CodingKey_AppIcon];
|
||||
#elif TARGET_OS_MAC
|
||||
NSData *screenshotData = [self.screenshot TIFFRepresentation];
|
||||
[aCoder encodeObject:screenshotData forKey:CodingKey_Screenshot];
|
||||
|
||||
NSData *appIconData = [self.appIcon TIFFRepresentation];
|
||||
[aCoder encodeObject:appIconData forKey:CodingKey_AppIcon];
|
||||
#endif
|
||||
|
||||
[aCoder encodeObject:self.appName forKey:CodingKey_AppName];
|
||||
[aCoder encodeObject:self.appBundleIdentifier forKey:@"appBundleIdentifier"];
|
||||
[aCoder encodeObject:self.deviceDescription forKey:CodingKey_DeviceDescription];
|
||||
[aCoder encodeObject:self.osDescription forKey:CodingKey_OsDescription];
|
||||
[aCoder encodeInteger:self.osMainVersion forKey:@"osMainVersion"];
|
||||
[aCoder encodeInteger:self.deviceType forKey:CodingKey_DeviceType];
|
||||
[aCoder encodeDouble:self.screenWidth forKey:CodingKey_ScreenWidth];
|
||||
[aCoder encodeDouble:self.screenHeight forKey:CodingKey_ScreenHeight];
|
||||
[aCoder encodeDouble:self.screenScale forKey:@"screenScale"];
|
||||
[aCoder encodeInteger:self.appInfoIdentifier forKey:@"appInfoIdentifier"];
|
||||
[aCoder encodeBool:self.shouldUseCache forKey:@"shouldUseCache"];
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
if (![object isKindOfClass:[LookinAppInfo class]]) {
|
||||
return NO;
|
||||
}
|
||||
if ([self isEqualToAppInfo:object]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return self.appName.hash ^ self.deviceDescription.hash ^ self.osDescription.hash ^ self.deviceType;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToAppInfo:(LookinAppInfo *)info {
|
||||
if (!info) {
|
||||
return NO;
|
||||
}
|
||||
if ([self.appName isEqualToString:info.appName] && [self.deviceDescription isEqualToString:info.deviceDescription] && [self.osDescription isEqualToString:info.osDescription] && self.deviceType == info.deviceType) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
+ (LookinAppInfo *)currentInfoWithScreenshot:(BOOL)hasScreenshot icon:(BOOL)hasIcon localIdentifiers:(NSArray<NSNumber *> *)localIdentifiers {
|
||||
NSInteger selfIdentifier = [self getAppInfoIdentifier];
|
||||
if ([localIdentifiers containsObject:@(selfIdentifier)]) {
|
||||
LookinAppInfo *info = [LookinAppInfo new];
|
||||
info.appInfoIdentifier = selfIdentifier;
|
||||
info.shouldUseCache = YES;
|
||||
return info;
|
||||
}
|
||||
|
||||
LookinAppInfo *info = [[LookinAppInfo alloc] init];
|
||||
info.serverReadableVersion = LOOKIN_SERVER_READABLE_VERSION;
|
||||
#ifdef LOOKIN_SERVER_SWIFT_ENABLED
|
||||
info.swiftEnabledInLookinServer = 1;
|
||||
#else
|
||||
info.swiftEnabledInLookinServer = -1;
|
||||
#endif
|
||||
info.appInfoIdentifier = selfIdentifier;
|
||||
info.appName = [self appName];
|
||||
info.deviceDescription = [UIDevice currentDevice].name;
|
||||
info.appBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if ([self isSimulator]) {
|
||||
info.deviceType = LookinAppInfoDeviceSimulator;
|
||||
} else if ([LKS_MultiplatformAdapter isiPad]) {
|
||||
info.deviceType = LookinAppInfoDeviceIPad;
|
||||
} else {
|
||||
info.deviceType = LookinAppInfoDeviceOthers;
|
||||
}
|
||||
|
||||
info.osDescription = [UIDevice currentDevice].systemVersion;
|
||||
|
||||
NSString *mainVersionStr = [[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."].firstObject;
|
||||
info.osMainVersion = [mainVersionStr integerValue];
|
||||
|
||||
CGSize screenSize = [LKS_MultiplatformAdapter mainScreenBounds].size;
|
||||
info.screenWidth = screenSize.width;
|
||||
info.screenHeight = screenSize.height;
|
||||
info.screenScale = [LKS_MultiplatformAdapter mainScreenScale];
|
||||
|
||||
if (hasScreenshot) {
|
||||
info.screenshot = [self screenshotImage];
|
||||
}
|
||||
if (hasIcon) {
|
||||
info.appIcon = [self appIcon];
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (NSString *)appName {
|
||||
NSDictionary *info = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString *displayName = [info objectForKey:@"CFBundleDisplayName"];
|
||||
NSString *name = [info objectForKey:@"CFBundleName"];
|
||||
return displayName.length ? displayName : name;
|
||||
}
|
||||
|
||||
+ (UIImage *)appIcon {
|
||||
#if TARGET_OS_TV
|
||||
return nil;
|
||||
#else
|
||||
NSString *imageName = [[[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIcons"] objectForKey:@"CFBundlePrimaryIcon"] objectForKey:@"CFBundleIconFiles"] lastObject];
|
||||
if (!imageName.length) {
|
||||
// 正常情况下拿到的 name 可能比如 “AppIcon60x60”。但某些情况可能为 nil,此时直接 return 否则 [UIImage imageNamed:nil] 可能导致 console 报 "CUICatalog: Invalid asset name supplied: '(null)'" 的错误信息
|
||||
return nil;
|
||||
}
|
||||
return [UIImage imageNamed:imageName];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (UIImage *)screenshotImage {
|
||||
UIWindow *window = [LKS_MultiplatformAdapter keyWindow];
|
||||
if (!window) {
|
||||
return nil;
|
||||
}
|
||||
CGSize size = window.bounds.size;
|
||||
if (size.width <= 0 || size.height <= 0) {
|
||||
// *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIGraphicsBeginImageContext() failed to allocate CGBitampContext: size={0, 0}, scale=3.000000, bitmapInfo=0x2002. Use UIGraphicsImageRenderer to avoid this assert.'
|
||||
|
||||
// https://github.com/hughkli/Lookin/issues/21
|
||||
return nil;
|
||||
}
|
||||
UIGraphicsBeginImageContextWithOptions(size, YES, 0.4);
|
||||
[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (BOOL)isSimulator {
|
||||
if (TARGET_OS_SIMULATOR) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (NSInteger)getAppInfoIdentifier {
|
||||
static dispatch_once_t onceToken;
|
||||
static NSInteger identifier = 0;
|
||||
dispatch_once(&onceToken,^{
|
||||
identifier = [[NSDate date] timeIntervalSince1970];
|
||||
});
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
257
Pods/LookinServer/Src/Main/Shared/LookinAttrIdentifiers.h
generated
Normal file
257
Pods/LookinServer/Src/Main/Shared/LookinAttrIdentifiers.h
generated
Normal file
@@ -0,0 +1,257 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinAttrIdentifiers.h
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/9/18.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#pragma mark - Group
|
||||
|
||||
typedef NSString * LookinAttrGroupIdentifier;
|
||||
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_None;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_Class;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_Relation;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_Layout;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_AutoLayout;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_ViewLayer;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UIImageView;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UILabel;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UIControl;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UIButton;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UIScrollView;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UITableView;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UITextView;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UITextField;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UIVisualEffectView;
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UIStackView;
|
||||
|
||||
extern LookinAttrGroupIdentifier const LookinAttrGroup_UserCustom;
|
||||
|
||||
#pragma mark - Section
|
||||
|
||||
typedef NSString * LookinAttrSectionIdentifier;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_None;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UserCustom;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Class_Class;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Relation_Relation;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Layout_Frame;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Layout_Bounds;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Layout_SafeArea;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Layout_Position;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_Layout_AnchorPoint;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_Hugging;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_Resistance;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_Constraints;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_IntrinsicSize;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Visibility;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_InterationAndMasks;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Corner;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_BgColor;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Border;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Shadow;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_ContentMode;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_TintColor;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Tag;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIImageView_Name;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIImageView_Open;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_Text;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_Font;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_NumberOfLines;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_TextColor;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_BreakMode;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_Alignment;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UILabel_CanAdjustFont;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIControl_EnabledSelected;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIControl_VerAlignment;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIControl_HorAlignment;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIControl_QMUIOutsideEdge;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIButton_ContentInsets;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIButton_TitleInsets;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIButton_ImageInsets;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ContentInset;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_AdjustedInset;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_IndicatorInset;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Offset;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ContentSize;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Behavior;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ShowsIndicator;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Bounce;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ScrollPaging;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ContentTouches;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Zoom;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_QMUIInitialInset;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITableView_Style;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SectionsNumber;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITableView_RowsNumber;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SeparatorStyle;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SeparatorColor;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SeparatorInset;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Basic;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Text;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Font;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextView_TextColor;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Alignment;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextView_ContainerInset;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Text;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Placeholder;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Font;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_TextColor;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Alignment;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Clears;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_CanAdjustFont;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UITextField_ClearButtonMode;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIVisualEffectView_Style;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIVisualEffectView_QMUIForegroundColor;
|
||||
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Axis;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Distribution;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Alignment;
|
||||
extern LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Spacing;
|
||||
|
||||
#pragma mark - Attr
|
||||
|
||||
typedef NSString * LookinAttrIdentifier;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_None;
|
||||
|
||||
/// 用户自定义的
|
||||
extern LookinAttrIdentifier const LookinAttr_UserCustom;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_Class_Class_Class;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_Relation_Relation_Relation;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_Layout_Frame_Frame;
|
||||
extern LookinAttrIdentifier const LookinAttr_Layout_Bounds_Bounds;
|
||||
extern LookinAttrIdentifier const LookinAttr_Layout_SafeArea_SafeArea;
|
||||
extern LookinAttrIdentifier const LookinAttr_Layout_Position_Position;
|
||||
extern LookinAttrIdentifier const LookinAttr_Layout_AnchorPoint_AnchorPoint;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_AutoLayout_Hugging_Hor;
|
||||
extern LookinAttrIdentifier const LookinAttr_AutoLayout_Hugging_Ver;
|
||||
extern LookinAttrIdentifier const LookinAttr_AutoLayout_Resistance_Hor;
|
||||
extern LookinAttrIdentifier const LookinAttr_AutoLayout_Resistance_Ver;
|
||||
extern LookinAttrIdentifier const LookinAttr_AutoLayout_Constraints_Constraints;
|
||||
extern LookinAttrIdentifier const LookinAttr_AutoLayout_IntrinsicSize_Size;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Visibility_Hidden;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Visibility_Opacity;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_InterationAndMasks_Interaction;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_InterationAndMasks_MasksToBounds;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Corner_Radius;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_BgColor_BgColor;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Border_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Border_Width;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_Opacity;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_Radius;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_OffsetW;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_OffsetH;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_ContentMode_Mode;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_TintColor_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_TintColor_Mode;
|
||||
extern LookinAttrIdentifier const LookinAttr_ViewLayer_Tag_Tag;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UIImageView_Name_Name;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIImageView_Open_Open;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_Text_Text;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_Font_Name;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_Font_Size;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_NumberOfLines_NumberOfLines;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_TextColor_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_Alignment_Alignment;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_BreakMode_Mode;
|
||||
extern LookinAttrIdentifier const LookinAttr_UILabel_CanAdjustFont_CanAdjustFont;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UIControl_EnabledSelected_Enabled;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIControl_EnabledSelected_Selected;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIControl_VerAlignment_Alignment;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIControl_HorAlignment_Alignment;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIControl_QMUIOutsideEdge_Edge;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UIButton_ContentInsets_Insets;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIButton_TitleInsets_Insets;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIButton_ImageInsets_Insets;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Offset_Offset;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ContentSize_Size;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ContentInset_Inset;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_AdjustedInset_Inset;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Behavior_Behavior;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_IndicatorInset_Inset;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ScrollPaging_ScrollEnabled;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ScrollPaging_PagingEnabled;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Bounce_Ver;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Bounce_Hor;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ShowsIndicator_Hor;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ShowsIndicator_Ver;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ContentTouches_Delay;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_ContentTouches_CanCancel;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_MinScale;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_MaxScale;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_Scale;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_Bounce;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIScrollView_QMUIInitialInset_Inset;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UITableView_Style_Style;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITableView_SectionsNumber_Number;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITableView_RowsNumber_Number;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITableView_SeparatorInset_Inset;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITableView_SeparatorColor_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITableView_SeparatorStyle_Style;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_Font_Name;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_Font_Size;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_Basic_Editable;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_Basic_Selectable;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_Text_Text;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_TextColor_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_Alignment_Alignment;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextView_ContainerInset_Inset;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Text_Text;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Placeholder_Placeholder;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Font_Name;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Font_Size;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_TextColor_Color;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Alignment_Alignment;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Clears_ClearsOnBeginEditing;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_Clears_ClearsOnInsertion;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_CanAdjustFont_CanAdjustFont;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_CanAdjustFont_MinSize;
|
||||
extern LookinAttrIdentifier const LookinAttr_UITextField_ClearButtonMode_Mode;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UIVisualEffectView_Style_Style;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIVisualEffectView_QMUIForegroundColor_Color;
|
||||
|
||||
extern LookinAttrIdentifier const LookinAttr_UIStackView_Axis_Axis;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIStackView_Distribution_Distribution;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIStackView_Alignment_Alignment;
|
||||
extern LookinAttrIdentifier const LookinAttr_UIStackView_Spacing_Spacing;
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
253
Pods/LookinServer/Src/Main/Shared/LookinAttrIdentifiers.m
generated
Normal file
253
Pods/LookinServer/Src/Main/Shared/LookinAttrIdentifiers.m
generated
Normal file
@@ -0,0 +1,253 @@
|
||||
#ifdef SHOULD_COMPILE_LOOKIN_SERVER
|
||||
|
||||
//
|
||||
// LookinAttrIdentifiers.m
|
||||
// Lookin
|
||||
//
|
||||
// Created by Li Kai on 2019/9/18.
|
||||
// https://lookin.work
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "LookinAttrIdentifiers.h"
|
||||
|
||||
// value 不能重复(AppDelegate 里的 runTests 有相关 test)
|
||||
// 如果要去掉某一项可以考虑注释掉而非直接删除,以防止新项和旧项的 value 相同而引发 preference 错乱(这些 value 会被存储到 userDefaults 里)
|
||||
|
||||
#pragma mark - Group
|
||||
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_None = @"n";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_Class = @"c";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_Relation = @"r";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_Layout = @"l";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_AutoLayout = @"a";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_ViewLayer = @"vl";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UIImageView = @"i";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UILabel = @"la";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UIControl = @"co";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UIButton = @"b";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UIScrollView = @"s";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UITableView = @"ta";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UITextView = @"te";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UITextField = @"tf";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UIVisualEffectView = @"ve";
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UIStackView = @"UIStackView";
|
||||
|
||||
LookinAttrGroupIdentifier const LookinAttrGroup_UserCustom = @"guc"; // user custom
|
||||
|
||||
#pragma mark - Section
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_None = @"n";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UserCustom = @"sec_ctm";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Class_Class = @"cl_c";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Relation_Relation = @"r_r";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Layout_Frame = @"l_f";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Layout_Bounds = @"l_b";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Layout_SafeArea = @"l_s";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Layout_Position = @"l_p";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_Layout_AnchorPoint = @"l_a";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_Hugging = @"a_h";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_Resistance = @"a_r";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_Constraints = @"a_c";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_AutoLayout_IntrinsicSize = @"a_i";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Visibility = @"v_v";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_InterationAndMasks = @"v_i";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Corner = @"v_c";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_BgColor = @"v_b";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Border = @"v_bo";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Shadow = @"v_s";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_ContentMode = @"v_co";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_TintColor = @"v_t";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_ViewLayer_Tag = @"v_ta";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIImageView_Name = @"i_n";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIImageView_Open = @"i_o";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_Text = @"lb_t";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_Font = @"lb_f";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_NumberOfLines = @"lb_n";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_TextColor = @"lb_tc";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_BreakMode = @"lb_b";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_Alignment = @"lb_a";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UILabel_CanAdjustFont = @"lb_c";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIControl_EnabledSelected = @"c_e";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIControl_VerAlignment = @"c_v";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIControl_HorAlignment = @"c_h";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIControl_QMUIOutsideEdge = @"c_o";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIButton_ContentInsets = @"b_c";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIButton_TitleInsets = @"b_t";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIButton_ImageInsets = @"b_i";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ContentInset = @"s_c";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_AdjustedInset = @"s_a";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_IndicatorInset = @"s_i";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Offset = @"s_o";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ContentSize = @"s_cs";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Behavior = @"s_b";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ShowsIndicator = @"s_si";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Bounce = @"s_bo";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ScrollPaging = @"s_s";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_ContentTouches = @"s_ct";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_Zoom = @"s_z";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIScrollView_QMUIInitialInset = @"s_ii";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITableView_Style = @"t_s";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SectionsNumber = @"t_sn";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITableView_RowsNumber = @"t_r";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SeparatorStyle = @"t_ss";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SeparatorColor = @"t_sc";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITableView_SeparatorInset = @"t_si";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Basic = @"tv_b";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Text = @"tv_t";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Font = @"tv_f";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextView_TextColor = @"tv_tc";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextView_Alignment = @"tv_a";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextView_ContainerInset = @"tv_c";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Text = @"tf_t";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Placeholder = @"tf_p";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Font = @"tf_f";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_TextColor = @"tf_tc";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Alignment = @"tf_a";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_Clears = @"tf_c";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_CanAdjustFont = @"tf_ca";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UITextField_ClearButtonMode = @"tf_cb";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIVisualEffectView_Style = @"ve_s";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIVisualEffectView_QMUIForegroundColor = @"ve_f";
|
||||
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Axis = @"usv_axis";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Distribution = @"usv_dis";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Alignment = @"usv_align";
|
||||
LookinAttrSectionIdentifier const LookinAttrSec_UIStackView_Spacing = @"usv_spa";
|
||||
|
||||
#pragma mark - Attr
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_None = @"n";
|
||||
LookinAttrIdentifier const LookinAttr_UserCustom = @"ctm";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_Class_Class_Class = @"c_c_c";
|
||||
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_Relation_Relation_Relation = @"r_r_r";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_Layout_Frame_Frame = @"l_f_f";
|
||||
LookinAttrIdentifier const LookinAttr_Layout_Bounds_Bounds = @"l_b_b";
|
||||
LookinAttrIdentifier const LookinAttr_Layout_SafeArea_SafeArea = @"l_s_s";
|
||||
LookinAttrIdentifier const LookinAttr_Layout_Position_Position = @"l_p_p";
|
||||
LookinAttrIdentifier const LookinAttr_Layout_AnchorPoint_AnchorPoint = @"l_a_a";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_AutoLayout_Hugging_Hor = @"al_h_h";
|
||||
LookinAttrIdentifier const LookinAttr_AutoLayout_Hugging_Ver = @"al_h_v";
|
||||
LookinAttrIdentifier const LookinAttr_AutoLayout_Resistance_Hor = @"al_r_h";
|
||||
LookinAttrIdentifier const LookinAttr_AutoLayout_Resistance_Ver = @"al_r_v";
|
||||
LookinAttrIdentifier const LookinAttr_AutoLayout_Constraints_Constraints = @"al_c_c";
|
||||
LookinAttrIdentifier const LookinAttr_AutoLayout_IntrinsicSize_Size = @"cl_i_s";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Visibility_Hidden = @"vl_v_h";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Visibility_Opacity = @"vl_v_o";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_InterationAndMasks_Interaction = @"vl_i_i";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_InterationAndMasks_MasksToBounds = @"vl_i_m";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Corner_Radius = @"vl_c_r";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_BgColor_BgColor = @"vl_b_b";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Border_Color = @"vl_b_c";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Border_Width = @"vl_b_w";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_Color = @"vl_s_c";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_Opacity = @"vl_s_o";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_Radius = @"vl_s_r";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_OffsetW = @"vl_s_ow";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Shadow_OffsetH = @"vl_s_oh";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_ContentMode_Mode = @"vl_c_m";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_TintColor_Color = @"vl_t_c";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_TintColor_Mode = @"vl_t_m";
|
||||
LookinAttrIdentifier const LookinAttr_ViewLayer_Tag_Tag = @"vl_t_t";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UIImageView_Name_Name = @"iv_n_n";
|
||||
LookinAttrIdentifier const LookinAttr_UIImageView_Open_Open = @"iv_o_o";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_Text_Text = @"lb_t_t";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_Font_Name = @"lb_f_n";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_Font_Size = @"lb_f_s";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_NumberOfLines_NumberOfLines = @"lb_n_n";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_TextColor_Color = @"lb_t_c";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_Alignment_Alignment = @"lb_a_a";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_BreakMode_Mode = @"lb_b_m";
|
||||
LookinAttrIdentifier const LookinAttr_UILabel_CanAdjustFont_CanAdjustFont = @"lb_c_c";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UIControl_EnabledSelected_Enabled = @"ct_e_e";
|
||||
LookinAttrIdentifier const LookinAttr_UIControl_EnabledSelected_Selected = @"ct_e_s";
|
||||
LookinAttrIdentifier const LookinAttr_UIControl_VerAlignment_Alignment = @"ct_v_a";
|
||||
LookinAttrIdentifier const LookinAttr_UIControl_HorAlignment_Alignment = @"ct_h_a";
|
||||
LookinAttrIdentifier const LookinAttr_UIControl_QMUIOutsideEdge_Edge = @"ct_o_e";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UIButton_ContentInsets_Insets = @"bt_c_i";
|
||||
LookinAttrIdentifier const LookinAttr_UIButton_TitleInsets_Insets = @"bt_t_i";
|
||||
LookinAttrIdentifier const LookinAttr_UIButton_ImageInsets_Insets = @"bt_i_i";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Offset_Offset = @"sv_o_o";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ContentSize_Size = @"sv_c_s";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ContentInset_Inset = @"sv_c_i";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_AdjustedInset_Inset = @"sv_a_i";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Behavior_Behavior = @"sv_b_b";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_IndicatorInset_Inset = @"sv_i_i";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ScrollPaging_ScrollEnabled = @"sv_s_s";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ScrollPaging_PagingEnabled = @"sv_s_p";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Bounce_Ver = @"sv_b_v";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Bounce_Hor = @"sv_b_h";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ShowsIndicator_Hor = @"sv_h_h";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ShowsIndicator_Ver = @"sv_s_v";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ContentTouches_Delay = @"sv_c_d";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_ContentTouches_CanCancel = @"sv_c_c";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_MinScale = @"sv_z_mi";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_MaxScale = @"sv_z_ma";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_Scale = @"sv_z_s";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_Zoom_Bounce = @"sv_z_b";
|
||||
LookinAttrIdentifier const LookinAttr_UIScrollView_QMUIInitialInset_Inset = @"sv_qi_i";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UITableView_Style_Style = @"tv_s_s";
|
||||
LookinAttrIdentifier const LookinAttr_UITableView_SectionsNumber_Number = @"tv_s_n";
|
||||
LookinAttrIdentifier const LookinAttr_UITableView_RowsNumber_Number = @"tv_r_n";
|
||||
LookinAttrIdentifier const LookinAttr_UITableView_SeparatorInset_Inset = @"tv_s_i";
|
||||
LookinAttrIdentifier const LookinAttr_UITableView_SeparatorColor_Color = @"tv_s_c";
|
||||
LookinAttrIdentifier const LookinAttr_UITableView_SeparatorStyle_Style = @"tv_ss_s";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_Font_Name = @"te_f_n";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_Font_Size = @"te_f_s";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_Basic_Editable = @"te_b_e";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_Basic_Selectable = @"te_b_s";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_Text_Text = @"te_t_t";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_TextColor_Color = @"te_t_c";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_Alignment_Alignment = @"te_a_a";
|
||||
LookinAttrIdentifier const LookinAttr_UITextView_ContainerInset_Inset = @"te_c_i";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Text_Text = @"tf_t_t";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Placeholder_Placeholder = @"tf_p_p";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Font_Name = @"tf_f_n";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Font_Size = @"tf_f_s";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_TextColor_Color = @"tf_t_c";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Alignment_Alignment = @"tf_a_a";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Clears_ClearsOnBeginEditing = @"tf_c_c";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_Clears_ClearsOnInsertion = @"tf_c_co";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_CanAdjustFont_CanAdjustFont = @"tf_c_ca";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_CanAdjustFont_MinSize = @"tf_c_m";
|
||||
LookinAttrIdentifier const LookinAttr_UITextField_ClearButtonMode_Mode = @"tf_cb_m";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UIVisualEffectView_Style_Style = @"ve_s_s";
|
||||
LookinAttrIdentifier const LookinAttr_UIVisualEffectView_QMUIForegroundColor_Color = @"ve_f_c";
|
||||
|
||||
LookinAttrIdentifier const LookinAttr_UIStackView_Axis_Axis = @"usv_axis_axis";
|
||||
LookinAttrIdentifier const LookinAttr_UIStackView_Distribution_Distribution = @"usv_dis_dis";
|
||||
LookinAttrIdentifier const LookinAttr_UIStackView_Alignment_Alignment = @"usv_ali_ali";
|
||||
LookinAttrIdentifier const LookinAttr_UIStackView_Spacing_Spacing = @"usv_spa_spa";
|
||||
|
||||
#endif /* SHOULD_COMPILE_LOOKIN_SERVER */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user