小说绘上架版本

This commit is contained in:
xtfei2011
2021-02-07 11:24:08 +08:00
commit ee5c1c8b12
1762 changed files with 115892 additions and 0 deletions
@@ -0,0 +1,19 @@
//
// CAGradientLayer+Crash.h
// WXReader
//
// Created by Andrew on 2019/6/25.
// Copyright © 2019 Andrew. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
NS_ASSUME_NONNULL_BEGIN
@interface CAGradientLayer (Crash)
- (void)_clearContents;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,18 @@
//
// CAGradientLayer+Crash.m
// WXReader
//
// Created by Andrew on 2019/6/25.
// Copyright © 2019 Andrew. All rights reserved.
//
#import "CAGradientLayer+Crash.h"
@implementation CAGradientLayer (Crash)
- (void)_clearContents
{
}
@end
@@ -0,0 +1,206 @@
#import <Foundation/Foundation.h>
#ifndef Release
@implementation NSSet(Log)
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *desc = [NSMutableString string];
NSMutableString *tabString = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[tabString appendString:@"\t"];
}
NSString *tab = @"\t";
if (level > 0) {
tab = tabString;
}
[desc appendString:@"\t{(\n"];
for (id obj in self) {
if ([obj isKindOfClass:[NSDictionary class]]
|| [obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)obj) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, obj];
} else if ([obj isKindOfClass:[NSData class]]) {
// if is NSDatatry parse
NSError *error = nil;
NSObject *result = [NSJSONSerialization JSONObjectWithData:obj
options:NSJSONReadingMutableContainers
error:&error];
if (error == nil && result != nil) {
if ([result isKindOfClass:[NSDictionary class]]
|| [result isKindOfClass:[NSArray class]]
|| [result isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)result) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, result];
}
} else {
@try {
NSString *str = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
if (str != nil) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, str];
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
@catch (NSException *exception) {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
[desc appendFormat:@"%@)}", tab];
return desc;
}
@end
@implementation NSArray (Log)
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *desc = [NSMutableString string];
NSMutableString *tabString = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[tabString appendString:@"\t"];
}
NSString *tab = @"";
if (level > 0) {
tab = tabString;
}
[desc appendString:@"\t(\n"];
for (id obj in self) {
if ([obj isKindOfClass:[NSDictionary class]]
|| [obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)obj) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, obj];
} else if ([obj isKindOfClass:[NSData class]]) {
NSError *error = nil;
NSObject *result = [NSJSONSerialization JSONObjectWithData:obj
options:NSJSONReadingMutableContainers
error:&error];
if (error == nil && result != nil) {
if ([result isKindOfClass:[NSDictionary class]]
|| [result isKindOfClass:[NSArray class]]
|| [result isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)result) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@,\n", tab, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, result];
}
} else {
@try {
NSString *str = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
if (str != nil) {
[desc appendFormat:@"%@\t\"%@\",\n", tab, str];
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
@catch (NSException *exception) {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
} else {
[desc appendFormat:@"%@\t%@,\n", tab, obj];
}
}
[desc appendFormat:@"%@)", tab];
return desc;
}
@end
@implementation NSDictionary (Log)
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *desc = [NSMutableString string];
NSMutableString *tabString = [[NSMutableString alloc] initWithCapacity:level];
for (NSUInteger i = 0; i < level; ++i) {
[tabString appendString:@"\t"];
}
NSString *tab = @"";
if (level > 0) {
tab = tabString;
}
[desc appendString:@"\t{\n"];
// Through array, self is array
for (id key in self.allKeys) {
id obj = [self objectForKey:key];
if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t%@ = \"%@\",\n", tab, key, obj];
} else if ([obj isKindOfClass:[NSArray class]]
|| [obj isKindOfClass:[NSDictionary class]]
|| [obj isKindOfClass:[NSSet class]]) {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, [obj descriptionWithLocale:locale indent:level + 1]];
} else if ([obj isKindOfClass:[NSData class]]) {
NSError *error = nil;
NSObject *result = [NSJSONSerialization JSONObjectWithData:obj
options:NSJSONReadingMutableContainers
error:&error];
if (error == nil && result != nil) {
if ([result isKindOfClass:[NSDictionary class]]
|| [result isKindOfClass:[NSArray class]]
|| [result isKindOfClass:[NSSet class]]) {
NSString *str = [((NSDictionary *)result) descriptionWithLocale:locale indent:level + 1];
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, str];
} else if ([obj isKindOfClass:[NSString class]]) {
[desc appendFormat:@"%@\t%@ = \"%@\",\n", tab, key, result];
}
} else {
@try {
NSString *str = [[NSString alloc] initWithData:obj encoding:NSUTF8StringEncoding];
if (str != nil) {
[desc appendFormat:@"%@\t%@ = \"%@\",\n", tab, key, str];
} else {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, obj];
}
}
@catch (NSException *exception) {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, obj];
}
}
} else {
[desc appendFormat:@"%@\t%@ = %@,\n", tab, key, obj];
}
}
[desc appendFormat:@"%@}", tab];
return desc;
}
@end
#endif
@@ -0,0 +1,16 @@
//
// NSAttributedString+TReaderPage.h
// Examda
//
// Created by tanyang on 16/1/26.
// Copyright © 2016年 tanyang. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NSAttributedString (TReaderPage)
// 根据渲染图文大小分页,返回range数组
- (NSArray *)pageRangeArrayWithConstrainedToSize:(CGSize)size;
@end
@@ -0,0 +1,46 @@
//
// NSAttributedString+TReaderPage.m
// Examda
//
// Created by tanyang on 16/1/26.
// Copyright © 2016年 tanyang. All rights reserved.
//
#import "NSAttributedString+TReaderPage.h"
#import <CoreText/CoreText.h>
@implementation NSAttributedString (TReaderPage)
//根据指定的大小,对字符串进行分页,计算出每页显示的字符串区间(NSRange)
- (NSArray *)pageRangeArrayWithConstrainedToSize:(CGSize)size
{
NSAttributedString *attributedString = self;
NSMutableArray * resultRange = [NSMutableArray array];
CGRect rect = CGRectMake(0, 0, size.width, size.height);
NSInteger rangeIndex = 0;
do {
NSUInteger length = MIN(1500, attributedString.length - rangeIndex);
NSAttributedString * childString = [attributedString attributedSubstringFromRange:NSMakeRange(rangeIndex, length)];
CTFramesetterRef childFramesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) childString);
UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:rect];
CTFrameRef frame = CTFramesetterCreateFrame(childFramesetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL);
CFRange range = CTFrameGetVisibleStringRange(frame);
NSRange r = {rangeIndex, range.length};
if (r.length > 0) {
[resultRange addObject:[NSValue valueWithRange:r]];
}
if (r.length == 0) {
rangeIndex ++;
} else {
rangeIndex += r.length;
}
CFRelease(frame);
CFRelease(childFramesetter);
} while (rangeIndex < attributedString.length && attributedString.length > 0);
return resultRange;
}
@end
@@ -0,0 +1,46 @@
//
// NSMutableArray+KVO.h
// iOSHelper
//
// Created by Chair on 2020/3/5.
// Copyright © 2020 Chair. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// 当数组的元素发生改变时回调
@interface NSMutableArray (KVO)
@property (nonatomic, copy) void(^changeBlock)(NSMutableArray *newVal);
- (void)KVO_addObject:(id)object;
- (void)KVO_insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)KVO_removeLastObject;
- (void)KVO_removeObjectAtIndex:(NSUInteger)index;
- (void)KVO_replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
- (void)KVO_addObjectsFromArray:(NSArray<id> *)otherArray;
- (void)KVO_exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2;
- (void)KVO_removeAllObjects;
- (void)KVO_removeObject:(id)anObject inRange:(NSRange)range;
- (void)KVO_removeObject:(id)anObject;
- (void)KVO_removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range;
- (void)KVO_removeObjectIdenticalTo:(id)anObject;
- (void)KVO_removeObjectsFromIndices:(NSUInteger *)indices numIndices:(NSUInteger)cnt API_DEPRECATED("Not supported", macos(10.0,10.6), ios(2.0,4.0), watchos(2.0,2.0), tvos(9.0,9.0));
- (void)KVO_removeObjectsInArray:(NSArray<id> *)otherArray;
- (void)KVO_removeObjectsInRange:(NSRange)range;
- (void)KVO_replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray<id> *)otherArray range:(NSRange)otherRange;
- (void)KVO_replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray<id> *)otherArray;
- (void)KVO_setArray:(NSArray<id> *)otherArray;
- (void)KVO_insertObjects:(NSArray<id> *)objects atIndexes:(NSIndexSet *)indexes;
- (void)KVO_removeObjectsAtIndexes:(NSIndexSet *)indexes;
- (void)KVO_replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray<id> *)objects;
- (void)KVO_setObject:(id)obj atIndexedSubscript:(NSUInteger)idx API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,152 @@
//
// NSMutableArray+KVO.m
// iOSHelper
//
// Created by Chair on 2020/3/5.
// Copyright © 2020 Chair. All rights reserved.
//
#import "NSMutableArray+KVO.h"
#import <objc/runtime.h>
static const char * identifier = "identifier";
@implementation NSMutableArray (KVO)
- (void)setChangeBlock:(void (^)(NSMutableArray * _Nonnull))changeBlock {
if (!changeBlock) return ;
objc_setAssociatedObject(self, identifier, changeBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void (^)(NSMutableArray * _Nonnull))changeBlock {
return objc_getAssociatedObject(self, identifier);
}
- (void)KVO_addObject:(id)object {
if (object == nil || [object isKindOfClass:NSNull.class]) return ;
[self addObject:object];
self.changeBlock(self);
}
- (void)KVO_insertObject:(id)anObject atIndex:(NSUInteger)index {
if (anObject == nil || [anObject isKindOfClass:NSNull.class]) return ;
[self insertObject:anObject atIndex:index];
self.changeBlock(self);
}
- (void)KVO_removeLastObject {
[self removeLastObject];
self.changeBlock(self);
}
- (void)KVO_removeObjectAtIndex:(NSUInteger)index {
[self removeObjectAtIndex:index];
self.changeBlock(self);
}
- (void)KVO_replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
if (anObject == nil || [anObject isKindOfClass:NSNull.class]) return ;
[self replaceObjectAtIndex:index withObject:anObject];
self.changeBlock(self);
}
- (void)KVO_addObjectsFromArray:(NSArray<id> *)otherArray {
if (![otherArray isKindOfClass:NSArray.class]) return ;
[self addObjectsFromArray:otherArray];
self.changeBlock(self);
}
- (void)KVO_exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2 {
[self exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2];
self.changeBlock(self);
}
- (void)KVO_removeAllObjects {
[self removeAllObjects];
self.changeBlock(self);
}
- (void)KVO_removeObject:(id)anObject inRange:(NSRange)range {
if (anObject == nil || [anObject isKindOfClass:NSNull.class]) return ;
[self removeObject:anObject inRange:range];
self.changeBlock(self);
}
- (void)KVO_removeObject:(id)anObject {
if (anObject == nil || [anObject isKindOfClass:NSNull.class]) return ;
[self removeObject:anObject];
self.changeBlock(self);
}
- (void)KVO_removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range {
if (anObject == nil || [anObject isKindOfClass:NSNull.class]) return ;
[self removeObjectIdenticalTo:anObject inRange:range];
self.changeBlock(self);
}
- (void)KVO_removeObjectIdenticalTo:(id)anObject {
if (anObject == nil || [anObject isKindOfClass:NSNull.class]) return ;
[self removeObjectIdenticalTo:anObject];
self.changeBlock(self);
}
- (void)KVO_removeObjectsFromIndices:(NSUInteger *)indices numIndices:(NSUInteger)cnt {
[self removeObjectsFromIndices:indices numIndices:cnt];
self.changeBlock(self);
}
- (void)KVO_removeObjectsInArray:(NSArray<id> *)otherArray {
if (![otherArray isKindOfClass:NSArray.class]) return ;
[self removeObjectsInArray:otherArray];
self.changeBlock(self);
}
- (void)KVO_removeObjectsInRange:(NSRange)range {
[self removeObjectsInRange:range];
self.changeBlock(self);
}
- (void)KVO_replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray<id> *)otherArray range:(NSRange)otherRange {
if (![otherArray isKindOfClass:NSArray.class]) return ;
[self replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange];
self.changeBlock(self);
}
- (void)KVO_replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray<id> *)otherArray {
if (![otherArray isKindOfClass:NSArray.class]) return ;
[self replaceObjectsInRange:range withObjectsFromArray:otherArray];
self.changeBlock(self);
}
- (void)KVO_setArray:(NSArray<id> *)otherArray {
if (![otherArray isKindOfClass:NSArray.class]) return ;
[self setArray:otherArray];
self.changeBlock(self);
}
- (void)KVO_insertObjects:(NSArray<id> *)objects atIndexes:(NSIndexSet *)indexes {
if (![objects isKindOfClass:NSArray.class]) return ;
[self insertObjects:objects atIndexes:indexes];
self.changeBlock(self);
}
- (void)KVO_removeObjectsAtIndexes:(NSIndexSet *)indexes {
[self removeObjectsAtIndexes:indexes];
self.changeBlock(self);
}
- (void)KVO_replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray<id> *)objects {
if (![objects isKindOfClass:NSArray.class]) return ;
[self replaceObjectsAtIndexes:indexes withObjects:objects];
self.changeBlock(self);
}
- (void)KVO_setObject:(id)obj atIndexedSubscript:(NSUInteger)idx {
if (obj == nil || [obj isKindOfClass:NSNull.class]) return ;
[self setObject:obj atIndexedSubscript:idx];
self.changeBlock(self);
}
@end
+16
View File
@@ -0,0 +1,16 @@
//
// NSObject+DZM.h
// DZMAnimatedTransitioning
//
// Created by 邓泽淼 on 2017/12/22.
// Copyright © 2017年 邓泽淼. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface NSObject (DZM)
@property (nonatomic, weak, nullable) UIView *ATTarget;
@end
+26
View File
@@ -0,0 +1,26 @@
//
// NSObject+DZM.m
// DZMAnimatedTransitioning
//
// Created by 邓泽淼 on 2017/12/22.
// Copyright © 2017年 邓泽淼. All rights reserved.
//
#import "NSObject+DZM.h"
#import <objc/runtime.h>
static const NSString *isATTarget = @"isATTarget";
@implementation NSObject (DZM)
- (void)setATTarget:(UIView *)ATTarget {
objc_setAssociatedObject(self, &isATTarget, ATTarget, OBJC_ASSOCIATION_ASSIGN);
}
- (UIView *)ATTarget {
return objc_getAssociatedObject(self, &isATTarget);
}
@end
@@ -0,0 +1,17 @@
//
// NSObject+LSDefaults.h
// WXReader
//
// Created by Andrew on 2019/10/9.
// Copyright © 2019 Andrew. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (LSDefaults)
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,37 @@
//
// NSObject+LSDefaults.m
// WXReader
//
// Created by Andrew on 2019/10/9.
// Copyright © 2019 Andrew. All rights reserved.
//
#import "NSObject+LSDefaults.h"
@implementation NSObject (LSDefaults)
+ (void)load{
SEL originalSelector = @selector(doesNotRecognizeSelector:);
SEL swizzledSelector = @selector(sw_doesNotRecognizeSelector:);
Method originalMethod = class_getClassMethod(self, originalSelector);
Method swizzledMethod = class_getClassMethod(self, swizzledSelector);
if(class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))){
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else{
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
+ (void)sw_doesNotRecognizeSelector:(SEL)aSelector{
//处理 _LSDefaults 崩溃问题
if([[self description] isEqualToString:@"_LSDefaults"] && (aSelector == NSSelectorFromString(@"sharedInstance"))){
return;
}
[self sw_doesNotRecognizeSelector:aSelector];
}
@end
@@ -0,0 +1,23 @@
//
// NSObject+WXYZ_Observer.h
// BW_Video
//
// Created by Chair on 2019/11/29.
// Copyright © 2019 WXYZ. All rights reserved.
//
#import <Foundation/Foundation.h>
/** 启用KVO自动清除功能 */
#define ENABLE_SWIZZ_IN_SIMPLEKVO 1
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Observer)
/** 添加一个会自动释放的KVO */
- (void)addObserver:(NSString *)keyPath complete:(void(^)(id obj, id _Nullable oldVal, id _Nullable newVal))complte;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,51 @@
//
// NSObject+WXYZ_Observer.m
// BW_Video
//
// Created by Chair on 2019/11/29.
// Copyright © 2019 WXYZ. All rights reserved.
//
#import "NSObject+Observer.h"
#import <objc/runtime.h>
static NSString *helper;
@implementation NSObject (Observer)
- (void)addObserver:(NSString *)keyPath complete:(void (^)(id _Nonnull, id _Nullable, id _Nullable))complte {
if (keyPath.length == 0 || complte == nil) return ;
[self addObserverBlockForKeyPath:keyPath block:complte];
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 11.0 && ENABLE_SWIZZ_IN_SIMPLEKVO) {
NSString *objHash = [NSString stringWithFormat:@"%zd", [self modelHash]];
NSString *pathHash = [NSString stringWithFormat:@"%zd", [keyPath modelHash]];
helper = [objHash stringByAppendingString:pathHash];
if (!objc_getAssociatedObject(self, &helper)) {
objc_setAssociatedObject(self, &helper, helper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
}
+ (void)load {
// 判断版本是否小于11.0并且开关打开,然后替换系统的dealloc方法
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 11.0 && ENABLE_SWIZZ_IN_SIMPLEKVO) {
NSString *dealloc = @"dealloc";
Method originalDealloc = class_getInstanceMethod(self, NSSelectorFromString(dealloc));
Method kvoDealloc = class_getInstanceMethod(self, @selector(customDealloc));
method_exchangeImplementations(originalDealloc, kvoDealloc);
}
}
- (void)customDealloc {
// 判断是否需要移除KVO
if (objc_getAssociatedObject(self, &helper) != nil) {
[self removeObserverBlocks];
objc_setAssociatedObject(self, &helper, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[self customDealloc];
}
@end
@@ -0,0 +1,23 @@
//
// NSObject+Utils.h
// WXReader
//
// Created by LL on 2020/6/5.
// Copyright © 2020 Andrew. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Utils)
- (BOOL)modelisEmpty;
+ (NSDictionary<NSString *, NSString *> *)propertyDict;
+ (NSArray<NSString *> *)propertyArr;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,120 @@
//
// NSObject+Utils.m
// WXReader
//
// Created by LL on 2020/6/5.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "NSObject+Utils.h"
#import "NSString+TFExtension.h"
@implementation NSObject (Utils)
/// 获取指定类中所有的属性信息(属性名-属性类型)
+ (NSDictionary<NSString *, NSString *> *)propertyDict {
// 属性数量
unsigned int count;
objc_property_t *properties = class_copyPropertyList(self, &count);
NSMutableDictionary<NSString *, NSString *> *dic = [NSMutableDictionary dictionary];
for (int i = 0; i < count; i++) {
// 获取属性名称
objc_property_t property = properties[i];
const char *cName = property_getName(property);
const char *cType = property_getAttributes(property);
// 属性名称
NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
// 属性类型
NSString *type = [NSString stringWithCString:cType encoding:NSUTF8StringEncoding];
NSError *error;
// 利用正则表达式获取正确类型
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=@\").*?(?=\")" options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
NSAssert(NO, error.localizedDescription ?: TFLocalizedString(@"动态获取属性类型错误"));
return @{};
}
NSTextCheckingResult *match = [regex firstMatchInString:type options:kNilOptions range:NSMakeRange(0, type.length)];
if (match) {
type = [type substringWithRange:match.range];
} else {
type = [self switchType:type];
}
[dic setObject:type forKey:name];
}
return dic;
}
/// 获取指定类中属性名数组
+ (NSArray<NSString *> *)propertyArr {
return [[self propertyDict] allKeys];
}
+ (NSString *)switchType:(NSString *)type {
NSString *sub = [type substringWithRange:NSMakeRange(1, 1)];
if ([sub isEqualToString:@"i"] ||
[sub isEqualToString:@"s"] ||
[sub isEqualToString:@"I"] ||
[sub isEqualToString:@"S"]) {
return @"int";
}
if ([sub isEqualToString:@"l"]) {
return @"long";
}
if ([sub isEqualToString:@"q"]) {
return @"NSInteger";
}
if ([sub isEqualToString:@"Q"]) {
return @"NSUInteger";
}
if ([sub isEqualToString:@"L"]) {
return @"long";// unsigned long
}
if ([sub isEqualToString:@"f"]) {
return @"float";
}
if ([sub isEqualToString:@"d"]) {
return @"CGFloat";
}
if ([sub isEqualToString:@"B"]) {
return @"BOOL";
}
return @"int";
}
/// 检查Model是否为空
- (BOOL)modelisEmpty {
for (NSString *name in [self.class propertyArr]) {
SEL sel = NSSelectorFromString(name);
if ([self respondsToSelector:sel]) {
id obj = [self performSelectorWithArgs:sel];
if (!kObjectIsEmpty(obj)) {
return NO;
}
} else {
SEL sel = NSSelectorFromString([NSString stringWithFormat:@"%@%@", @"is", [name firstCapitalized]]);
if ([self respondsToSelector:sel]) {
id obj = [self performSelectorWithArgs:sel];
if (!kObjectIsEmpty(obj)) {
return NO;
}
} else {
return YES;
}
}
}
return YES;
}
@end
@@ -0,0 +1,28 @@
//
// NSString+TFExtension.h
// WXReader
//
// Created by 谢腾飞 on 2020/12/3.
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSString (TFExtension)
- (NSString *)fileSize;
- (NSString *)safeSubstringWithRange:(NSRange)range;
- (BOOL)containChinese;
/// 获取正确的网络URL地址
- (NSString *)networkUrl;
- (NSString *)firstCapitalized;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,114 @@
//
// NSString+TFExtension.m
// WXReader
//
// Created by 谢腾飞 on 2020/12/3.
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
//
#import "NSString+TFExtension.h"
@implementation NSString (TFExtension)
- (NSString *)fileSize
{
// 总大小
unsigned long long size = 0;
NSString *sizeText = @"";
// 文件管理者
NSFileManager *mgr = [NSFileManager defaultManager];
// 文件属性
NSDictionary *attrs = [mgr attributesOfItemAtPath:self error:nil];
// 如果这个文件或者文件夹不存在,或者路径不正确直接返回0;
if (attrs == nil) return @"";
if ([attrs.fileType isEqualToString:NSFileTypeDirectory]) { // 如果是文件夹
// 获得文件夹的大小 == 获得文件夹中所有文件的总大小
NSDirectoryEnumerator *enumerator = [mgr enumeratorAtPath:self];
for (NSString *subpath in enumerator) {
// 全路径
NSString *fullSubpath = [self stringByAppendingPathComponent:subpath];
// 累加文件大小
size += [mgr attributesOfItemAtPath:fullSubpath error:nil].fileSize;
if (size >= pow(10, 9)) { // size >= 1GB
sizeText = [NSString stringWithFormat:@"%.2fGB", size / pow(10, 9)];
} else if (size >= pow(10, 6)) { // 1GB > size >= 1MB
sizeText = [NSString stringWithFormat:@"%.2fMB", size / pow(10, 6)];
} else if (size >= pow(10, 3)) { // 1MB > size >= 1KB
sizeText = [NSString stringWithFormat:@"%.2fKB", size / pow(10, 3)];
} else { // 1KB > size
sizeText = [NSString stringWithFormat:@"%zdB", (long)size];
}
}
return sizeText;
} else { // 如果是文件
size = attrs.fileSize;
if (size >= pow(10, 9)) { // size >= 1GB
sizeText = [NSString stringWithFormat:@"%.2fGB", size / pow(10, 9)];
} else if (size >= pow(10, 6)) { // 1GB > size >= 1MB
sizeText = [NSString stringWithFormat:@"%.2fMB", size / pow(10, 6)];
} else if (size >= pow(10, 3)) { // 1MB > size >= 1KB
sizeText = [NSString stringWithFormat:@"%.2fKB", size / pow(10, 3)];
} else { // 1KB > size
sizeText = [NSString stringWithFormat:@"%zdB", (long)size];
}
}
return sizeText;
}
- (NSString *)safeSubstringWithRange:(NSRange)range
{
if (range.location > self.length) {
return @"";
}
if (range.length > self.length) {
return @"";
}
if ((range.location + range.length) > self.length) {
return @"";
}
return [self substringWithRange:range];
}
- (BOOL)containChinese
{
for (int i = 0; i < [self length]; i++) {
unichar t_char = [self characterAtIndex:i];
if (t_char >= 0x4E00 && t_char <= 0x9FA5) {
return YES;
}
}
return NO;
}
YYCache *_cache = nil;
- (NSString *)networkUrl
{
if (!_cache) {
_cache = [YYCache cacheWithName:@"networkingURL"];
}
if ([_cache containsObjectForKey:self]) {
return self;
} else {
BOOL status = [self containChinese];
if (status) {
NSString *t_url = [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
[_cache setObject:t_url forKey:self];
return t_url;
} else {
[_cache setObject:self forKey:self];
return self;
}
}
}
- (NSString *)firstCapitalized
{
NSString *firstStirng = [self substringToIndex:1];
NSString *lastString = [self substringFromIndex:1];
return [firstStirng.capitalizedString stringByAppendingString:lastString];
}
@end
@@ -0,0 +1,17 @@
//
// NSURL+ImageURL.h
// WXReader
//
// Created by Andrew on 2020/7/2.
// Copyright © 2020 Andrew. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSURL (ImageURL)
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,37 @@
//
// NSURL+ImageURL.m
// WXReader
//
// Created by Andrew on 2020/7/2.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "NSURL+ImageURL.h"
#import <objc/runtime.h>
@implementation NSURL (ImageURL)
+ (void)load
{
Method URLWithStringMethod = class_getClassMethod(self, @selector(URLWithString:));
Method sc_URLWithStringMethod = class_getClassMethod(self, @selector(wxyz_URLWithString:));
method_exchangeImplementations(URLWithStringMethod, sc_URLWithStringMethod);
}
// 处理网络图片地址带中文的问题
+ (NSURL *)wxyz_URLWithString:(NSString *)URLString
{
for (int i = 0; i < [URLString length]; i++) {
int character = [URLString characterAtIndex:i];
if (character > 0x4e00 && character < 0x9fff) { // 如果包含中文
// 转义中文内容
NSString *t_string = [[URLString substringWithRange:NSMakeRange(i, 1)] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
URLString = [URLString stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:t_string];
}
}
return [NSURL wxyz_URLWithString:URLString];
}
@end
@@ -0,0 +1,27 @@
//
// UIButton+LayoutCallback.h
// iOS_TEST
//
// Created by Chair on 2020/1/22.
// Copyright © 2020 Chair. All rights reserved.
//
#import <UIKit/UIKit.h>
/** 是否启用该功能 */
#define ENABLE_SWIZZ_IN_SIMPLEFrame 1
NS_ASSUME_NONNULL_BEGIN
@interface UIButton (LayoutCallback)
#if ENABLE_SWIZZ_IN_SIMPLEFrame
/** 子视图布局完成后返回自身 */
@property (nonatomic, copy) void(^frameBlock)(UIButton *button);
#endif
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,42 @@
//
// UIButton+LayoutCallback.m
// iOS_TEST
//
// Created by Chair on 2020/1/22.
// Copyright © 2020 Chair. All rights reserved.
//
#import "UIButton+LayoutCallback.h"
#import <objc/runtime.h>
static const char * identifier = "identifier";
@implementation UIButton (LayoutCallback)
#if ENABLE_SWIZZ_IN_SIMPLEFrame
+ (void)load {
Method method1 = class_getInstanceMethod(self, @selector(layoutSubviews));
Method method2 = class_getInstanceMethod(self, @selector(xl_Btn_layoutSubviews));
method_exchangeImplementations(method1, method2);
}
- (void)xl_Btn_layoutSubviews {
[self xl_Btn_layoutSubviews];
!self.frameBlock ?: self.frameBlock(self);
}
- (void)setFrameBlock:(void (^)(UIButton * _Nonnull))frameBlock {
if (frameBlock == nil) return ;
objc_setAssociatedObject(self, identifier, frameBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void (^)(UIButton * _Nonnull))frameBlock {
return objc_getAssociatedObject(self, identifier);
}
#endif
@end
@@ -0,0 +1,19 @@
//
// UIControl+EventInterval.h
// WXReader
//
// Created by Andrew on 2020/7/23.
// Copyright © 2020 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIControl (EventInterval)
@property (nonatomic, assign) NSTimeInterval touchEventInterval;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,69 @@
//
// UIControl+EventInterval.m
// WXReader
//
// Created by Andrew on 2020/7/23.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "UIControl+EventInterval.h"
#import <objc/runtime.h>
static char * const touchEventIntervalKey = "touchEventIntervalKey";
static char * const eventUnavailableKey = "eventUnavailableKey";
@interface UIControl ()
@property (nonatomic, assign) BOOL eventUnavailable;
@end
@implementation UIControl (EventInterval)
+ (void)load
{
Method method = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method qi_method = class_getInstanceMethod(self, @selector(qi_sendAction:to:forEvent:));
method_exchangeImplementations(method, qi_method);
}
#pragma mark - Action functions
- (void)qi_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
if ([self isMemberOfClass:[UIButton class]]) {
if (self.eventUnavailable == NO) {
self.eventUnavailable = YES;
[self qi_sendAction:action to:target forEvent:event];
[self performSelector:@selector(setEventUnavailable:) withObject:0 afterDelay:self.touchEventInterval];
}
} else {
[self qi_sendAction:action to:target forEvent:event];
}
}
#pragma mark - Setter & Getter functions
- (NSTimeInterval)touchEventInterval
{
return [objc_getAssociatedObject(self, touchEventIntervalKey) doubleValue];
}
- (void)setTouchEventInterval:(NSTimeInterval)touchEventInterval
{
objc_setAssociatedObject(self, touchEventIntervalKey, @(touchEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)eventUnavailable
{
return [objc_getAssociatedObject(self, eventUnavailableKey) boolValue];
}
- (void)setEventUnavailable:(BOOL)eventUnavailable
{
objc_setAssociatedObject(self, eventUnavailableKey, @(eventUnavailable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
@@ -0,0 +1,28 @@
//
// UIImage+Blur.h
// WXReader
//
// Created by LL on 2020/5/20.
// Copyright © 2020 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIImage (Blur)
/*
1.白色,参数:
透明度 0~1, 0为白, 1为深灰色
半径:默认30,推荐值 3 半径值越大越模糊 ,值越小越清楚
色彩饱和度(浓度)因子: 0是黑白灰, 9是浓彩色, 1是原色 默认1.8
“彩度”,英文是称Saturation,即饱和度。将无彩色的黑白灰定为0,最鲜艳定为9s,这样大致分成十阶段,让数值和人的感官直觉一致。
*/
- (UIImage *)imgWithLightAlpha:(CGFloat)alpha radius:(CGFloat)radius colorSaturationFactor:(CGFloat)colorSaturationFactor;
- (UIImage *)imgWithBlur;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,129 @@
//
// UIImage+LightMode.m
// WXReader
//
// Created by LL on 2020/5/20.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "UIImage+Blur.h"
#import <Accelerate/Accelerate.h>
@implementation UIImage (Blur)
- (UIImage *)imgWithLightAlpha:(CGFloat)alpha radius:(CGFloat)radius colorSaturationFactor:(CGFloat)colorSaturationFactor {
UIColor *tintColor = [UIColor colorWithRed:180 / 255.0 green:180 / 255.0 blue:180 / 255.0 alpha:alpha];
return [self imgBluredWithRadius:radius tintColor:tintColor saturationDeltaFactor:colorSaturationFactor maskImage:nil];
}
- (UIImage *)imgWithBlur {
return [self imgWithLightAlpha:0.1 radius:3 colorSaturationFactor:1];
}
// 内部方法,核心代码,封装了毛玻璃效果 参数:半径,颜色,色彩饱和度
- (UIImage *)imgBluredWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
{
CGRect imageRect = { CGPointZero, self.size };
UIImage *effectImage = self;
BOOL hasBlur = blurRadius > __FLT_EPSILON__;
BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
if (hasBlur || hasSaturationChange) {
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef effectInContext = UIGraphicsGetCurrentContext();
CGContextScaleCTM(effectInContext, 1.0, -1.0);
CGContextTranslateCTM(effectInContext, 0, -self.size.height);
CGContextDrawImage(effectInContext, imageRect, self.CGImage);
vImage_Buffer effectInBuffer;
effectInBuffer.data = CGBitmapContextGetData(effectInContext);
effectInBuffer.width = CGBitmapContextGetWidth(effectInContext);
effectInBuffer.height = CGBitmapContextGetHeight(effectInContext);
effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
vImage_Buffer effectOutBuffer;
effectOutBuffer.data = CGBitmapContextGetData(effectOutContext);
effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext);
effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext);
effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);
if (hasBlur) {
CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
if (radius % 2 != 1) {
radius += 1; // force radius to be odd so that the three box-blur methodology works.
}
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, (int)radius, (int)radius, 0, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, (int)radius, (int)radius, 0, kvImageEdgeExtend);
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, (int)radius, (int)radius, 0, kvImageEdgeExtend);
}
BOOL effectImageBuffersAreSwapped = NO;
if (hasSaturationChange) {
CGFloat s = saturationDeltaFactor;
CGFloat floatingPointSaturationMatrix[] = {
0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
0, 0, 0, 1,
};
const int32_t divisor = 256;
NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
int16_t saturationMatrix[matrixSize];
for (NSUInteger i = 0; i < matrixSize; ++i) {
saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
}
if (hasBlur) {
vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
effectImageBuffersAreSwapped = YES;
}
else {
vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
}
}
if (!effectImageBuffersAreSwapped)
effectImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (effectImageBuffersAreSwapped)
effectImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
// 开启上下文 用于输出图像
UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
CGContextRef outputContext = UIGraphicsGetCurrentContext();
CGContextScaleCTM(outputContext, 1.0, -1.0);
CGContextTranslateCTM(outputContext, 0, -self.size.height);
// 开始画底图
CGContextDrawImage(outputContext, imageRect, self.CGImage);
// 开始画模糊效果
if (hasBlur) {
CGContextSaveGState(outputContext);
if (maskImage) {
CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
}
CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
CGContextRestoreGState(outputContext);
}
// 添加颜色渲染
if (tintColor) {
CGContextSaveGState(outputContext);
CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
CGContextFillRect(outputContext, imageRect);
CGContextRestoreGState(outputContext);
}
// 输出成品,并关闭上下文
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
@end
@@ -0,0 +1,19 @@
//
// UIImage+Color.h
// WXReader
//
// Created by LL on 2020/6/4.
// Copyright © 2020 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIImage (Color)
- (instancetype)imageWithColor:(UIColor *)color;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,28 @@
//
// UIImage+Color.m
// WXReader
//
// Created by LL on 2020/6/4.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "UIImage+Color.h"
@implementation UIImage (Color)
- (instancetype)imageWithColor:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextClipToMask(context, rect, self.CGImage);
[color setFill];
CGContextFillRect(context, rect);
UIImage *t_image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return t_image;
}
@end
@@ -0,0 +1,24 @@
//
// UIImageView+CornerRadius.h
// MyPractise
//
// Created by lzy on 16/3/1.
// Copyright © 2016年 lzy. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface UIImageView (CornerRadius)
- (instancetype)initWithCornerRadiusAdvance:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType;
- (void)zy_cornerRadiusAdvance:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType;
- (instancetype)initWithRoundingRectImageView;
- (void)zy_cornerRadiusRoundingRect;
- (void)zy_attachBorderWidth:(CGFloat)width color:(UIColor *)color;
@end
@@ -0,0 +1,300 @@
//
// UIImageView+CornerRadius.m
// MyPractise
//
// Created by lzy on 16/3/1.
// Copyright © 2016年 lzy. All rights reserved.
//
#import "UIImageView+CornerRadius.h"
#import "NSObject+Observer.h"
const char kProcessedImage;
@interface UIImageView ()
@property (assign, nonatomic) CGFloat zyRadius;
@property (assign, nonatomic) UIRectCorner roundingCorners;
@property (assign, nonatomic) CGFloat zyBorderWidth;
@property (strong, nonatomic) UIColor *zyBorderColor;
@property (assign, nonatomic) BOOL zyHadAddObserver;
@property (assign, nonatomic) BOOL zyIsRounding;
@end
@implementation UIImageView (CornerRadius)
/**
* @brief init the Rounding UIImageView, no off-screen-rendered
*/
- (instancetype)initWithRoundingRectImageView {
self = [super init];
if (self) {
[self zy_cornerRadiusRoundingRect];
}
return self;
}
/**
* @brief init the UIImageView with cornerRadius, no off-screen-rendered
*/
- (instancetype)initWithCornerRadiusAdvance:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType {
self = [super init];
if (self) {
[self zy_cornerRadiusAdvance:cornerRadius rectCornerType:rectCornerType];
}
return self;
}
/**
* @brief attach border for UIImageView with width & color
*/
- (void)zy_attachBorderWidth:(CGFloat)width color:(UIColor *)color {
self.zyBorderWidth = width;
self.zyBorderColor = color;
}
#pragma mark - Kernel
/**
* @brief clip the cornerRadius with image, UIImageView must be setFrame before, no off-screen-rendered
*/
- (void)zy_cornerRadiusWithImage:(UIImage *)image cornerRadius:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType {
CGSize size = self.bounds.size;
CGFloat scale = [UIScreen mainScreen].scale;
CGSize cornerRadii = CGSizeMake(cornerRadius, cornerRadius);
UIGraphicsBeginImageContextWithOptions(size, NO, scale);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
if (nil == currentContext) {
return;
}
UIBezierPath *cornerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:rectCornerType cornerRadii:cornerRadii];
[cornerPath addClip];
[self.layer renderInContext:currentContext];
[self drawBorder:cornerPath];
UIImage *processedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (processedImage) {
objc_setAssociatedObject(processedImage, &kProcessedImage, @(1), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
self.image = processedImage;
}
/**
* @brief clip the cornerRadius with image, draw the backgroundColor you want, UIImageView must be setFrame before, no off-screen-rendered, no Color Blended layers
*/
- (void)zy_cornerRadiusWithImage:(UIImage *)image cornerRadius:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType backgroundColor:(UIColor *)backgroundColor {
CGSize size = self.bounds.size;
CGFloat scale = [UIScreen mainScreen].scale;
CGSize cornerRadii = CGSizeMake(cornerRadius, cornerRadius);
UIGraphicsBeginImageContextWithOptions(size, YES, scale);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
if (nil == currentContext) {
return;
}
UIBezierPath *cornerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:rectCornerType cornerRadii:cornerRadii];
UIBezierPath *backgroundRect = [UIBezierPath bezierPathWithRect:self.bounds];
[backgroundColor setFill];
[backgroundRect fill];
[cornerPath addClip];
[self.layer renderInContext:currentContext];
[self drawBorder:cornerPath];
UIImage *processedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (processedImage) {
objc_setAssociatedObject(processedImage, &kProcessedImage, @(1), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
self.image = processedImage;
}
/**
* @brief set cornerRadius for UIImageView, no off-screen-rendered
*/
- (void)zy_cornerRadiusAdvance:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType {
self.zyRadius = cornerRadius;
self.roundingCorners = rectCornerType;
self.zyIsRounding = NO;
if (!self.zyHadAddObserver) {
// [[self class] swizzleDealloc];
// [self addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:KEY_PATH(self, image) complete:^(UIImageView * _Nonnull obj, id _Nullable oldVal, UIImage * _Nullable newImage) {
if ([newImage isMemberOfClass:[NSNull class]]) {
return;
} else if ([objc_getAssociatedObject(newImage, &kProcessedImage) intValue] == 1) {
return;
}
[obj validateFrame];
if (obj.zyIsRounding) {
[obj zy_cornerRadiusWithImage:newImage cornerRadius:obj.frame.size.width/2 rectCornerType:UIRectCornerAllCorners];
} else if (0 != self.zyRadius && 0 != self.roundingCorners && nil != self.image) {
[obj zy_cornerRadiusWithImage:newImage cornerRadius:obj.zyRadius rectCornerType:obj.roundingCorners];
}
}];
self.zyHadAddObserver = YES;
}
//Xcode 8 xib 删除了控件的Frame信息,需要主动创造
[self layoutIfNeeded];
}
/**
* @brief become Rounding UIImageView, no off-screen-rendered
*/
- (void)zy_cornerRadiusRoundingRect {
self.zyIsRounding = YES;
if (!self.zyHadAddObserver) {
// [[self class] swizzleDealloc];
// [self addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:KEY_PATH(self, image) complete:^(UIImageView * _Nonnull obj, id _Nullable oldVal, UIImage * _Nullable newImage) {
if ([newImage isMemberOfClass:[NSNull class]]) {
return;
} else if ([objc_getAssociatedObject(newImage, &kProcessedImage) intValue] == 1) {
return;
}
[obj validateFrame];
if (obj.zyIsRounding) {
[obj zy_cornerRadiusWithImage:newImage cornerRadius:obj.frame.size.width/2 rectCornerType:UIRectCornerAllCorners];
} else if (0 != self.zyRadius && 0 != self.roundingCorners && nil != self.image) {
[obj zy_cornerRadiusWithImage:newImage cornerRadius:obj.zyRadius rectCornerType:obj.roundingCorners];
}
}];
self.zyHadAddObserver = YES;
}
//Xcode 8 xib 删除了控件的Frame信息,需要主动创造
[self layoutIfNeeded];
}
#pragma mark - Private
- (void)drawBorder:(UIBezierPath *)path {
if (0 != self.zyBorderWidth && nil != self.zyBorderColor) {
[path setLineWidth:2 * self.zyBorderWidth];
[self.zyBorderColor setStroke];
[path stroke];
}
}
//- (void)zy_dealloc {
// if (self.zyHadAddObserver) {
//// [self removeObserver:self forKeyPath:@"image"];
// }
// [self zy_dealloc];
//}
- (void)validateFrame {
if (self.frame.size.width == 0) {
[self.class swizzleLayoutSubviews];
}
}
+ (void)swizzleMethod:(SEL)oneSel anotherMethod:(SEL)anotherSel {
Method oneMethod = class_getInstanceMethod(self, oneSel);
Method anotherMethod = class_getInstanceMethod(self, anotherSel);
method_exchangeImplementations(oneMethod, anotherMethod);
}
//
//+ (void)swizzleDealloc {
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// [self swizzleMethod:NSSelectorFromString(@"dealloc") anotherMethod:@selector(zy_dealloc)];
// });
//}
+ (void)swizzleLayoutSubviews {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleMethod:@selector(layoutSubviews) anotherMethod:@selector(zy_LayoutSubviews)];
});
}
- (void)zy_LayoutSubviews {
[self zy_LayoutSubviews];
if (self.zyIsRounding) {
[self zy_cornerRadiusWithImage:self.image cornerRadius:self.frame.size.width/2 rectCornerType:UIRectCornerAllCorners];
} else if (0 != self.zyRadius && 0 != self.roundingCorners && nil != self.image) {
[self zy_cornerRadiusWithImage:self.image cornerRadius:self.zyRadius rectCornerType:self.roundingCorners];
}
}
#pragma mark - KVO for .image
//- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
// if ([keyPath isEqualToString:@"image"]) {
// UIImage *newImage = change[NSKeyValueChangeNewKey];
// if ([newImage isMemberOfClass:[NSNull class]]) {
// return;
// } else if ([objc_getAssociatedObject(newImage, &kProcessedImage) intValue] == 1) {
// return;
// }
// [self validateFrame];
// if (self.zyIsRounding) {
// [self zy_cornerRadiusWithImage:newImage cornerRadius:self.frame.size.width/2 rectCornerType:UIRectCornerAllCorners];
// } else if (0 != self.zyRadius && 0 != self.roundingCorners && nil != self.image) {
// [self zy_cornerRadiusWithImage:newImage cornerRadius:self.zyRadius rectCornerType:self.roundingCorners];
// }
// }
//}
#pragma mark property
- (CGFloat)zyBorderWidth {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
- (void)setZyBorderWidth:(CGFloat)zyBorderWidth {
objc_setAssociatedObject(self, @selector(zyBorderWidth), @(zyBorderWidth), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIColor *)zyBorderColor {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setZyBorderColor:(UIColor *)zyBorderColor {
objc_setAssociatedObject(self, @selector(zyBorderColor), zyBorderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)zyHadAddObserver {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setZyHadAddObserver:(BOOL)zyHadAddObserver {
objc_setAssociatedObject(self, @selector(zyHadAddObserver), @(zyHadAddObserver), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)zyIsRounding {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setZyIsRounding:(BOOL)zyIsRounding {
objc_setAssociatedObject(self, @selector(zyIsRounding), @(zyIsRounding), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIRectCorner)roundingCorners {
return [objc_getAssociatedObject(self, _cmd) unsignedLongValue];
}
- (void)setRoundingCorners:(UIRectCorner)roundingCorners {
objc_setAssociatedObject(self, @selector(roundingCorners), @(roundingCorners), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (CGFloat)zyRadius {
return [objc_getAssociatedObject(self, _cmd) floatValue];
}
- (void)setZyRadius:(CGFloat)zyRadius {
objc_setAssociatedObject(self, @selector(zyRadius), @(zyRadius), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
//ZYCornerRadius is available under the MIT license.
//Please visit https://github.com/liuzhiyi1992/ZYCornerRadius for details.
@@ -0,0 +1,19 @@
//
// YYLabel+LineBreak.h
// WXReader
//
// Created by Andrew on 2020/4/2.
// Copyright © 2020 Andrew. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@interface UILabel (LineBreak)
- (void)setLineBreakByTruncatingLastLineMiddle;
- (NSArray *)getSeparatedLinesArray;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,93 @@
//
// YYLabel+LineBreak.m
// WXReader
//
// Created by Andrew on 2020/4/2.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "UILabel+LineBreak.h"
@implementation UILabel (LineBreak)
- (void)setLineBreakByTruncatingLastLineMiddle {
if ( self.numberOfLines <= 0 ) {
return;
}
NSMutableString *limitedText = [NSMutableString string];
NSArray *separatedLines = [self getSeparatedLinesArray];
if (separatedLines > 0) {
for (int i = 0; i < separatedLines.count; i ++) {
if (i == separatedLines.count - 1) {
UILabel *lastLineLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width / 2, MAXFLOAT)];
lastLineLabel.font = self.font;
lastLineLabel.text = [separatedLines lastObject];
NSArray *subSeparatedLines = [lastLineLabel getSeparatedLinesArray];
NSString *lastLineText = [subSeparatedLines firstObject];
NSInteger lastLineTextCount = lastLineText.length;
[limitedText appendString:[NSString stringWithFormat:@"%@...",[lastLineText substringToIndex:lastLineTextCount]]];
} else {
[limitedText appendString:[separatedLines objectOrNilAtIndex:i]];
}
}
}
[self setText:limitedText lineSpacing:5];
}
-(void)setText:(NSString*)text lineSpacing:(CGFloat)lineSpacing {
if (!text || lineSpacing < 0.01) {
self.text = text;
return;
}
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:lineSpacing]; //设置行间距
[paragraphStyle setLineBreakMode:self.lineBreakMode];
[paragraphStyle setAlignment:self.textAlignment];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [text length])];
self.attributedText = attributedString;
}
- (NSArray *)getSeparatedLinesArray {
NSString *text = [self text];
if (!text || text.length <= 0) {
return @[@""];
}
UIFont *font = [self font];
CGRect rect = [self frame];
CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,rect.size.width,100000));
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);
NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);
NSMutableArray *linesArray = [[NSMutableArray alloc] init];
for (id line in lines) {
CTLineRef lineRef = (__bridge CTLineRef )line;
CFRange lineRange = CTLineGetStringRange(lineRef);
NSRange range = NSMakeRange(lineRange.location, lineRange.length);
NSString *lineString = [text substringWithRange:range];
[linesArray addObject:lineString];
}
return (NSArray *)linesArray;
}
@end
@@ -0,0 +1,22 @@
//
// UINavigationController+TFExtension.h
// WXReader
//
// Created by 谢腾飞 on 2020/12/1.
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UINavigationController (TFExtension)
@property (nonatomic ,assign) BOOL isTransition;
// 执行转场动画
- (void)jumpNextViewController:(UIViewController *)viewController animated:(BOOL)animated;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,36 @@
//
// UINavigationController+TFExtension.m
// WXReader
//
// Created by 谢腾飞 on 2020/12/1.
// Copyright © 2020 xtfei_2011@126.com. All rights reserved.
//
#import "UINavigationController+TFExtension.h"
#import <objc/runtime.h>
static const char *transitionKey = "transitionKey";
@implementation UINavigationController (TFExtension)
- (void)setIsTransition:(BOOL)isTransition
{
objc_setAssociatedObject(self, transitionKey, @(isTransition), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)isTransition
{
NSNumber *number = objc_getAssociatedObject(self, transitionKey);
return [number boolValue];
}
- (void)jumpNextViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[viewController setValue:[NSNumber numberWithBool:YES] forKey:@"isAT"];
self.delegate = (id<UINavigationControllerDelegate>)viewController;
self.isTransition = YES;
[self pushViewController:viewController animated:animated];
}
@end
@@ -0,0 +1,13 @@
//
// UIPageViewController+DPGestureDeal.h
// WXReader
//
// Created by Andrew on 2018/5/30.
// Copyright © 2018年 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIPageViewController (DPGestureDeal)
@end
@@ -0,0 +1,31 @@
//
// UIPageViewController+DPGestureDeal.m
// WXReader
//
// Created by Andrew on 2018/5/30.
// Copyright © 2018年 Andrew. All rights reserved.
//
#import "UIPageViewController+DPGestureDeal.h"
#import "NSObject+YYAdd.h"
@implementation UIPageViewController (DPGestureDeal)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
return NO;
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return NO;
}
return YES;
}
@end
@@ -0,0 +1,13 @@
//
// UIScrollView+UITouch.h
// WXReader
//
// Created by Andrew on 2018/7/7.
// Copyright © 2018年 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIScrollView (UITouch)
@end
@@ -0,0 +1,20 @@
//
// UIScrollView+UITouch.m
// WXReader
//
// Created by Andrew on 2018/7/7.
// Copyright © 2018年 Andrew. All rights reserved.
//
#import "UIScrollView+UITouch.h"
@implementation UIScrollView (UITouch)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 选其一即可
[super touchesBegan:touches withEvent:event];
// [[self nextResponder] touchesBegan:touches withEvent:event];
}
@end
@@ -0,0 +1,35 @@
// The MIT License (MIT)
//
// Copyright (c) 2014 Suyeol Jeon (http:xoul.kr)
//
// 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.
@import UIKit;
FOUNDATION_EXPORT double UITextView_PlaceholderVersionNumber;
FOUNDATION_EXPORT const unsigned char UITextView_PlaceholderVersionString[];
@interface UITextView (Placeholder)
/* 占位文字 */
@property (nonatomic, copy) NSString *placeholder;
/* 占位文字颜色 */
@property (nonatomic, strong) UIColor *placeholderColor;
@end
@@ -0,0 +1,163 @@
// The MIT License (MIT)
//
// Copyright (c) 2014 Suyeol Jeon (http:xoul.kr)
//
// 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.
#import <objc/runtime.h>
#import "UITextView+Placeholder.h"
// 占位文字
static const void *PlaceholderViewKey = &PlaceholderViewKey;
// 占位文字颜色
static const void *PlaceholderColorKey = &PlaceholderColorKey;
@implementation UITextView (Placeholder)
#pragma mark - Swizzle Dealloc
#pragma mark - Swizzle Dealloc
+ (void)load {
// 交换dealoc
Method dealoc = class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc"));
Method myDealoc = class_getInstanceMethod(self.class, @selector(myDealoc));
method_exchangeImplementations(dealoc, myDealoc);
}
- (void)myDealoc {
// 移除监听
[[NSNotificationCenter defaultCenter] removeObserver:self];
UITextView *placeholderView = objc_getAssociatedObject(self, PlaceholderViewKey);
// 如果有值才去调用,这步很重要
if (placeholderView) {
NSArray *propertys = @[@"frame", @"bounds", @"font", @"text", @"textAlignment", @"textContainerInset"];
for (NSString *property in propertys) {
@try {
[self removeObserver:self forKeyPath:property];
} @catch (NSException *exception) {}
}
}
[self myDealoc];
}
#pragma mark - set && get
- (UITextView *)placeholderView {
// 为了让占位文字和textView的实际文字位置能够完全一致,这里用UITextView
UITextView *placeholderView = objc_getAssociatedObject(self, PlaceholderViewKey);
if (!placeholderView) {
placeholderView = [[UITextView alloc] init];
placeholderView.hidden = (self.text.length > 0 && self.text);
// 动态添加属性的本质是: 让对象的某个属性与值产生关联
objc_setAssociatedObject(self, PlaceholderViewKey, placeholderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
placeholderView = placeholderView;
// 设置基本属性
self.scrollEnabled = placeholderView.scrollEnabled = placeholderView.showsHorizontalScrollIndicator = placeholderView.showsVerticalScrollIndicator = placeholderView.userInteractionEnabled = NO;
placeholderView.textColor = [UIColor lightGrayColor];
placeholderView.backgroundColor = [UIColor clearColor];
[self refreshPlaceholderView];
[self addSubview:placeholderView];
// 监听文字改变
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewTextChange) name:UITextViewTextDidChangeNotification object:self];
// 这些属性改变时,都要作出一定的改变,尽管已经监听了TextDidChange的通知,也要监听text属性,因为通知监听不到setText:
NSArray *propertys = @[@"frame", @"bounds", @"font", @"text", @"textAlignment", @"textContainerInset"];
// 监听属性
for (NSString *property in propertys) {
[self addObserver:self forKeyPath:property options:NSKeyValueObservingOptionNew context:nil];
}
}
return placeholderView;
}
- (void)setPlaceholder:(NSString *)placeholder
{
// 为placeholder赋值
[self placeholderView].text = placeholder;
}
- (NSString *)placeholder
{
// 如果有placeholder值才去调用,这步很重要
if (self.placeholderExist) {
return [self placeholderView].text;
}
return nil;
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
self.placeholderView.textColor = placeholderColor;
}
- (UIColor *)placeholderColor {
return self.placeholderView.textColor;
}
#pragma mark - KVO监听属性改变
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self refreshPlaceholderView];
if ([keyPath isEqualToString:@"text"]) [self textViewTextChange];
}
- (void)refreshPlaceholderView {
UITextView *placeholderView = objc_getAssociatedObject(self, PlaceholderViewKey);
// 如果有值才去调用,这步很重要
if (placeholderView) {
self.placeholderView.frame = self.bounds;
self.placeholderView.font = self.font;
self.placeholderView.textAlignment = self.textAlignment;
self.placeholderView.textContainerInset = self.textContainerInset;
}
}
- (void)textViewTextChange {
UITextView *placeholderView = objc_getAssociatedObject(self, PlaceholderViewKey);
// 如果有值才去调用,这步很重要
if (placeholderView) {
self.placeholderView.hidden = (self.text.length > 0 && self.text);
}
if (!self.isFirstResponder) [self becomeFirstResponder];
}
// 判断是否有placeholder值,这步很重要
- (BOOL)placeholderExist {
// 获取对应属性的值
UITextView *placeholderView = objc_getAssociatedObject(self, PlaceholderViewKey);
// 如果有placeholder值
if (placeholderView) return YES;
return NO;
}
@end
@@ -0,0 +1,42 @@
//
// UIView+AZGradient.h
// AZCategory
//
// Created by Alfred Zhang on 2017/6/29.
// Copyright © 2017年 Alfred Zhang. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (AZGradient)
/* The array of CGColorRef objects defining the color of each gradient
* stop. Defaults to nil. Animatable. */
@property(nullable, copy) NSArray *az_colors;
/* An optional array of NSNumber objects defining the location of each
* gradient stop as a value in the range [0,1]. The values must be
* monotonically increasing. If a nil array is given, the stops are
* assumed to spread uniformly across the [0,1] range. When rendered,
* the colors are mapped to the output colorspace before being
* interpolated. Defaults to nil. Animatable. */
@property(nullable, copy) NSArray<NSNumber *> *az_locations;
/* The start and end points of the gradient when drawn into the layer's
* coordinate space. The start point corresponds to the first gradient
* stop, the end point to the last gradient stop. Both points are
* defined in a unit coordinate space that is then mapped to the
* layer's bounds rectangle when drawn. (I.e. [0,0] is the bottom-left
* corner of the layer, [1,1] is the top-right corner.) The default values
* are [.5,0] and [.5,1] respectively. Both are animatable. */
@property CGPoint az_startPoint;
@property CGPoint az_endPoint;
+ (UIView *_Nullable)az_gradientViewWithColors:(NSArray<UIColor *> *_Nullable)colors locations:(NSArray<NSNumber *> *_Nullable)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;
- (void)az_setGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors locations:(NSArray<NSNumber *> *_Nullable)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;
@end
@@ -0,0 +1,93 @@
//
// UIView+AZGradient.m
// AZCategory
//
// Created by Alfred Zhang on 2017/6/29.
// Copyright © 2017年 Alfred Zhang. All rights reserved.
//
#import "UIView+AZGradient.h"
#import <objc/runtime.h>
@implementation UIView (AZGradient)
+ (Class)layerClass {
return [CAGradientLayer class];
}
+ (UIView *)az_gradientViewWithColors:(NSArray<UIColor *> *)colors locations:(NSArray<NSNumber *> *)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
UIView *view = [[self alloc] init];
[view az_setGradientBackgroundWithColors:colors locations:locations startPoint:startPoint endPoint:endPoint];
return view;
}
- (void)az_setGradientBackgroundWithColors:(NSArray<UIColor *> *)colors locations:(NSArray<NSNumber *> *)locations startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
NSMutableArray *colorsM = [NSMutableArray array];
for (UIColor *color in colors) {
[colorsM addObject:(__bridge id)color.CGColor];
}
self.az_colors = [colorsM copy];
self.az_locations = locations;
self.az_startPoint = startPoint;
self.az_endPoint = endPoint;
}
#pragma mark- Getter&Setter
- (NSArray *)az_colors {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAz_colors:(NSArray *)colors {
objc_setAssociatedObject(self, @selector(az_colors), colors, OBJC_ASSOCIATION_COPY_NONATOMIC);
if ([self.layer isKindOfClass:[CAGradientLayer class]]) {
[((CAGradientLayer *)self.layer) setColors:self.az_colors];
}
}
- (NSArray<NSNumber *> *)az_locations {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAz_locations:(NSArray<NSNumber *> *)locations {
objc_setAssociatedObject(self, @selector(az_locations), locations, OBJC_ASSOCIATION_COPY_NONATOMIC);
if ([self.layer isKindOfClass:[CAGradientLayer class]]) {
[((CAGradientLayer *)self.layer) setLocations:self.az_locations];
}
}
- (CGPoint)az_startPoint {
return [objc_getAssociatedObject(self, _cmd) CGPointValue];
}
- (void)setAz_startPoint:(CGPoint)startPoint {
objc_setAssociatedObject(self, @selector(az_startPoint), [NSValue valueWithCGPoint:startPoint], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([self.layer isKindOfClass:[CAGradientLayer class]]) {
[((CAGradientLayer *)self.layer) setStartPoint:self.az_startPoint];
}
}
- (CGPoint)az_endPoint {
return [objc_getAssociatedObject(self, _cmd) CGPointValue];
}
- (void)setAz_endPoint:(CGPoint)endPoint {
objc_setAssociatedObject(self, @selector(az_endPoint), [NSValue valueWithCGPoint:endPoint], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([self.layer isKindOfClass:[CAGradientLayer class]]) {
[((CAGradientLayer *)self.layer) setEndPoint:self.az_endPoint];
}
}
@end
@implementation UILabel (AZGradient)
+ (Class)layerClass {
return [CAGradientLayer class];
}
@end
@@ -0,0 +1,72 @@
//
// UIView+BorderLine.h
// WXReader
//
// Created by Andrew on 2018/5/24.
// Copyright © 2018年 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_OPTIONS(NSUInteger, UIBorderSideType) {
UIBorderSideTypeAll = 0, // 全边框
UIBorderSideTypeTop = 1 << 0, // 上侧边框
UIBorderSideTypeBottom = 1 << 1, // 下侧边框
UIBorderSideTypeLeft = 1 << 2, // 左侧边框
UIBorderSideTypeRight = 1 << 3, // 右侧边框
};
typedef NS_OPTIONS(NSUInteger, UIBorderConnerType) {
UIBorderConnerTypeLeftTop = 0, // 左上
UIBorderConnerTypeLeftBottom = 1 << 0, // 左下
UIBorderConnerTypeRightTop = 1 << 1, // 右上
UIBorderConnerTypeRightBottom = 1 << 2, // 右下
UIBorderConnerTypeTopLeft = 1 << 3, // 上左
UIBorderConnerTypeTopRight = 1 << 4, // 上右
UIBorderConnerTypeBottomLeft = 1 << 5, // 下左
UIBorderConnerTypeBottomRight = 1 << 6, // 下右
};
@interface UIView (BorderLine)
/**
增加边框
@param borderWidth 边框宽度
@param borderColor 边框颜色
@param cornerRadius 边框圆角
*/
- (void)addBorderLineWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor cornerRadius:(CGFloat)cornerRadius;
/**
增加边框
@param borderWidth 边框宽度
@param borderColor 边框颜色
@param cornerRadius 边框圆角
@param borderType 边框类型
*/
- (void)addBorderLineWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor cornerRadius:(CGFloat)cornerRadius borderType:(UIBorderSideType)borderType;
/**
增加边框
@param borderWidth 边框宽度
@param borderColor 边框颜色
@param cornerRadius 边框圆角
@param borderType 边框类型
@param connerSpaceWidth 角边距离
@param connerType 角边类型
*/
- (void)addBorderLineWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor cornerRadius:(CGFloat)cornerRadius borderType:(UIBorderSideType)borderType connerSpaceWidth:(CGFloat)connerSpaceWidth connerType:(UIBorderConnerType)connerType;
// 设置正常背景色
- (void)normalBackgroundColor;
// 设置选中背景色
- (void)selectBackgroundColor;
// 添加圆角
- (void)addRoundingCornersWithRoundingCorners:(UIRectCorner)corners;
@end
@@ -0,0 +1,115 @@
//
// UIView+BorderLine.m
// WXReader
//
// Created by Andrew on 2018/5/24.
// Copyright © 2018年 Andrew. All rights reserved.
//
#import "UIView+BorderLine.h"
@implementation UIView (BorderLine)
- (void)addBorderLineWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor cornerRadius:(CGFloat)cornerRadius borderType:(UIBorderSideType)borderType connerSpaceWidth:(CGFloat)connerSpaceWidth connerType:(UIBorderConnerType)connerType
{
}
- (void)addBorderLineWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor cornerRadius:(CGFloat)cornerRadius borderType:(UIBorderSideType)borderType
{
CGFloat space = borderWidth / 2;
switch (borderType) {
case UIBorderSideTypeAll: {
CAShapeLayer *lineBorder = [[CAShapeLayer alloc] init];
lineBorder.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
[lineBorder setLineWidth:borderWidth];
[lineBorder setStrokeColor:borderColor.CGColor];
[lineBorder setFillColor:[UIColor clearColor].CGColor];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:lineBorder.frame byRoundingCorners:UIRectCornerAllCorners cornerRadii:(CGSizeMake(cornerRadius, cornerRadius))];
lineBorder.path = path.CGPath;
[self.layer addSublayer:lineBorder];
}
break;
case UIBorderSideTypeLeft: {
[self.layer addSublayer:[self addLineOriginPoint:CGPointMake(0.f, 0.f + space) toPoint:CGPointMake(0.0f, self.frame.size.height - space) color:borderColor borderWidth:borderWidth]];
}
break;
case UIBorderSideTypeRight: {
[self.layer addSublayer:[self addLineOriginPoint:CGPointMake(self.frame.size.width, 0.0f + space) toPoint:CGPointMake(self.frame.size.width, self.frame.size.height - space) color:borderColor borderWidth:borderWidth]];
}
break;
case UIBorderSideTypeTop: {
[self.layer addSublayer:[self addLineOriginPoint:CGPointMake(0.0f + space, 0.0f) toPoint:CGPointMake(self.frame.size.width - space, 0.0f) color:borderColor borderWidth:borderWidth]];
}
break;
case UIBorderSideTypeBottom: {
[self.layer addSublayer:[self addLineOriginPoint:CGPointMake(0.0f + space, self.frame.size.height) toPoint:CGPointMake(self.frame.size.width - space, self.frame.size.height) color:borderColor borderWidth:borderWidth]];
}
break;
default:
break;
}
}
- (void)addBorderLineWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor cornerRadius:(CGFloat)cornerRadius
{
[self addBorderLineWithBorderWidth:borderWidth borderColor:borderColor cornerRadius:cornerRadius borderType:UIBorderSideTypeAll];
}
- (CAShapeLayer *)addLineOriginPoint:(CGPoint)p0 toPoint:(CGPoint)p1 color:(UIColor *)color borderWidth:(CGFloat)borderWidth {
/// 线的路径
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:p0];
[bezierPath addLineToPoint:p1];
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = color.CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
/// 添加路径
shapeLayer.path = bezierPath.CGPath;
/// 线宽度
shapeLayer.lineWidth = borderWidth;
return shapeLayer;
}
- (void)normalBackgroundColor
{
//创建一个渐变的图层
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.colors = @[(__bridge id)[UIColor colorWithHexString:@"#ffb084"].CGColor, (__bridge id)[UIColor colorWithHexString:@"#fd9a63"].CGColor];
gradientLayer.locations = @[@0, @1.0];
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(0, 1.0);
gradientLayer.frame = self.bounds;
gradientLayer.name = @"gradientLayer";
//生成一个image
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0.0);
[gradientLayer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//设置背景颜色
self.backgroundColor = [UIColor colorWithPatternImage:img];
}
- (void)selectBackgroundColor
{
self.backgroundColor = kColorRGBA(0, 0, 0, 0.2);
}
// 添加圆角
- (void)addRoundingCornersWithRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(12, 12)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
@end
@@ -0,0 +1,29 @@
//
// UIView+LayoutSubviewsCallback.h
// iOS_TEST
//
// Created by Chair on 2019/11/24.
// Copyright © 2019 Chair. All rights reserved.
//
//#import <AppKit/AppKit.h>
#import <UIKit/UIKit.h>
#import "UIButton+LayoutCallback.h"
NS_ASSUME_NONNULL_BEGIN
@interface UIView (LayoutCallback)
#if ENABLE_SWIZZ_IN_SIMPLEFrame
/** 子视图布局完成后返回自身 */
@property (nonatomic, copy) void(^frameBlock)(UIView *view);
#endif
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,42 @@
//
// UIView+LayoutSubviewsCallback.m
// iOS_TEST
//
// Created by Chair on 2019/11/24.
// Copyright © 2019 Chair. All rights reserved.
//
#import "UIView+LayoutCallback.h"
#import <objc/runtime.h>
static const char * identifier = "identifier";
@implementation UIView (LayoutCallback)
#if ENABLE_SWIZZ_IN_SIMPLEFrame
+ (void)load {
Method method1 = class_getInstanceMethod(self, @selector(layoutSubviews));
Method method2 = class_getInstanceMethod(self, @selector(xl_layoutSubviews));
method_exchangeImplementations(method1, method2);
}
- (void)xl_layoutSubviews {
[self xl_layoutSubviews];
!self.frameBlock ?: self.frameBlock(self);
}
- (void)setFrameBlock:(void (^)(UIView * _Nonnull))frameBlock {
if (frameBlock == nil) return ;
objc_setAssociatedObject(self, identifier, frameBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void (^)(UIView * _Nonnull))frameBlock {
return objc_getAssociatedObject(self, identifier);
}
#endif
@end
@@ -0,0 +1,29 @@
//
// UIView+TFExtension.h
// EmotionFM
//
// Created by 谢腾飞 on 2018/10/27.
// Copyright © 2018 xtfei_2011@126.com. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (TFExtension)
@property (nonatomic ,assign) CGPoint xtfei_origin;
@property (nonatomic ,assign) CGSize xtfei_size;
@property (nonatomic ,assign) CGFloat xtfei_width;
@property (nonatomic ,assign) CGFloat xtfei_height;
@property (nonatomic ,assign) CGFloat xtfei_x;
@property (nonatomic ,assign) CGFloat xtfei_y;
@property (nonatomic ,assign) CGFloat xtfei_centerX;
@property (nonatomic ,assign) CGFloat xtfei_centerY;
@property (nonatomic ,assign) CGFloat xtfei_right;
@property (nonatomic ,assign) CGFloat xtfei_bottom;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,129 @@
//
// UIView+TFExtension.m
// EmotionFM
//
// Created by 谢腾飞 on 2018/10/27.
// Copyright © 2018 xtfei_2011@126.com. All rights reserved.
//
#import "UIView+TFExtension.h"
@implementation UIView (TFExtension)
- (CGPoint)xtfei_origin
{
return self.frame.origin;
}
- (void)setXtfei_origin:(CGPoint)point
{
CGRect rect = self.frame;
rect.origin = point;
self.frame = rect;
}
- (CGSize)xtfei_size
{
return self.frame.size;
}
- (void)setXtfei_size:(CGSize)xtfei_size
{
CGRect frame = self.frame;
frame.size = xtfei_size;
self.frame = frame;
}
- (CGFloat)xtfei_width
{
return self.frame.size.width;
}
- (CGFloat)xtfei_height
{
return self.frame.size.height;
}
- (void)setXtfei_width:(CGFloat)xtfei_width
{
CGRect frame = self.frame;
frame.size.width = xtfei_width;
self.frame = frame;
}
- (void)setXtfei_height:(CGFloat)xtfei_height
{
CGRect frame = self.frame;
frame.size.height = xtfei_height;
self.frame = frame;
}
- (CGFloat)xtfei_x
{
return self.frame.origin.x;
}
- (void)setXtfei_x:(CGFloat)xtfei_x
{
CGRect frame = self.frame;
frame.origin.x = xtfei_x;
self.frame = frame;
}
- (CGFloat)xtfei_y
{
return self.frame.origin.y;
}
- (void)setXtfei_y:(CGFloat)xtfei_y
{
CGRect frame = self.frame;
frame.origin.y = xtfei_y;
self.frame = frame;
}
- (CGFloat)xtfei_centerX
{
return self.center.x;
}
- (void)setXtfei_centerX:(CGFloat)xtfei_centerX
{
CGPoint center = self.center;
center.x = xtfei_centerX;
self.center = center;
}
- (CGFloat)xtfei_centerY
{
return self.center.y;
}
- (void)setXtfei_centerY:(CGFloat)xtfei_centerY
{
CGPoint center = self.center;
center.y = xtfei_centerY;
self.center = center;
}
- (CGFloat)xtfei_right
{
return CGRectGetMaxX(self.frame);
}
- (CGFloat)xtfei_bottom
{
return CGRectGetMaxY(self.frame);
}
- (void)setXtfei_right:(CGFloat)xtfei_right
{
self.xtfei_x = xtfei_right - self.xtfei_width;
}
- (void)setXtfei_bottom:(CGFloat)xtfei_bottom
{
self.xtfei_y = xtfei_bottom - self.xtfei_height;
}
@end
@@ -0,0 +1,21 @@
//
// UIViewController+Present.h
// WXReader
//
// Created by Andrew on 2019/10/9.
// Copyright © 2019 Andrew. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIViewController (Present)
@property (nonatomic, assign) BOOL LL_automaticallySetModalPresentationStyle;
+ (BOOL)LL_automaticallySetModalPresentationStyle;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,53 @@
//
// UIViewController+Present.m
// WXReader
//
// Created by Andrew on 2019/10/9.
// Copyright © 2019 Andrew. All rights reserved.
//
#import "UIViewController+Present.h"
#import <objc/runtime.h>
static const char *LL_automaticallySetModalPresentationStyleKey;
@implementation UIViewController (Present)
+ (void)load {
Method originAddObserverMethod = class_getInstanceMethod(self, @selector(presentViewController:animated:completion:));
Method swizzledAddObserverMethod = class_getInstanceMethod(self, @selector(LL_presentViewController:animated:completion:));
method_exchangeImplementations(originAddObserverMethod, swizzledAddObserverMethod);
}
- (void)setLL_automaticallySetModalPresentationStyle:(BOOL)LL_automaticallySetModalPresentationStyle {
objc_setAssociatedObject(self, LL_automaticallySetModalPresentationStyleKey, @(LL_automaticallySetModalPresentationStyle), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)LL_automaticallySetModalPresentationStyle {
id obj = objc_getAssociatedObject(self, LL_automaticallySetModalPresentationStyleKey);
if (obj) {
return [obj boolValue];
}
return [self.class LL_automaticallySetModalPresentationStyle];
}
+ (BOOL)LL_automaticallySetModalPresentationStyle {
if ([self isKindOfClass:[UIImagePickerController class]] || [self isKindOfClass:[UIAlertController class]]) {
return NO;
}
return YES;
}
- (void)LL_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
if (@available(iOS 13.0, *)) {
if (viewControllerToPresent.LL_automaticallySetModalPresentationStyle && ![NSStringFromClass([viewControllerToPresent class]) isEqualToString:@"BUDislikeViewController"] && ![NSStringFromClass([viewControllerToPresent class]) isEqualToString:@"BUDislikeNewViewController"]) {
viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
}
[self LL_presentViewController:viewControllerToPresent animated:flag completion:completion];
} else {
// Fallback on earlier versions
[self LL_presentViewController:viewControllerToPresent animated:flag completion:completion];
}
}
@end
@@ -0,0 +1,17 @@
//
// YYAnimatedImageView+Util.h
// WXReader
//
// Created by LL on 2020/10/9.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "YYAnimatedImageView.h"
NS_ASSUME_NONNULL_BEGIN
@interface YYAnimatedImageView (Util)
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,37 @@
//
// YYAnimatedImageView+Util.m
// WXReader
//
// Created by LL on 2020/10/9.
// Copyright © 2020 Andrew. All rights reserved.
//
#import "YYAnimatedImageView+Util.h"
@implementation YYAnimatedImageView (Util)
+ (void)load {
// 获取系统的对象方法
Method displayLayerMethod = class_getInstanceMethod(self, @selector(displayLayer:));
// 获取自己定义的对象方法
Method displayLayerNewMethod = class_getInstanceMethod(self, @selector(displayLayerNew:));
// 方法交换
method_exchangeImplementations(displayLayerMethod, displayLayerNewMethod);
}
- (void)displayLayerNew:(CALayer *)layer {
Ivar imgIvar = class_getInstanceVariable([self class], "_curFrame");
UIImage *img = object_getIvar(self, imgIvar);
if (img) {
layer.contents = (__bridge id)img.CGImage;
} else {
if (@available(iOS 14.0, *)) {
[super displayLayer:layer];
}
}
}
@end