This commit is contained in:
2025-10-28 10:18:10 +08:00
parent efb04d134e
commit 1deca2ae5b
166 changed files with 17288 additions and 1427 deletions

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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];
}
// 0F0F
+ (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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */