1
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#import "UICollectionViewLeftAlignedLayout.h"
|
||||
#import "KBMyKeyboardCell.h"
|
||||
#import "KBAlert.h"
|
||||
#import "KBMyVM.h"
|
||||
|
||||
/// 复用标识
|
||||
static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
@@ -29,6 +30,7 @@ static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
|
||||
// 数据源(必须是二维数组,库内部会在拖动时直接调整顺序)
|
||||
@property (nonatomic, strong) NSMutableArray<NSMutableArray<NSDictionary *> *> *dataSourceArray; // {emoji,title}
|
||||
@property (nonatomic, strong) KBMyVM *viewModel; // 我的页面 VM
|
||||
|
||||
@end
|
||||
|
||||
@@ -40,6 +42,7 @@ static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.viewModel = [[KBMyVM alloc] init];
|
||||
self.view.backgroundColor = [UIColor colorWithHex:0xF6F8F9];
|
||||
self.kb_navView.backgroundColor = [UIColor clearColor];
|
||||
self.kb_titleLabel.text = @"My KeyBoard";
|
||||
@@ -71,7 +74,11 @@ static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
}];
|
||||
|
||||
// 初始数据
|
||||
[self buildDefaultData];
|
||||
// [self buildDefaultData];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.viewModel fetchCharacterListByUserWithCompletion:^(NSArray<KBCharacter *> * _Nonnull characterArray, NSError * _Nullable error) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
@@ -326,17 +326,26 @@
|
||||
|
||||
- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14.0)) {
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
PHPickerResult *first = results.firstObject; if (!first) return;
|
||||
|
||||
PHPickerResult *first = results.firstObject;
|
||||
if (!first) return;
|
||||
|
||||
NSItemProvider *p = first.itemProvider;
|
||||
if ([p canLoadObjectOfClass:UIImage.class]) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[p loadObjectOfClass:UIImage.class completionHandler:^(__kindof id<NSItemProviderReading> _Nullable object, NSError * _Nullable error) {
|
||||
UIImage *img = ([object isKindOfClass:UIImage.class] ? (UIImage *)object : nil);
|
||||
if (!img) return;
|
||||
|
||||
// 这里就在子线程里压缩,避免卡 UI
|
||||
// 比如目标 100KB,你自己改成想要的值
|
||||
NSUInteger targetKB = 50;
|
||||
NSData *compressedData = [weakSelf kb_compressImage:img targetKB:targetKB];
|
||||
if (!compressedData) return;
|
||||
UIImage *compressedImage = [UIImage imageWithData:compressedData];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIImage *compressed = [weakSelf kb_compressImage:img maxPixel:512 quality:0.85];
|
||||
weakSelf.avatarView.image = compressed;
|
||||
weakSelf.avatarJPEGData = UIImageJPEGRepresentation(compressed, 0.85);
|
||||
|
||||
|
||||
});
|
||||
}];
|
||||
}
|
||||
@@ -378,6 +387,95 @@
|
||||
UIImage *result = [UIImage imageWithData:jpeg] ?: scaled ?: image;
|
||||
return result;
|
||||
}
|
||||
/// 按目标大小(KB)压缩图片:
|
||||
/// 1. 先逐步降低 JPEG 质量;
|
||||
/// 2. 如果质量已经降到下限仍然太大,再按比例缩小尺寸,并重复尝试。
|
||||
- (nullable NSData *)kb_compressImage:(UIImage *)image targetKB:(NSUInteger)targetKB {
|
||||
if (!image || targetKB == 0) { return nil; }
|
||||
|
||||
NSUInteger maxBytes = targetKB * 1024;
|
||||
|
||||
// 初始质量参数
|
||||
CGFloat compression = 0.9f;
|
||||
CGFloat minCompression = 0.1f; // 不建议再低了,太低会糊
|
||||
NSData *data = UIImageJPEGRepresentation(image, compression);
|
||||
if (!data) return nil;
|
||||
|
||||
// 如果一开始就小于目标大小,直接返回
|
||||
if (data.length <= maxBytes) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// 1) 先通过降低质量来压缩
|
||||
while (data.length > maxBytes && compression > minCompression + 0.01f) {
|
||||
compression -= 0.1f;
|
||||
data = UIImageJPEGRepresentation(image, compression);
|
||||
if (!data) return nil;
|
||||
}
|
||||
|
||||
if (data.length <= maxBytes) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// 2) 质量降到下限还是太大 -> 等比缩放尺寸
|
||||
UIImage *currentImage = image;
|
||||
|
||||
// 防止死循环,限定最多缩放几次
|
||||
NSInteger maxResizeCount = 6;
|
||||
NSInteger resizeCount = 0;
|
||||
|
||||
while (data.length > maxBytes && resizeCount < maxResizeCount) {
|
||||
resizeCount++;
|
||||
|
||||
// 按面积比例来算缩放因子:新的面积约等于 (maxBytes / 当前字节数) * 原面积
|
||||
CGFloat ratio = (CGFloat)maxBytes / (CGFloat)data.length;
|
||||
// 为了更激进一点,可以乘个经验系数,比如 0.8
|
||||
ratio *= 0.8f;
|
||||
if (ratio <= 0.0f) {
|
||||
ratio = 0.5f; // 兜底,至少缩小一半
|
||||
}
|
||||
|
||||
CGFloat scale = sqrt(ratio);
|
||||
if (scale >= 1.0f) {
|
||||
scale = 0.5f; // 理论上不会走到这里,兜底
|
||||
}
|
||||
|
||||
CGSize newSize = CGSizeMake(currentImage.size.width * scale,
|
||||
currentImage.size.height * scale);
|
||||
if (newSize.width < 10 || newSize.height < 10) {
|
||||
// 太小就没意义了,直接 break,返回目前能做到的最小值
|
||||
break;
|
||||
}
|
||||
|
||||
// 绘制缩小后的图片
|
||||
UIGraphicsBeginImageContextWithOptions(newSize, NO, currentImage.scale);
|
||||
[currentImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
|
||||
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
if (!resizedImage) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentImage = resizedImage;
|
||||
|
||||
// 每次尺寸缩小之后,再重新从稍高一点的质量开始压一轮
|
||||
compression = 0.9f;
|
||||
data = UIImageJPEGRepresentation(currentImage, compression);
|
||||
if (!data) return nil;
|
||||
|
||||
// 再次在质量范围内往下压
|
||||
while (data.length > maxBytes && compression > minCompression + 0.01f) {
|
||||
compression -= 0.1f;
|
||||
data = UIImageJPEGRepresentation(currentImage, compression);
|
||||
if (!data) return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// 最终返回当前能做到的最小数据(可能略大于目标,但已经尽力)
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
- (KBMyVM *)myVM{
|
||||
if (!_myVM) {
|
||||
|
||||
Reference in New Issue
Block a user