海报分享

This commit is contained in:
pengguangjian 2024-09-27 17:42:08 +08:00
parent a02eb85c53
commit 6d4694dfab
464 changed files with 50673 additions and 2637 deletions

View File

@ -30,7 +30,12 @@ pod 'YYText'
#pod 'AMapLocation'
#pod 'AMapSearch'
pod 'HXPhotoPicker'
#pod 'HXPhotoPicker-Lite'
#pod 'HXPhotoPicker-Lite/Picker'
#pod 'HXPhotoPicker-Lite/Editor'
#pod 'HXPhotoPicker-Lite/Camera'
post_install do |installer|

View File

@ -15,6 +15,9 @@ PODS:
- AFNetworking/UIKit (4.0.1):
- AFNetworking/NSURLSession
- DZNEmptyDataSet (1.8.1)
- HXPhotoPicker (3.3.2):
- HXPhotoPicker/Default (= 3.3.2)
- HXPhotoPicker/Default (3.3.2)
- IQKeyboardManager (6.5.10)
- LSTTimer (0.2.10)
- Masonry (1.1.0)
@ -37,6 +40,7 @@ PODS:
DEPENDENCIES:
- AFNetworking
- DZNEmptyDataSet
- HXPhotoPicker
- IQKeyboardManager
- LSTTimer
- Masonry
@ -57,6 +61,7 @@ SPEC REPOS:
trunk:
- AFNetworking
- DZNEmptyDataSet
- HXPhotoPicker
- IQKeyboardManager
- LSTTimer
- Masonry
@ -76,6 +81,7 @@ SPEC REPOS:
SPEC CHECKSUMS:
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7
HXPhotoPicker: 3d0526bc46cd2a4510a777d5f57dfad8e683fcfd
IQKeyboardManager: 45a1fa55c1a5b02c61ac0fd7fd5b62bb4ad20d97
LSTTimer: caf8f02ff366ca175cf4c1778d26c166183c1b6f
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
@ -92,6 +98,6 @@ SPEC CHECKSUMS:
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
PODFILE CHECKSUM: 3a3f16ff03dd509a5468a270e3ff3de69a081d1e
PODFILE CHECKSUM: b0cff93312a33561a9d76a35193aedc3fc970c7f
COCOAPODS: 1.15.2

View File

@ -0,0 +1,34 @@
//
// NSArray+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/7.
// Copyright © 2019年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class AVAsset, HXPhotoModel;
@interface NSArray (HXExtension)
/// 获取image同时获取
/// 如果model是视频的话,获取的则是视频封面
/// @param original 是否原图
/// @param completion imageArray 获取成功的image数组, errorArray 获取失败的model数组
- (void)hx_requestImageWithOriginal:(BOOL)original completion:(void (^)(NSArray<UIImage *> * _Nullable imageArray, NSArray<HXPhotoModel *> * _Nullable errorArray))completion;
/// 分别获取image前面一个获取完了再去获取第二个
/// @param original 是否原图
/// @param completion imageArray 获取成功的image数组, errorArray 获取失败的model数组
- (void)hx_requestImageSeparatelyWithOriginal:(BOOL)original completion:(void (^)(NSArray<UIImage *> * _Nullable imageArray, NSArray<HXPhotoModel *> * _Nullable errorArray))completion;
/// 获取imageData
/// @param completion 获取失败的不会添加到数组中
- (void)hx_requestImageDataWithCompletion:(void (^)(NSArray<NSData *> * _Nullable imageDataArray))completion;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,369 @@
//
// NSArray+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/7.
// Copyright © 2019 Silence. All rights reserved.
//
#import "NSArray+HXExtension.h"
#import "HXPhotoModel.h"
#import "HXPhotoEdit.h"
#import "HXPhotoManager.h"
@implementation NSArray (HXExtension)
- (BOOL)hx_detection {
if (!self.count) {
return NO;
}
for (id obj in self) {
if (![obj respondsToSelector:@selector(isKindOfClass:)]) {
return NO;
}
if (![obj isKindOfClass:[HXPhotoModel class]]) {
return NO;
}
}
return YES;
}
+ (NSArray *)hx_dictHandler:(NSDictionary *)dict {
NSMutableArray *dataArray = [NSMutableArray array];
NSArray *keys = [dict.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString * obj1, NSString * obj2) {
if (obj1.integerValue > obj2.integerValue) {
return NSOrderedDescending;
}else if (obj1.integerValue < obj2.integerValue) {
return NSOrderedAscending;
}else {
return NSOrderedSame;
}
}];
for (NSString *key in keys) {
if ([dict[key] isKindOfClass:[NSError class]]) {
continue;
}
if ([dict[key] isKindOfClass:[NSString class]]) {
NSString *path = dict[key];
UIImage *image = [self hx_disposeHEICWithPath:path];
if (image) {
if (image.imageOrientation != UIImageOrientationUp) {
image = [image hx_normalizedImage];
}
[dataArray addObject:image];
}
}else {
[dataArray addObject:dict[key]];
}
}
return dataArray.copy;
}
+ (UIImage *)hx_disposeHEICWithPath:(NSString *)path {
if ([path.pathExtension isEqualToString:@"HEIC"]) {
// HEIC
CIImage *ciImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:path]];
CIContext *context = [CIContext context];
NSString *key = (__bridge NSString *)kCGImageDestinationLossyCompressionQuality;
NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{key : @1}];
UIImage *image = [UIImage imageWithData:jpgData];
if (image.imageOrientation != UIImageOrientationUp) {
image = [image hx_normalizedImage];
}
return image;
}else {
NSData *imageData = [NSData dataWithContentsOfFile:path];
UIImage *image = [UIImage imageWithData:imageData];
if (!image) {
if (image.imageOrientation != UIImageOrientationUp) {
image = [image hx_normalizedImage];
}
image = [UIImage imageWithContentsOfFile:path];
}
return image;
}
}
- (void)hx_requestImageWithOriginal:(BOOL)original completion:(nonnull void (^)(NSArray<UIImage *> * _Nullable, NSArray<HXPhotoModel *> * _Nullable))completion {
if (![self hx_detection] || !self.count) {
if (completion) {
completion(nil, self);
}
if (HXShowLog) NSSLog(@"数组里装的不是HXPhotoModel对象或者为空");
return;
}
NSInteger count = self.count;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (int i = 0 ; i < self.count; i++) {
HXPhotoModel *model = self[i];
[dict setValue:[NSError errorWithDomain:@"获取失败" code:99999 userInfo:nil] forKey:model.selectIndexStr];
}
__block NSInteger index = 0;
NSMutableArray *errorArray = [NSMutableArray array];
for (HXPhotoModel *model in self) {
[self requestImageWithOriginal:original photoModel:model successful:^(UIImage * _Nullable image, NSURL * _Nullable imagePath, HXPhotoModel *photoModel) {
if (image) {
// photoModel nil
[dict setObject:image forKey:photoModel.selectIndexStr];
}else if (imagePath) {
UIImage *hImage = [NSArray hx_disposeHEICWithPath:imagePath.relativePath];
if (hImage) {
photoModel.thumbPhoto = hImage;
photoModel.previewPhoto = hImage;
[dict setObject:hImage forKey:photoModel.selectIndexStr];
}else {
[errorArray addObject:photoModel];
}
}else {
[errorArray addObject:photoModel];
}
index++;
if (index == count) {
if (completion) {
completion([NSArray hx_dictHandler:dict], errorArray);
}
}
} failure:^(HXPhotoModel *photoModel) {
[errorArray addObject:photoModel];
index++;
if (index == count) {
if (completion) {
completion([NSArray hx_dictHandler:dict], errorArray);
}
}
}];
}
}
- (void)hx_requestImageSeparatelyWithOriginal:(BOOL)original completion:(void (^)(NSArray<UIImage *> * _Nullable, NSArray<HXPhotoModel *> * _Nullable))completion {
if (![self hx_detection] || !self.count) {
if (completion) {
completion(nil, self);
}
if (HXShowLog) NSSLog(@"数组里装的不是HXPhotoModel对象或者为空");
return;
}
[self requestImageSeparatelyWithOriginal:original imageList:@[].mutableCopy photoModels:self.mutableCopy errorPhotoModels:@[].mutableCopy completion:completion];
}
- (void)requestImageSeparatelyWithOriginal:(BOOL)original
imageList:(NSMutableArray *)imageList
photoModels:(NSMutableArray *)photoModels
errorPhotoModels:(NSMutableArray *)errorPhotoModels
completion:(void (^)(NSArray<UIImage *> * _Nullable imageArray, NSArray<HXPhotoModel *> * _Nullable errorArray))completion {
if (!photoModels.count) {
if (completion) {
completion(imageList, errorPhotoModels);
}
return;
}
if (!imageList) imageList = [NSMutableArray array];
if (!errorPhotoModels) errorPhotoModels = [NSMutableArray array];
HXPhotoModel *model = photoModels.firstObject;
HXWeakSelf
[self requestImageWithOriginal:original photoModel:model successful:^(UIImage * _Nullable image, NSURL * _Nullable imagePath, HXPhotoModel *photoModel) {
if (image) {
photoModel.thumbPhoto = image;
photoModel.previewPhoto = image;
[imageList addObject:image];
}else if (imagePath) {
UIImage *hImage = [NSArray hx_disposeHEICWithPath:imagePath.relativePath];
if (hImage) {
photoModel.thumbPhoto = hImage;
photoModel.previewPhoto = hImage;
[imageList addObject:hImage];
}else {
//iPhone 8iOS 10.0.2requestImageDataStartRequestICloudURLURLimagerequestPreviewImageWithSizeimagetmp
[model requestPreviewImageWithSize:PHImageManagerMaximumSize startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel * _Nullable model) {
} progressHandler:^(double progress, HXPhotoModel * _Nullable model) {
} success:^(UIImage * _Nullable imageValue, HXPhotoModel * _Nullable model, NSDictionary * _Nullable info) {
NSString *photoPathStr = [NSTemporaryDirectory() stringByAppendingString:@"HXPhotoPickerSave/"];
BOOL isDir;
BOOL isDirExit = [[NSFileManager defaultManager] fileExistsAtPath:photoPathStr isDirectory:&isDir];
NSError *error;
if (isDirExit == NO) {
[[NSFileManager defaultManager] createDirectoryAtPath:photoPathStr withIntermediateDirectories:YES attributes:nil error:&error];
}
if (error) {
[errorPhotoModels addObject:photoModel];
}
else {
NSInteger timeStamp = [[NSDate new] timeIntervalSince1970];
NSString *imgPath = [NSString stringWithFormat:@"%@%zd_%zd.jpg",photoPathStr, timeStamp, photoModels.count];
[UIImageJPEGRepresentation(imageValue, 1.0) writeToFile:imgPath atomically:YES];
photoModel.imageURL = [NSURL fileURLWithPath:imgPath];
photoModel.thumbPhoto = imageValue;
photoModel.previewPhoto = imageValue;
[imageList addObject:imageValue];
}
[photoModels removeObjectAtIndex:0];
if (!photoModels.count) {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
}else {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
}
} failed:^(NSDictionary * _Nullable info, HXPhotoModel * _Nullable model) {
[errorPhotoModels addObject:photoModel];
[photoModels removeObjectAtIndex:0];
if (!photoModels.count) {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
}else {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
}
}];
return;
}
}else {
[errorPhotoModels addObject:photoModel];
}
[photoModels removeObjectAtIndex:0];
if (!photoModels.count) {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
}else {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
}
} failure:^(HXPhotoModel *photoModel) {
[errorPhotoModels addObject:photoModel];
[photoModels removeObjectAtIndex:0];
if (!photoModels.count) {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList.copy photoModels:photoModels errorPhotoModels:errorPhotoModels.copy completion:completion];
}else {
[weakSelf requestImageSeparatelyWithOriginal:original imageList:imageList photoModels:photoModels errorPhotoModels:errorPhotoModels completion:completion];
}
}];
}
- (void)requestImageWithOriginal:(BOOL)original photoModel:(HXPhotoModel *)photoModel successful:(void (^)(UIImage * _Nullable image, NSURL * _Nullable imagePath, HXPhotoModel *photoModel))successful failure:(void (^)(HXPhotoModel *photoModel))failure {
if (photoModel.photoEdit) {
UIImage *image = photoModel.photoEdit.editPreviewImage;
if (successful) {
successful(image, nil, photoModel);
}
return;
}
if (photoModel.type == HXPhotoModelMediaTypeCameraPhoto) {
if (photoModel.networkPhotoUrl) {
if ([HXPhotoCommon photoCommon].requestNetworkAfter) {
// [HXPhotoCommon photoCommon].requestNetworkAfter = YES
[HXPhotoModel requestImageWithURL:photoModel.networkPhotoUrl progress:nil completion:^(UIImage * _Nullable image, NSURL * _Nullable url, NSError * _Nullable error) {
if (image) {
photoModel.thumbPhoto = image;
photoModel.previewPhoto = image;
if (successful) {
successful(image, nil, photoModel);
}
}else {
if (failure) {
failure(photoModel);
}
if (HXShowLog) NSSLog(@"网络图片获取失败!");
}
}];
}else {
if (successful) {
successful(photoModel.thumbPhoto, nil, photoModel);
}
}
}else {
//
if (successful) {
successful(photoModel.thumbPhoto, nil, photoModel);
}
}
return;
}else {
if (!photoModel.asset) {
if (photoModel.thumbPhoto) {
if (successful) {
successful(photoModel.thumbPhoto, nil, photoModel);
}
}else {
if (failure) {
failure(photoModel);
}
}
return;
}
}
if ((original && photoModel.type != HXPhotoModelMediaTypeVideo) ||
photoModel.type == HXPhotoModelMediaTypePhotoGif) {
//
[photoModel getAssetURLWithSuccess:^(NSURL * _Nullable imageURL, HXPhotoModelMediaSubType mediaType, BOOL isNetwork, HXPhotoModel * _Nullable model) {
if (successful) {
successful(nil, imageURL, model);
}
} failed:^(NSDictionary * _Nullable info, HXPhotoModel * _Nullable model) {
if (failure) {
failure(model);
}
}];
}else {
[photoModel requestImageDataStartRequestICloud:nil progressHandler:nil success:^(NSData * _Nullable imageData, UIImageOrientation orientation, HXPhotoModel * _Nullable model, NSDictionary * _Nullable info) {
UIImage *image = [UIImage imageWithData:imageData];
if (image.imageOrientation != UIImageOrientationUp) {
image = [image hx_normalizedImage];
}
//
if (!original) {
image = [image hx_scaleImagetoScale:0.5f];
}
if (successful) {
successful(image, nil, model);
}
} failed:^(NSDictionary * _Nullable info, HXPhotoModel * _Nullable model) {
if (model.previewPhoto) {
if (successful) {
successful(model.previewPhoto, nil, model);
}
}else {
if (failure) {
failure(model);
}
}
}];
}
}
- (void)hx_requestImageDataWithCompletion:(void (^)(NSArray<NSData *> * _Nullable imageDataArray))completion {
if (![self hx_detection] || !self.count) {
if (completion) {
completion(nil);
}
if (HXShowLog) NSSLog(@"数组里装的不是HXPhotoModel对象或者为空");
return;
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (int i = 0 ; i < self.count; i++) {
HXPhotoModel *model = self[i];
[dict setValue:[NSError errorWithDomain:@"获取失败" code:99999 userInfo:nil] forKey:model.selectIndexStr];
}
__block NSInteger index = 0;
NSInteger count = self.count;
for (HXPhotoModel *model in self) {
if (model.subType != HXPhotoModelMediaSubTypePhoto) {
continue;
}
[model requestImageDataStartRequestICloud:nil progressHandler:nil success:^(NSData *imageData, UIImageOrientation orientation, HXPhotoModel *model, NSDictionary *info) {
if (model.asset && [HXPhotoTools assetIsHEIF:model.asset]) {
CIImage *ciImage = [CIImage imageWithData:imageData];
CIContext *context = [CIContext context];
NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}];
[dict setObject:jpgData forKey:model.selectIndexStr];
}else {
[dict setObject:imageData forKey:model.selectIndexStr];
}
index++;
if (index == count) {
if (completion) {
completion([NSArray hx_dictHandler:dict]);
}
}
} failed:^(NSDictionary *info, HXPhotoModel *model) {
index++;
if (HXShowLog) NSSLog(@"一个获取失败!");
if (index == count) {
if (completion) {
completion([NSArray hx_dictHandler:dict]);
}
}
}];
}
}
@end

View File

@ -0,0 +1,15 @@
//
// NSBundle+HXPhotoPicker.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/7/25.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSBundle (HXPhotoPicker)
+ (instancetype)hx_photoPickerBundle;
+ (NSString *)hx_localizedStringForKey:(NSString *)key value:(NSString *)value;
+ (NSString *)hx_localizedStringForKey:(NSString *)key;
@end

View File

@ -0,0 +1,43 @@
//
// NSBundle+HXPhotopicker.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/7/25.
// Copyright © 2017 Silence. All rights reserved.
//
#import "NSBundle+HXPhotoPicker.h"
#import "HXPhotoCommon.h"
@implementation NSBundle (HXPhotoPicker)
+ (instancetype)hx_photoPickerBundle {
static NSBundle *hxBundle = nil;
if (hxBundle == nil) {
NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(@"HXPhotoPicker")];
NSString *path = [bundle pathForResource:@"HXPhotoPicker" ofType:@"bundle"];
//使framework
if (!path) {
NSURL *associateBundleURL = [[NSBundle mainBundle] URLForResource:@"Frameworks" withExtension:nil];
if (associateBundleURL) {
associateBundleURL = [associateBundleURL URLByAppendingPathComponent:@"HXPhotoPicker"];
associateBundleURL = [associateBundleURL URLByAppendingPathExtension:@"framework"];
NSBundle *associateBunle = [NSBundle bundleWithURL:associateBundleURL];
path = [associateBunle pathForResource:@"HXPhotoPicker" ofType:@"bundle"];
}
}
hxBundle = path ? [NSBundle bundleWithPath:path]: [NSBundle mainBundle];
}
return hxBundle;
}
+ (NSString *)hx_localizedStringForKey:(NSString *)key {
return [self hx_localizedStringForKey:key value:nil];
}
+ (NSString *)hx_localizedStringForKey:(NSString *)key value:(NSString *)value {
NSBundle *bundle = [HXPhotoCommon photoCommon].languageBundle;
value = [bundle localizedStringForKey:key value:value table:nil];
if (!value) {
value = key;
}
return value;
}
@end

View File

@ -0,0 +1,51 @@
//
// NSDate+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSDate (HXExtension)
/**
@return yes or no
*/
- (BOOL)hx_isToday;
/**
@return yes or no
*/
- (BOOL)hx_isYesterday;
/**
@return yes or no
*/
- (BOOL)hx_isThisYear;
/**
@return yes or no
*/
- (BOOL)hx_isSameWeek;
- (NSString *)hx_getNowWeekday;
/**
@param format
@return
*/
- (NSString *)hx_dateStringWithFormat:(NSString *)format;
@end

View File

@ -0,0 +1,199 @@
//
// NSDate+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017 Silence. All rights reserved.
//
#import "NSDate+HXExtension.h"
#import "HXPhotoCommon.h"
@implementation NSDate (HXExtension)
/**
*
*/
- (BOOL)hx_isToday {
NSCalendar *calendar = [NSCalendar currentCalendar];
int unit = NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear;
// 1.
NSDateComponents *nowCmps = [calendar components:unit fromDate:[NSDate date]];
// 2.self
NSDateComponents *selfCmps = [calendar components:unit fromDate:self];
return
(selfCmps.year == nowCmps.year) &&
(selfCmps.month == nowCmps.month) &&
(selfCmps.day == nowCmps.day);
}
/**
*
*/
- (BOOL)hx_isYesterday {
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyyMMdd";
//
NSString *selfString = [fmt stringFromDate:self];
NSString *nowString = [fmt stringFromDate:[NSDate date]];
//
NSDate *selfDate = [fmt dateFromString:selfString];
NSDate *nowDate = [fmt dateFromString:nowString];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSDateComponents *cmps = [calendar components:unit fromDate:selfDate toDate:nowDate options:0];
return cmps.year == 0
&& cmps.month == 0
&& cmps.day == 1;
}
/**
*
*/
- (BOOL)hx_isThisYear {
NSCalendar *calendar = [NSCalendar currentCalendar];
int unit = NSCalendarUnitYear;
// 1.
NSDateComponents *nowCmps = [calendar components:unit fromDate:[NSDate date]];
// 2.self
NSDateComponents *selfCmps = [calendar components:unit fromDate:self];
return nowCmps.year == selfCmps.year;
}
/**
*/
- (BOOL)hx_isSameWeek {
NSCalendar *calendar = [NSCalendar currentCalendar];
int unit = NSCalendarUnitWeekday | NSCalendarUnitMonth | NSCalendarUnitYear | kCFCalendarUnitDay | kCFCalendarUnitHour | kCFCalendarUnitMinute ;
//1.
NSDateComponents *nowCmps = [calendar components:unit fromDate:[NSDate date]];
NSDateComponents *sourceCmps = [calendar components:unit fromDate:self];
//
NSDateComponents *dateCom = [calendar components:unit fromDate:[NSDate date] toDate:self options:0];
NSInteger subDay = labs(dateCom.day);
NSInteger subMonth = labs(dateCom.month);
NSInteger subYear = labs(dateCom.year);
if (subYear == 0 && subMonth == 0) { //
if (subDay > 6) { //6
return NO;
} else { //weekday
if (dateCom.day >= 0 && dateCom.hour >=0 && dateCom.minute >= 0) { //
//西12
NSInteger chinaWeekday = sourceCmps.weekday == 1 ? 7 : sourceCmps.weekday - 1;
if (subDay >= chinaWeekday) {
return NO;
} else {
return YES;
}
} else {
NSInteger chinaWeekday = sourceCmps.weekday == 1 ? 7 : nowCmps.weekday - 1;
if (subDay >= chinaWeekday) { //
return NO;
} else {
return YES;
}
}
}
} else { //
return NO;
}
}
- (NSString *)hx_getNowWeekday {
NSDateFormatter *dateday = [[NSDateFormatter alloc] init];
HXPhotoLanguageType type = [HXPhotoCommon photoCommon].languageType;
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-Hans"];
if (type == HXPhotoLanguageTypeSc) {
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-Hans"];
}else if (type == HXPhotoLanguageTypeTc) {
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-Hant"];
}else if (type == HXPhotoLanguageTypeJa) {
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"ja"];
}else if (type == HXPhotoLanguageTypeKo) {
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"ko"];
}else if (type == HXPhotoLanguageTypeEn) {
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"];
}
if (locale) {
[dateday setLocale:locale];
}
[dateday setDateFormat:@"MMM dd"];
[dateday setDateFormat:@"EEE"];
// switch (type) {
// case HXPhotoLanguageTypeSc :
// case HXPhotoLanguageTypeTc :
// case HXPhotoLanguageTypeJa : {
// // / /
// [dateday setDateFormat:@"MM月dd日"];
// [dateday setDateFormat:@"EEEE"];
// } break;
// case HXPhotoLanguageTypeKo : {
// //
// [dateday setDateFormat:@"MM월dd일"];
// [dateday setDateFormat:@"EEEE"];
// } break;
// case HXPhotoLanguageTypeEn : {
// //
// [dateday setDateFormat:@"MMM dd"];
// [dateday setDateFormat:@"EEE"];
// } break;
// default : {
// NSString *language = [NSLocale preferredLanguages].firstObject;
// if ([language hasPrefix:@"zh"] ||
// [language hasPrefix:@"ja"]) {
// // / /
// [dateday setDateFormat:@"MM月dd日"];
// [dateday setDateFormat:@"EEEE"];
// }else if ([language hasPrefix:@"ko"]) {
// //
// [dateday setDateFormat:@"MM월dd일"];
// [dateday setDateFormat:@"EEEE"];
// } else {
// //
// [dateday setDateFormat:@"MMM dd"];
// [dateday setDateFormat:@"EEE"];
// }
// }break;
// }
return [dateday stringFromDate:self];
}
- (NSString *)hx_dateStringWithFormat:(NSString *)format {
NSDateFormatter *formater = [[NSDateFormatter alloc] init];
formater.dateFormat = format;
HXPhotoLanguageType type = [HXPhotoCommon photoCommon].languageType;
NSLocale *locale;
switch (type) {
case HXPhotoLanguageTypeEn:
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"];
break;
case HXPhotoLanguageTypeSc:
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-Hans"];
break;
case HXPhotoLanguageTypeTc:
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-Hant"];
break;
case HXPhotoLanguageTypeJa:
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"ja"];
break;
case HXPhotoLanguageTypeKo:
locale = [[NSLocale alloc] initWithLocaleIdentifier:@"ko"];
break;
default:
break;
}
if (locale) {
[formater setLocale:locale];
}
return [formater stringFromDate:self];
}
@end

View File

@ -0,0 +1,18 @@
//
// NSString+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/10.
// Copyright © 2019年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSString (HXExtension)
- (NSString *)hx_countStrBecomeComma;
+ (NSString *)hx_fileName;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,53 @@
//
// NSString+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/10.
// Copyright © 2019 Silence. All rights reserved.
//
#import "NSString+HXExtension.h"
@implementation NSString (HXExtension)
- (NSString *)hx_countStrBecomeComma {
if (self.length < 3) {
return self;
}
int count = 0;
long long int a = self.longLongValue;
while (a != 0) {
count++;
a /= 10;
}
NSMutableString *string = [NSMutableString stringWithString:self];
NSMutableString *newstring = [NSMutableString string];
while (count > 3) {
count -= 3;
NSRange rang = NSMakeRange(string.length - 3, 3);
NSString *str = [string substringWithRange:rang];
[newstring insertString:str atIndex:0];
[newstring insertString:@"," atIndex:0];
[string deleteCharactersInRange:rang];
}
[newstring insertString:string atIndex:0];
return newstring;
}
+ (NSString *)hx_fileName {
CFUUIDRef uuid = CFUUIDCreate(nil);
NSString *uuidString = (__bridge_transfer NSString*)CFUUIDCreateString(nil, uuid);
CFRelease(uuid);
NSString *uuidStr = [[uuidString stringByReplacingOccurrencesOfString:@"-" withString:@""] lowercaseString];
NSString *name = [NSString stringWithFormat:@"%@",uuidStr];
NSString *fileName = @"";
NSDate *nowDate = [NSDate date];
NSString *dateStr = [NSString stringWithFormat:@"%ld", (long)[nowDate timeIntervalSince1970]];
NSString *numStr = [NSString stringWithFormat:@"%d",arc4random()%10000];
fileName = [fileName stringByAppendingString:@"hx"];
fileName = [fileName stringByAppendingString:dateStr];
fileName = [fileName stringByAppendingString:numStr];
return [NSString stringWithFormat:@"%@%@",name,fileName];
}
@end

View File

@ -0,0 +1,18 @@
//
// NSTimer+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/3.
// Copyright © 2019年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSTimer (HXExtension)
+ (id)hx_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats;
+ (id)hx_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,34 @@
//
// NSTimer+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/3.
// Copyright © 2019 Silence. All rights reserved.
//
#import "NSTimer+HXExtension.h"
@implementation NSTimer (HXExtension)
+ (id)hx_scheduledTimerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats
{
void (^block)(void) = [inBlock copy];
id ret = [self scheduledTimerWithTimeInterval:inTimeInterval target:self selector:@selector(hx_executeSimpleBlock:) userInfo:block repeats:inRepeats];
return ret;
}
+ (id)hx_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(void))inBlock repeats:(BOOL)inRepeats
{
void (^block)(void) = [inBlock copy];
id ret = [self timerWithTimeInterval:inTimeInterval target:self selector:@selector(hx_executeSimpleBlock:) userInfo:block repeats:inRepeats];
return ret;
}
+ (void)hx_executeSimpleBlock:(NSTimer *)inTimer;
{
if([inTimer userInfo])
{
void (^block)(void) = (void (^)(void))[inTimer userInfo];
block();
}
}
@end

View File

@ -0,0 +1,18 @@
//
// PHAsset+HXExtension.h
// HXPhotoPickerExample
//
// Created by Slience on 2021/6/8.
// Copyright © 2021 洪欣. All rights reserved.
//
#import <Photos/Photos.h>
NS_ASSUME_NONNULL_BEGIN
@interface PHAsset (HXExtension)
- (void)hx_checkForModificationsWithAssetPathMethodCompletion:(void (^)(BOOL))completion;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,30 @@
//
// PHAsset+HXExtension.m
// HXPhotoPickerExample
//
// Created by Slience on 2021/6/8.
// Copyright © 2021 . All rights reserved.
//
#import <UIKit/UIKit.h>
#import "PHAsset+HXExtension.h"
#import "HXPhotoDefine.h"
@implementation PHAsset (HXExtension)
- (void)hx_checkForModificationsWithAssetPathMethodCompletion:(void (^)(BOOL))completion {
[self requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
AVAsset *avAsset;
if HX_IOS9Later {
avAsset = contentEditingInput.audiovisualAsset;
}else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
avAsset = contentEditingInput.avAsset;
#pragma clang diagnostic pop
}
NSString *path = avAsset ? [avAsset description] : contentEditingInput.fullSizeImageURL.path;
completion([path containsString:@"/Mutations/"]);
}];
}
@end

View File

@ -0,0 +1,14 @@
//
// UIButton+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 17/2/16.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIButton (HXExtension)
/** 扩大buuton点击范围 */
- (void)hx_setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left;
@end

View File

@ -0,0 +1,53 @@
//
// UIButton+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 17/2/16.
// Copyright © 2017 Silence. All rights reserved.
//
#import "UIButton+HXExtension.h"
#import <objc/runtime.h>
@implementation UIButton (HXExtension)
static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;
- (void)hx_setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left
{
objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (CGRect)enlargedRect{
NSNumber* topEdge = objc_getAssociatedObject(self, &topNameKey);
NSNumber* rightEdge = objc_getAssociatedObject(self, &rightNameKey);
NSNumber* bottomEdge = objc_getAssociatedObject(self, &bottomNameKey);
NSNumber* leftEdge = objc_getAssociatedObject(self, &leftNameKey);
if (topEdge && rightEdge && bottomEdge && leftEdge){
return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,
self.bounds.origin.y - topEdge.floatValue,
self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,
self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
} else{
return self.bounds;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
CGRect rect = [self enlargedRect];
if (CGRectEqualToRect(rect, self.bounds)){
return [super pointInside:point withEvent:event];
}else{
return CGRectContainsPoint(rect, point);
}
}
@end

View File

@ -0,0 +1,21 @@
//
// UIColor+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2019/12/3.
// Copyright © 2019 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIColor (HXExtension)
+ (UIColor *)hx_colorWithHexStr:(NSString *)string;
+ (UIColor *)hx_colorWithHexStr:(NSString *)string alpha:(CGFloat)alpha;
+ (UIColor *)hx_colorWithR:(CGFloat)red g:(CGFloat)green b:(CGFloat)blue a:(CGFloat)alpha;
+ (NSString *)hx_hexStringWithColor:(UIColor *)color;
- (BOOL)hx_colorIsWhite;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,58 @@
//
// UIColor+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2019/12/3.
// Copyright © 2019 Silence. All rights reserved.
//
#import "UIColor+HXExtension.h"
@implementation UIColor (HXExtension)
+ (UIColor *)hx_colorWithHexStr:(NSString *)string {
return [UIColor hx_colorWithHexStr:string alpha:1.0];
}
+ (UIColor *)hx_colorWithHexStr:(NSString *)string alpha:(CGFloat)alpha {
NSString *str;
if ([string containsString:@"#"]) {
str = [string substringFromIndex:1];
} else {
str = string;
}
NSScanner *scanner = [[NSScanner alloc] initWithString:str];
UInt32 hexNum = 0;
if ([scanner scanHexInt:&hexNum] == NO) {
NSLog(@"16进制转UIColor, hexString为空");
}
return [UIColor hx_colorWithR:(hexNum & 0xFF0000) >> 16 g:(hexNum & 0x00FF00) >> 8 b:hexNum & 0x0000FF a:alpha];
}
+ (UIColor *)hx_colorWithR:(CGFloat)red g:(CGFloat)green b:(CGFloat)blue a:(CGFloat)alpha {
return [UIColor colorWithRed:red / 255.0 green:green / 255.0 blue:blue / 255.0 alpha:alpha];
}
+ (NSString *)hx_hexStringWithColor:(UIColor *)color {
CGFloat r, g, b, a;
BOOL bo = [color getRed:&r green:&g blue:&b alpha:&a];
if (bo) {
int rgb = (int) (r * 255.0f)<<16 | (int) (g * 255.0f)<<8 | (int) (b * 255.0f)<<0;
return [NSString stringWithFormat:@"#%06x", rgb].uppercaseString;
} else {
return @"";
}
}
- (BOOL)hx_colorIsWhite {
CGFloat red = 0;
CGFloat green = 0;
CGFloat blue = 0;
[self getRed:&red green:&green blue:&blue alpha:NULL];
if (red >= 0.99 && green >= 0.99 & blue >= 0.99) {
return YES;
}
return NO;
}
@end

View File

@ -0,0 +1,19 @@
//
// UIFont+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIFont (HXExtension)
+ (instancetype)hx_pingFangFontOfSize:(CGFloat)size;
+ (instancetype)hx_regularPingFangOfSize:(CGFloat)size;
+ (instancetype)hx_mediumPingFangOfSize:(CGFloat)size;
+ (instancetype)hx_boldPingFangOfSize:(CGFloat)size;
+ (instancetype)hx_helveticaNeueOfSize:(CGFloat)size;
+ (instancetype)hx_mediumHelveticaNeueOfSize:(CGFloat)size;
+ (instancetype)hx_mediumSFUITextOfSize:(CGFloat)size;
@end

View File

@ -0,0 +1,42 @@
//
// UIFont+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017 Silence. All rights reserved.
//
#import "UIFont+HXExtension.h"
@implementation UIFont (HXExtension)
+ (instancetype)hx_pingFangFontOfSize:(CGFloat)size {
UIFont *font = [self fontWithName:@"PingFangSC-Regular" size:size];
return font ? font : [UIFont systemFontOfSize:size];
}
+ (instancetype)hx_regularPingFangOfSize:(CGFloat)size {
UIFont *font = [self fontWithName:@"PingFangSC-Regular" size:size];
return font ? font : [UIFont systemFontOfSize:size];
}
+ (instancetype)hx_mediumPingFangOfSize:(CGFloat)size {
UIFont *font = [self fontWithName:@"PingFangSC-Medium" size:size];
return font ? font : [UIFont systemFontOfSize:size];
}
+ (instancetype)hx_boldPingFangOfSize:(CGFloat)size {
UIFont *font = [self fontWithName:@"PingFangSC-Semibold" size:size];
return font ? font : [UIFont systemFontOfSize:size];
}
+ (instancetype)hx_helveticaNeueOfSize:(CGFloat)size {
UIFont *font = [self fontWithName:@"HelveticaNeue" size:size];
return font ? font : [UIFont systemFontOfSize:size];
}
+ (instancetype)hx_mediumHelveticaNeueOfSize:(CGFloat)size {
UIFont *font = [self fontWithName:@"HelveticaNeue-Medium" size:size];
return font ? font : [UIFont systemFontOfSize:size];
}
+ (instancetype)hx_mediumSFUITextOfSize:(CGFloat)size {
return [self hx_mediumPingFangOfSize:size];
// UIFont *font = [self fontWithName:@".SFUIText-Medium" size:size];
// return font ? font : [UIFont systemFontOfSize:size];
}
@end

View File

@ -0,0 +1,35 @@
//
// UIImage+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 17/2/15.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <CoreMedia/CoreMedia.h>
@interface UIImage (HXExtension)
+ (UIImage *)hx_imageNamed:(NSString *)imageName;
+ (UIImage *)hx_imageContentsOfFile:(NSString *)imageName;
+ (UIImage *)hx_thumbnailImageForVideo:(NSURL *)videoURL
atTime:(NSTimeInterval)time;
+ (UIImage *)hx_animatedGIFWithData:(NSData *)data;
+ (UIImage *)hx_animatedGIFWithURL:(NSURL *)URL;
- (UIImage *)hx_normalizedImage;
- (UIImage *)hx_scaleImagetoScale:(float)scaleSize;
- (UIImage *)hx_rotationImage:(UIImageOrientation)orient;
+ (UIImage *)hx_imageWithColor:(UIColor *)color havingSize:(CGSize)size;
- (UIImage *)hx_cropInRect:(CGRect)rect;
- (UIImage *)hx_roundClipingImage;
- (UIImage *)hx_scaleToFillSize:(CGSize)size;
- (UIImage *)hx_mergeimages:(NSArray <UIImage *>*)images;
+ (UIImage *)hx_mergeimages:(NSArray <UIImage *>*)images;
+ (CGSize)hx_scaleImageSizeBySize:(CGSize)imageSize targetSize:(CGSize)size isBoth:(BOOL)isBoth ;
- (UIImage *)hx_scaleToFitSize:(CGSize)size;
@end

View File

@ -0,0 +1,445 @@
//
// UIImage+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 17/2/15.
// Copyright © 2017 Silence. All rights reserved.
//
#import "UIImage+HXExtension.h"
#import "HXPhotoTools.h"
#import <ImageIO/ImageIO.h>
#import <Accelerate/Accelerate.h>
@implementation UIImage (HXExtension)
+ (UIImage *)hx_imageNamed:(NSString *)imageName {
if (!imageName) {
return nil;
}
UIImage *image;
NSBundle *bundle = [NSBundle hx_photoPickerBundle];
if (bundle) {
NSString *path = [bundle pathForResource:@"images" ofType:nil];
path = [path stringByAppendingPathComponent:imageName];
image = [UIImage imageNamed:path];
}
if (!image) {
image = [self imageNamed:imageName];
}
return image;
}
+ (UIImage *)hx_imageContentsOfFile:(NSString *)imageName {
if (!imageName) {
return nil;
}
UIImage *image;
NSBundle *bundle = [NSBundle hx_photoPickerBundle];
if (bundle) {
NSString *path = [bundle pathForResource:@"images" ofType:nil];
path = [path stringByAppendingPathComponent:imageName];
image = [UIImage imageWithContentsOfFile:path];
}
if (!image) {
image = [self imageNamed:imageName];
}
return image;
}
+ (UIImage *)hx_thumbnailImageForVideo:(NSURL *)videoURL atTime:(NSTimeInterval)time {
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
if (!asset) {
return nil;
}
AVAssetImageGenerator *assetImageGenerator =[[AVAssetImageGenerator alloc] initWithAsset:asset];
assetImageGenerator.appliesPreferredTrackTransform = YES;
assetImageGenerator.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels;
CGImageRef thumbnailImageRef = NULL;
CFTimeInterval thumbnailImageTime = time;
NSError *thumbnailImageGenerationError = nil;
thumbnailImageRef = [assetImageGenerator copyCGImageAtTime:CMTimeMake(thumbnailImageTime, 60)actualTime:NULL error:&thumbnailImageGenerationError];
UIImage*thumbnailImage = thumbnailImageRef ? [[UIImage alloc]initWithCGImage: thumbnailImageRef] : nil;
CGImageRelease(thumbnailImageRef);
return thumbnailImage;
}
+ (UIImage *)hx_animatedGIFWithImageSourceRef:(CGImageSourceRef)source {
//gif
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage;
if (count > 1) { //
NSMutableArray *images = [NSMutableArray array];
//gif
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i++) {
//gif
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
if (!image) {
continue;
}
//
duration += [self frameDurationAtIndex:i source:source];
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
CGImageRelease(image);
}
if (!duration) {//
duration = (1.0f / 10.0f) * count;
}
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
return animatedImage;
}
+ (UIImage *)hx_animatedGIFWithURL:(NSURL *)URL {
if (!URL) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)URL, NULL);
UIImage *animatedImage = [self hx_animatedGIFWithImageSourceRef:source];
if (!animatedImage) {
animatedImage = [UIImage imageWithContentsOfFile:URL.relativePath];
}
CFRelease(source);
return animatedImage;
}
+ (UIImage *)hx_animatedGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
//CFDatagif
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
UIImage *animatedImage = [self hx_animatedGIFWithImageSourceRef:source];
if (!animatedImage) {
animatedImage = [UIImage imageWithData:data];
}
CFRelease(source);
return animatedImage;
}
+ (float)frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
float frameDuration = 0.1f;
//
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
//gif
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
//
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
if (delayTimeUnclampedProp) {
frameDuration = [delayTimeUnclampedProp floatValue];
}
else {
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
if (delayTimeProp) {
frameDuration = [delayTimeProp floatValue];
}
}
//0.1,0.1
if (frameDuration < 0.011f) {
frameDuration = 0.100f;
}
CFRelease(cfFrameProperties);
return frameDuration;
}
- (UIImage *)hx_normalizedImage {
if (self.imageOrientation == UIImageOrientationUp) return self;
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
[self drawInRect:(CGRect){0, 0, self.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}
- (UIImage *)hx_scaleImagetoScale:(float)scaleSize {
UIGraphicsBeginImageContext(CGSizeMake(self.size.width * scaleSize, self.size.height * scaleSize));
[self drawInRect:CGRectMake(0, 0, self.size.width * scaleSize, self.size.height * scaleSize)];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
- (UIImage *)hx_rotationImage:(UIImageOrientation)orient {
CGRect bnds = CGRectZero;
UIImage* copy = nil;
CGContextRef ctxt = nil;
CGImageRef imag = self.CGImage;
CGRect rect = CGRectZero;
CGAffineTransform tran = CGAffineTransformIdentity;
rect.size.width = CGImageGetWidth(imag) * self.scale;
rect.size.height = CGImageGetHeight(imag) * self.scale;
while (rect.size.width * rect.size.height > 3 * 1000 * 1000) {
rect.size.width /= 2;
rect.size.height /= 2;
}
bnds = rect;
switch (orient)
{
case UIImageOrientationUp:
return self;
case UIImageOrientationUpMirrored:
tran = CGAffineTransformMakeTranslation(rect.size.width, 0.0);
tran = CGAffineTransformScale(tran, -1.0, 1.0);
break;
case UIImageOrientationDown:
tran = CGAffineTransformMakeTranslation(rect.size.width,
rect.size.height);
tran = CGAffineTransformRotate(tran, M_PI);
break;
case UIImageOrientationDownMirrored:
tran = CGAffineTransformMakeTranslation(0.0, rect.size.height);
tran = CGAffineTransformScale(tran, 1.0, -1.0);
break;
case UIImageOrientationLeft:
bnds = swapWidthAndHeight(bnds);
tran = CGAffineTransformMakeTranslation(0.0, rect.size.width);
tran = CGAffineTransformRotate(tran, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationLeftMirrored:
bnds = swapWidthAndHeight(bnds);
tran = CGAffineTransformMakeTranslation(rect.size.height,
rect.size.width);
tran = CGAffineTransformScale(tran, -1.0, 1.0);
tran = CGAffineTransformRotate(tran, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationRight:
bnds = swapWidthAndHeight(bnds);
tran = CGAffineTransformMakeTranslation(rect.size.height, 0.0);
tran = CGAffineTransformRotate(tran, M_PI / 2.0);
break;
case UIImageOrientationRightMirrored:
bnds = swapWidthAndHeight(bnds);
tran = CGAffineTransformMakeScale(-1.0, 1.0);
tran = CGAffineTransformRotate(tran, M_PI / 2.0);
break;
default:
return self;
}
UIGraphicsBeginImageContext(bnds.size);
ctxt = UIGraphicsGetCurrentContext();
switch (orient)
{
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
CGContextScaleCTM(ctxt, -1.0, 1.0);
CGContextTranslateCTM(ctxt, -rect.size.height, 0.0);
break;
default:
CGContextScaleCTM(ctxt, 1.0, -1.0);
CGContextTranslateCTM(ctxt, 0.0, -rect.size.height);
break;
}
CGContextConcatCTM(ctxt, tran);
CGContextDrawImage(UIGraphicsGetCurrentContext(), rect, imag);
copy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return copy;
}
/** */
static CGRect swapWidthAndHeight(CGRect rect) {
CGFloat swap = rect.size.width;
rect.size.width = rect.size.height;
rect.size.height = swap;
return rect;
}
+ (UIImage *)hx_imageWithColor:(UIColor *)color havingSize:(CGSize)size {
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)hx_cropInRect:(CGRect)rect {
if (CGPointEqualToPoint(CGPointZero, rect.origin) && CGSizeEqualToSize(self.size, rect.size)) {
return self;
}
UIImage *smallImage = nil;
CGImageRef sourceImageRef = [self CGImage];
CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, rect);
if (newImageRef) {
smallImage = [UIImage imageWithCGImage:newImageRef scale:self.scale orientation:self.imageOrientation];
CGImageRelease(newImageRef);
}
return smallImage;
}
- (UIImage *)hx_roundClipingImage {
UIGraphicsBeginImageContext(self.size);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
[path addClip];
[self drawAtPoint:CGPointZero];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage *)hx_scaleToFillSize:(CGSize)size {
if (CGSizeEqualToSize(self.size, size)) {
return self;
}
// context
// 使context
UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
//
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
// context
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// 使context
UIGraphicsEndImageContext();
//
return scaledImage;
}
/** */
- (UIImage *)hx_mergeimages:(NSArray <UIImage *>*)images {
CGSize size = self.size;
while (size.width * size.height > 3 * 1000 * 1000) {
size.width /= 2;
size.height /= 2;
}
UIGraphicsBeginImageContextWithOptions(size ,NO, 0);
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
for (UIImage *image in images) {
size = image.size;
while (size.width * size.height > 3 * 1000 * 1000) {
size.width /= 2;
size.height /= 2;
}
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
}
UIImage *mergeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return mergeImage;
}
/** () */
+ (UIImage *)hx_mergeimages:(NSArray <UIImage *>*)images {
UIGraphicsBeginImageContextWithOptions(images.firstObject.size ,NO, 0);
for (UIImage *image in images) {
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
}
UIImage *mergeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return mergeImage;
}
+ (CGSize)hx_scaleImageSizeBySize:(CGSize)imageSize targetSize:(CGSize)size isBoth:(BOOL)isBoth {
/** 0 */
if (CGSizeEqualToSize(imageSize, CGSizeZero)) {
return imageSize;
}
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = size.width;
CGFloat targetHeight = size.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0, 0.0);
if(CGSizeEqualToSize(imageSize, size) == NO){
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (isBoth) {
if(widthFactor > heightFactor){
scaleFactor = widthFactor;
}
else{
scaleFactor = heightFactor;
}
} else {
if(widthFactor > heightFactor){
scaleFactor = heightFactor;
}
else{
scaleFactor = widthFactor;
}
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
if(widthFactor > heightFactor){
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}else if(widthFactor < heightFactor){
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
return CGSizeMake(ceilf(scaledWidth), ceilf(scaledHeight));
}
- (UIImage*)hx_scaleToFitSize:(CGSize)size {
if (CGSizeEqualToSize(self.size, size)) {
return self;
}
CGFloat width = CGImageGetWidth(self.CGImage);
CGFloat height = CGImageGetHeight(self.CGImage);
float verticalRadio = size.height*1.0/height;
float horizontalRadio = size.width*1.0/width;
float radio = 1;
if(verticalRadio>1 && horizontalRadio>1)
{
radio = verticalRadio > horizontalRadio ? horizontalRadio : verticalRadio;
}
else
{
radio = verticalRadio < horizontalRadio ? verticalRadio : horizontalRadio;
}
width = roundf(width*radio);
height = roundf(height*radio);
int xPos = (size.width - width)/2;
int yPos = (size.height-height)/2;
// context
// 使context
UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
//
[self drawInRect:CGRectMake(xPos, yPos, width, height)];
// context
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// 使context
UIGraphicsEndImageContext();
//
return scaledImage;
}
@end

View File

@ -0,0 +1,19 @@
//
// UIImageView+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2018/2/14.
// Copyright © 2018年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
@class HXPhotoModel;
@interface UIImageView (HXExtension)
- (void)hx_setImageWithModel:(HXPhotoModel *)model progress:(void (^)(CGFloat progress, HXPhotoModel *model))progressBlock completed:(void (^)(UIImage * image, NSError * error, HXPhotoModel * model))completedBlock;
- (void)hx_setImageWithModel:(HXPhotoModel *)model original:(BOOL)original progress:(void (^)(CGFloat progress, HXPhotoModel *model))progressBlock completed:(void (^)(UIImage * image, NSError * error, HXPhotoModel * model))completedBlock;
- (void)hx_setImageWithURL:(NSURL *)url
progress:(void (^)(CGFloat progress))progressBlock
completed:(void (^)(UIImage * image, NSError * error))completedBlock;
@end

View File

@ -0,0 +1,196 @@
//
// UIImageView+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2018/2/14.
// Copyright © 2018 Silence. All rights reserved.
//
#import "UIImageView+HXExtension.h"
#import "HXPhotoDefine.h"
#import "HXPhotoModel.h"
#if __has_include(<SDWebImage/UIImageView+WebCache.h>)
#import <SDWebImage/UIImageView+WebCache.h>
#import <SDWebImage/SDWebImageManager.h>
#elif __has_include("UIImageView+WebCache.h")
#import "UIImageView+WebCache.h"
#import "SDWebImageManager.h"
#endif
#if __has_include(<YYWebImage/YYWebImage.h>)
#import <YYWebImage/YYWebImage.h>
#elif __has_include("YYWebImage.h")
#import "YYWebImage.h"
#elif __has_include(<YYKit/YYKit.h>)
#import <YYKit/YYKit.h>
#elif __has_include("YYKit.h")
#import "YYKit.h"
#endif
#import "HXPhotoEdit.h"
@implementation UIImageView (HXExtension)
- (void)hx_setImageWithModel:(HXPhotoModel *)model progress:(void (^)(CGFloat progress, HXPhotoModel *model))progressBlock completed:(void (^)(UIImage * image, NSError * error, HXPhotoModel * model))completedBlock {
[self hx_setImageWithModel:model original:YES progress:progressBlock completed:completedBlock];
}
- (void)hx_setImageWithModel:(HXPhotoModel *)model original:(BOOL)original progress:(void (^)(CGFloat progress, HXPhotoModel *model))progressBlock completed:(void (^)(UIImage * image, NSError * error, HXPhotoModel * model))completedBlock {
if (model.photoEdit) {
UIImage *image = model.photoEdit.editPreviewImage;
self.image = image;
model.imageSize = image.size;
model.thumbPhoto = image;
model.previewPhoto = image;
model.downloadComplete = YES;
model.downloadError = NO;
if (completedBlock) {
completedBlock(image, nil, model);
}
return;
}
if (!model.networkThumbURL) model.networkThumbURL = model.networkPhotoUrl;
#if HasSDWebImage
HXWeakSelf
NSString *cacheKey = [[SDWebImageManager sharedManager] cacheKeyForURL:model.networkPhotoUrl];
[[SDWebImageManager sharedManager].imageCache queryImageForKey:cacheKey options:SDWebImageQueryMemoryData context:nil completion:^(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType) {
if (image) {
weakSelf.image = image;
model.imageSize = image.size;
model.thumbPhoto = image;
model.previewPhoto = image;
model.downloadComplete = YES;
model.downloadError = NO;
if (completedBlock) {
completedBlock(image, nil, model);
}
}else {
NSURL *url = (original || image) ? model.networkPhotoUrl : model.networkThumbURL;
[weakSelf sd_setImageWithURL:url placeholderImage:model.thumbPhoto options:0 context:nil progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
model.receivedSize = receivedSize;
model.expectedSize = expectedSize;
CGFloat progress = (CGFloat)receivedSize / expectedSize;
dispatch_async(dispatch_get_main_queue(), ^{
if (progressBlock) {
progressBlock(progress, model);
}
});
} completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
model.downloadComplete = YES;
if (error != nil) {
model.downloadError = YES;
}else {
if (image) {
weakSelf.image = image;
model.imageSize = image.size;
model.thumbPhoto = image;
model.previewPhoto = image;
model.downloadError = NO;
}
}
if (completedBlock) {
completedBlock(image,error,model);
}
}];
}
}];
#elif HasYYKitOrWebImage
HXWeakSelf
YYWebImageManager *manager = [YYWebImageManager sharedManager];
[manager.cache getImageForKey:[manager cacheKeyForURL:model.networkPhotoUrl] withType:YYImageCacheTypeAll withBlock:^(UIImage * _Nullable image, YYImageCacheType type) {
if (image) {
weakSelf.image = image;
model.imageSize = weakSelf.image.size;
model.thumbPhoto = weakSelf.image;
model.previewPhoto = weakSelf.image;
model.downloadComplete = YES;
model.downloadError = NO;
if (completedBlock) {
completedBlock(weakSelf.image, nil, model);
}
}else {
NSURL *url = original ? model.networkPhotoUrl : model.networkThumbURL;
[weakSelf yy_setImageWithURL:url placeholder:model.thumbPhoto options:YYWebImageOptionShowNetworkActivity progress:^(NSInteger receivedSize, NSInteger expectedSize) {
model.receivedSize = receivedSize;
model.expectedSize = expectedSize;
CGFloat progress = (CGFloat)receivedSize / expectedSize;
dispatch_async(dispatch_get_main_queue(), ^{
if (progressBlock) {
progressBlock(progress, model);
}
});
} transform:^UIImage * _Nullable(UIImage * _Nonnull image, NSURL * _Nonnull url) {
return image;
} completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
if (error != nil) {
model.downloadError = YES;
model.downloadComplete = YES;
}else {
if (image) {
weakSelf.image = image;
model.imageSize = image.size;
model.thumbPhoto = image;
model.previewPhoto = image;
model.downloadComplete = YES;
model.downloadError = NO;
}
}
if (completedBlock) {
completedBlock(image,error,model);
}
}];
}
}];
#else
/// podSDYY HX pod pod install
/// pod HXPhotoPicker/SDWebImage HXPhotoPicker/YYWebImage
NSSLog(@"请导入YYWebImage/SDWebImage后再使用网络图片功能");
// NSAssert(NO, @"请导入YYWebImage/SDWebImage后再使用网络图片功能HXPhotoPicker为pod导入的那么YY或者SD也必须是pod导入的否则会找不到");
#endif
}
- (void)hx_setImageWithURL:(NSURL *)url
progress:(void (^)(CGFloat progress))progressBlock
completed:(void (^)(UIImage * image, NSError * error))completedBlock {
#if HasSDWebImage
HXWeakSelf
[self sd_setImageWithURL:url placeholderImage:nil options:0 context:nil progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
CGFloat progress = (CGFloat)receivedSize / expectedSize;
dispatch_async(dispatch_get_main_queue(), ^{
if (progressBlock) {
progressBlock(progress);
}
});
} completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
weakSelf.image = image;
if (completedBlock) {
completedBlock(image, error);
}
}];
#elif HasYYKitOrWebImage
HXWeakSelf
[self yy_setImageWithURL:url placeholder:nil options:YYWebImageOptionShowNetworkActivity progress:^(NSInteger receivedSize, NSInteger expectedSize) {
CGFloat progress = (CGFloat)receivedSize / expectedSize;
dispatch_async(dispatch_get_main_queue(), ^{
if (progressBlock) {
progressBlock(progress);
}
});
} transform:^UIImage * _Nullable(UIImage * _Nonnull image, NSURL * _Nonnull url) {
return image;
} completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
weakSelf.image = image;
if (completedBlock) {
completedBlock(image, error);
}
}];
#else
/// podSDYY HX pod pod install
/// pod HXPhotoPicker/SDWebImage HXPhotoPicker/YYWebImage
NSSLog(@"请导入YYWebImage/SDWebImage后再使用网络图片功能");
// NSAssert(NO, @"请导入YYWebImage/SDWebImage后再使用网络图片功能HXPhotoPicker为pod导入的那么YY或者SD也必须是pod导入的否则会找不到");
#endif
}
@end

View File

@ -0,0 +1,35 @@
//
// UILabel+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2018/12/28.
// Copyright © 2018年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UILabel (HXExtension)
/**
label高度
@return
*/
- (CGFloat)hx_getTextWidth;
/**
label宽度
@return
*/
- (CGFloat)hx_getTextHeight;
+ (CGFloat)hx_getTextWidthWithText:(NSString *)text height:(CGFloat)height font:(UIFont *)font;
+ (CGFloat)hx_getTextWidthWithText:(NSString *)text height:(CGFloat)height fontSize:(CGFloat)fontSize;
+ (CGFloat)hx_getTextHeightWithText:(NSString *)text width:(CGFloat)width font:(UIFont *)font;
+ (CGFloat)hx_getTextHeightWithText:(NSString *)text width:(CGFloat)width fontSize:(CGFloat)fontSize;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,34 @@
//
// UILabel+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2018/12/28.
// Copyright © 2018 Silence. All rights reserved.
//
#import "UILabel+HXExtension.h"
#import "UIView+HXExtension.h"
@implementation UILabel (HXExtension)
- (CGFloat)hx_getTextWidth {
return [UILabel hx_getTextWidthWithText:self.text height:self.hx_h font:self.font];
}
- (CGFloat)hx_getTextHeight {
return [UILabel hx_getTextHeightWithText:self.text width:self.hx_w font:self.font];
}
+ (CGFloat)hx_getTextWidthWithText:(NSString *)text height:(CGFloat)height font:(UIFont *)font {
CGSize newSize = [text boundingRectWithSize:CGSizeMake(MAXFLOAT, height) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size;
return newSize.width;
}
+ (CGFloat)hx_getTextWidthWithText:(NSString *)text height:(CGFloat)height fontSize:(CGFloat)fontSize {
return [UILabel hx_getTextWidthWithText:text height:height font:[UIFont systemFontOfSize:fontSize]];
}
+ (CGFloat)hx_getTextHeightWithText:(NSString *)text width:(CGFloat)width font:(UIFont *)font {
CGSize newSize = [text boundingRectWithSize:CGSizeMake(width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size;
return newSize.height;
}
+ (CGFloat)hx_getTextHeightWithText:(NSString *)text width:(CGFloat)width fontSize:(CGFloat)fontSize {
return [UILabel hx_getTextHeightWithText:text width:width font:[UIFont systemFontOfSize:fontSize]];
}
@end

View File

@ -0,0 +1,61 @@
//
// UIView+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 17/2/16.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
@class HXPhotoManager;
@interface UIView (HXExtension)
@property (assign, nonatomic) CGFloat hx_x;
@property (assign, nonatomic) CGFloat hx_y;
@property (assign, nonatomic) CGFloat hx_w;
@property (assign, nonatomic) CGFloat hx_h;
@property (assign, nonatomic) CGFloat hx_centerX;
@property (assign, nonatomic) CGFloat hx_centerY;
@property (assign, nonatomic) CGSize hx_size;
@property (assign, nonatomic) CGPoint hx_origin;
/**
@return
*/
- (UIViewController *)hx_viewController;
- (void)hx_showImageHUDText:(NSString *)text;
- (void)hx_showLoadingHUDText:(NSString *)text;
- (void)hx_showLoadingHUDText:(NSString *)text delay:(NSTimeInterval)delay;
- (void)hx_immediatelyShowLoadingHudWithText:(NSString *)text;
- (void)hx_handleLoading;
- (void)hx_handleLoading:(BOOL)animation;
- (void)hx_handleLoading:(BOOL)animation duration:(NSTimeInterval)duration;
- (void)hx_handleImageWithDelay:(NSTimeInterval)delay;
- (void)hx_handleImageWithAnimation:(BOOL)animation;
- (void)hx_handleGraceTimer;
/* <HXAlbumListViewControllerDelegate> */
- (void)hx_presentAlbumListViewControllerWithManager:(HXPhotoManager *)manager delegate:(id)delegate DEPRECATED_MSG_ATTRIBUTE("Use UIViewController+HXEXtension 'hx_presentSelectPhotoControllerWithManager:' instead");
/* <HXCustomCameraViewControllerDelegate> */
- (void)hx_presentCustomCameraViewControllerWithManager:(HXPhotoManager *)manager delegate:(id)delegate DEPRECATED_MSG_ATTRIBUTE("Use UIViewController+HXEXtension 'hx_presentCustomCameraViewControllerWithManager:' instead");
/// 设置圆角。使用自动布局需要在layoutsubviews 中使用
/// @param radius 圆角尺寸
/// @param corner 圆角位置
- (void)hx_radiusWithRadius:(CGFloat)radius corner:(UIRectCorner)corner;
- (UIImage *)hx_captureImageAtFrame:(CGRect)rect;
- (UIColor *)hx_colorOfPoint:(CGPoint)point;
@end
@interface HXHUD : UIView
@property (assign, nonatomic) BOOL isImage;
@property (copy, nonatomic) NSString *text;
- (instancetype)initWithFrame:(CGRect)frame imageName:(NSString *)imageName text:(NSString *)text;
- (void)showloading;
@end

View File

@ -0,0 +1,482 @@
//
// UIView+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 17/2/16.
// Copyright © 2017 Silence. All rights reserved.
//
#import "UIView+HXExtension.h"
#import "HXPhotoPicker.h"
@implementation UIView (HXExtension)
- (void)setHx_x:(CGFloat)hx_x
{
CGRect frame = self.frame;
frame.origin.x = hx_x;
self.frame = frame;
}
- (CGFloat)hx_x
{
return self.frame.origin.x;
}
- (void)setHx_y:(CGFloat)hx_y
{
CGRect frame = self.frame;
frame.origin.y = hx_y;
self.frame = frame;
}
- (CGFloat)hx_y
{
return self.frame.origin.y;
}
- (void)setHx_w:(CGFloat)hx_w
{
CGRect frame = self.frame;
frame.size.width = hx_w;
self.frame = frame;
}
- (CGFloat)hx_w
{
return self.frame.size.width;
}
- (void)setHx_h:(CGFloat)hx_h
{
CGRect frame = self.frame;
frame.size.height = hx_h;
self.frame = frame;
}
- (CGFloat)hx_h
{
return self.frame.size.height;
}
- (CGFloat)hx_centerX
{
return self.center.x;
}
- (void)setHx_centerX:(CGFloat)hx_centerX {
CGPoint center = self.center;
center.x = hx_centerX;
self.center = center;
}
- (CGFloat)hx_centerY
{
return self.center.y;
}
- (void)setHx_centerY:(CGFloat)hx_centerY {
CGPoint center = self.center;
center.y = hx_centerY;
self.center = center;
}
- (void)setHx_size:(CGSize)hx_size
{
CGRect frame = self.frame;
frame.size = hx_size;
self.frame = frame;
}
- (CGSize)hx_size
{
return self.frame.size;
}
- (void)setHx_origin:(CGPoint)hx_origin
{
CGRect frame = self.frame;
frame.origin = hx_origin;
self.frame = frame;
}
- (CGPoint)hx_origin
{
return self.frame.origin;
}
/**
@return
*/
- (UIViewController*)hx_viewController {
for (UIView* next = [self superview]; next; next = next.superview) {
UIResponder* nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UINavigationController class]] || [nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController*)nextResponder;
}
}
return nil;
}
- (void)hx_presentAlbumListViewControllerWithManager:(HXPhotoManager *)manager delegate:(id)delegate {
HXAlbumListViewController *vc = [[HXAlbumListViewController alloc] initWithManager:manager];
vc.delegate = delegate ? delegate : (id)self;
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithRootViewController:vc];
nav.supportRotation = manager.configuration.supportRotation;
nav.modalPresentationStyle = UIModalPresentationOverFullScreen;
nav.modalPresentationCapturesStatusBarAppearance = YES;
[self.hx_viewController presentViewController:nav animated:YES completion:nil];
}
- (void)hx_presentCustomCameraViewControllerWithManager:(HXPhotoManager *)manager delegate:(id)delegate {
if(![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[self.hx_viewController.view hx_showImageHUDText:[NSBundle hx_localizedStringForKey:@"无法使用相机!"]];
return;
}
HXWeakSelf
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
HXCustomCameraViewController *vc = [[HXCustomCameraViewController alloc] init];
vc.delegate = delegate ? delegate : (id)weakSelf;
vc.manager = manager;
vc.isOutside = YES;
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithRootViewController:vc];
nav.isCamera = YES;
nav.supportRotation = manager.configuration.supportRotation;
nav.modalPresentationStyle = UIModalPresentationOverFullScreen;
nav.modalPresentationCapturesStatusBarAppearance = YES;
[weakSelf.hx_viewController presentViewController:nav animated:YES completion:nil];
}else {
[HXPhotoTools showUnusableCameraAlert:weakSelf.hx_viewController];
}
});
}];
}
- (void)hx_showImageHUDText:(NSString *)text {
CGFloat hudW = [UILabel hx_getTextWidthWithText:text height:15 fontSize:14];
if (hudW > self.frame.size.width - 60) {
hudW = self.frame.size.width - 60;
}
CGFloat hudH = [UILabel hx_getTextHeightWithText:text width:hudW fontSize:14];
if (hudW < 100) {
hudW = 100;
}
HXHUD *hud = [[HXHUD alloc] initWithFrame:CGRectMake(0, 0, hudW + 20, 110 + hudH - 15) imageName:@"hx_alert_failed" text:text];
hud.alpha = 0;
[self addSubview:hud];
hud.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
hud.transform = CGAffineTransformMakeScale(0.4, 0.4);
[UIView animateWithDuration:0.25 delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:1.0 options:0 animations:^{
hud.alpha = 1;
hud.transform = CGAffineTransformIdentity;
} completion:nil];
[UIView cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(hx_handleGraceTimer) withObject:nil afterDelay:1.75f inModes:@[NSRunLoopCommonModes]];
}
- (void)hx_immediatelyShowLoadingHudWithText:(NSString *)text {
[self hx_showLoadingHudWithText:text delay:0 immediately:YES];
}
- (void)hx_showLoadingHUDText:(NSString *)text {
[self hx_showLoadingHUDText:text delay:0.f];
}
- (void)hx_showLoadingHUDText:(NSString *)text delay:(NSTimeInterval)delay {
[self hx_showLoadingHudWithText:text delay:delay immediately:NO];
}
- (void)hx_showLoadingHudWithText:(NSString *)text delay:(NSTimeInterval)delay immediately:(BOOL)immediately {
CGFloat hudW = [UILabel hx_getTextWidthWithText:text height:15 fontSize:14];
if (hudW > self.frame.size.width - 60) {
hudW = self.frame.size.width - 60;
}
CGFloat hudH = [UILabel hx_getTextHeightWithText:text width:hudW fontSize:14];
CGFloat width = 110;
CGFloat height = width + hudH - 15;
if (!text) {
width = 95;
height = 95;
}
HXHUD *hud = [[HXHUD alloc] initWithFrame:CGRectMake(0, 0, width, height) imageName:nil text:text];
[hud showloading];
hud.alpha = 0;
[self addSubview:hud];
hud.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
if (immediately) {
hud.alpha = 1;
}else {
hud.transform = CGAffineTransformMakeScale(0.4, 0.4);
[UIView animateWithDuration:0.25 delay:delay usingSpringWithDamping:0.5 initialSpringVelocity:1 options:0 animations:^{
hud.alpha = 1;
hud.transform = CGAffineTransformIdentity;
} completion:nil];
}
}
- (void)hx_handleLoading {
[self hx_handleLoading:YES];
}
- (void)hx_handleLoading:(BOOL)animation {
[self hx_handleLoading:animation duration:0.2f];
}
- (void)hx_handleLoading:(BOOL)animation duration:(NSTimeInterval)duration {
[UIView cancelPreviousPerformRequestsWithTarget:self];
for (UIView *view in self.subviews) {
if ([view isKindOfClass:[HXHUD class]] && ![(HXHUD *)view isImage]) {
if (animation) {
[UIView animateWithDuration:duration animations:^{
view.alpha = 0;
view.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}else {
[view removeFromSuperview];
}
}
}
}
- (void)hx_handleImageWithAnimation:(BOOL)animation {
[UIView cancelPreviousPerformRequestsWithTarget:self];
for (UIView *view in self.subviews) {
if ([view isKindOfClass:[HXHUD class]] && [(HXHUD *)view isImage]) {
if (animation) {
[UIView animateWithDuration:0.25f animations:^{
view.alpha = 0;
view.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}else {
[view removeFromSuperview];
}
}
}
}
- (void)hx_handleImageWithDelay:(NSTimeInterval)delay {
if (delay) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self hx_handleGraceTimer];
});
}else {
[self hx_handleGraceTimer];
}
}
- (void)hx_handleGraceTimer {
[self hx_handleImageWithAnimation:YES];
}
/**
使layoutsubviews 使
@param radius
@param corner
*/
- (void)hx_radiusWithRadius:(CGFloat)radius corner:(UIRectCorner)corner {
#ifdef __IPHONE_11_0
if (@available(iOS 11.0, *)) {
self.layer.cornerRadius = radius;
self.layer.maskedCorners = (CACornerMask)corner;
#else
if ((NO)) {
#endif
} else {
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corner cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = path.CGPath;
self.layer.mask = maskLayer;
}
}
- (UIImage *)hx_captureImageAtFrame:(CGRect)rect {
UIImage* image = nil;
if (/* DISABLES CODE */ (YES)) {
CGSize size = self.bounds.size;
CGPoint point = self.bounds.origin;
if (!CGRectEqualToRect(CGRectZero, rect)) {
size = rect.size;
point = CGPointMake(-rect.origin.x, -rect.origin.y);
}
@autoreleasepool {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
[self drawViewHierarchyInRect:(CGRect){point, self.bounds.size} afterScreenUpdates:YES];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
} else {
BOOL translateCTM = !CGRectEqualToRect(CGRectZero, rect);
if (!translateCTM) {
rect = self.frame;
}
/** 1 */
/** */
#define lfme_fixDecimal(d) ((fmod(d, (int)d)) > 0.59f ? ((int)(d+0.5)*1.f) : (((fmod(d, (int)d)) < 0.59f && (fmod(d, (int)d)) > 0.1f) ? ((int)(d)*1.f+0.5f) : (int)(d)*1.f))
rect.origin.x = lfme_fixDecimal(rect.origin.x);
rect.origin.y = lfme_fixDecimal(rect.origin.y);
rect.size.width = lfme_fixDecimal(rect.size.width);
rect.size.height = lfme_fixDecimal(rect.size.height);
#undef lfme_fixDecimal
CGSize size = rect.size;
@autoreleasepool {
//1.
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (translateCTM) {
/** */
CGContextTranslateCTM(context, -rect.origin.x, -rect.origin.y);
}
//2.
[self.layer renderInContext: context];
//3.
image = UIGraphicsGetImageFromCurrentImageContext();
//4.
UIGraphicsEndImageContext();
}
}
return image;
}
- (UIColor *)hx_colorOfPoint:(CGPoint)point {
unsigned char pixel[4] = {0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast);
CGContextTranslateCTM(context, -point.x, -point.y);
[self.layer renderInContext:context];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];
return color;
}
@end
@interface HXHUD ()
@property (copy, nonatomic) NSString *imageName;
@property (strong, nonatomic) UIImageView *imageView;
@property (strong, nonatomic) UIVisualEffectView *visualEffectView;
@property (strong, nonatomic) UILabel *titleLb;
@property (strong, nonatomic) UIActivityIndicatorView *loading;
@end
@implementation HXHUD
- (instancetype)initWithFrame:(CGRect)frame imageName:(NSString *)imageName text:(NSString *)text {
self = [super initWithFrame:frame];
if (self) {
_text = text;
self.imageName = imageName;
self.layer.masksToBounds = YES;
self.layer.cornerRadius = 5;
[self addSubview:self.visualEffectView];
[self setup];
}
return self;
}
- (void)setup {
UIImage *image = self.imageName.length ? [UIImage hx_imageNamed:self.imageName] : nil;
self.isImage = image != nil;
if ([HXPhotoCommon photoCommon].isDark) {
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
self.imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:self.imageView];
if ([HXPhotoCommon photoCommon].isDark) {
self.imageView.tintColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
}
self.titleLb = [[UILabel alloc] init];
self.titleLb.text = self.text;
self.titleLb.textColor = [HXPhotoCommon photoCommon].isDark ? [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1] : [UIColor whiteColor];
self.titleLb.textAlignment = NSTextAlignmentCenter;
self.titleLb.font = [UIFont systemFontOfSize:14];
self.titleLb.numberOfLines = 0;
[self addSubview:self.titleLb];
}
- (void)setText:(NSString *)text {
_text = text;
self.titleLb.text = text;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat imgW = self.imageView.image.size.width;
if (imgW <= 0) imgW = 37;
CGFloat imgH = self.imageView.image.size.height;
if (imgH <= 0) imgH = 37;
CGFloat imgCenterX = self.frame.size.width / 2;
self.imageView.frame = CGRectMake(0, 20, imgW, imgH);
self.imageView.center = CGPointMake(imgCenterX, self.imageView.center.y);
self.titleLb.hx_x = 10;
self.titleLb.hx_y = CGRectGetMaxY(self.imageView.frame) + 10;
self.titleLb.hx_w = self.frame.size.width - 20;
self.titleLb.hx_h = [self.titleLb hx_getTextHeight];
if (self.text.length) {
self.hx_h = CGRectGetMaxY(self.titleLb.frame) + 20;
}
if (_loading) {
if (self.text) {
self.loading.frame = self.imageView.frame;
}else {
self.loading.frame = self.bounds;
}
}
}
- (UIActivityIndicatorView *)loading {
if (!_loading) {
_loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
#ifdef __IPHONE_13_0
if ([HXPhotoCommon photoCommon].isDark) {
if (@available(iOS 13.0, *)) {
_loading.activityIndicatorViewStyle = UIActivityIndicatorViewStyleLarge;
} else {
_loading.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
}
_loading.color = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
}
#endif
[_loading startAnimating];
}
return _loading;
}
- (void)showloading {
[self addSubview:self.loading];
self.imageView.hidden = YES;
}
- (UIVisualEffectView *)visualEffectView {
if (!_visualEffectView) {
if ([HXPhotoCommon photoCommon].isDark) {
UIBlurEffect *blurEffrct =[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_visualEffectView = [[UIVisualEffectView alloc]initWithEffect:blurEffrct];
}else {
UIBlurEffect *blurEffrct =[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
_visualEffectView = [[UIVisualEffectView alloc]initWithEffect:blurEffrct];
}
_visualEffectView.frame = self.bounds;
}
return _visualEffectView;
}
@end

View File

@ -0,0 +1,161 @@
//
// UIViewController+HXExtension.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/11/24.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXCustomCameraViewController.h"
#import "HXPhotoEditViewController.h"
#import "HX_PhotoEditViewController.h"
#import "HXVideoEditViewController.h"
#import "HXPhotoView.h"
#import "HXCustomNavigationController.h"
@class HXPhotoView;
@interface UIViewController (HXExtension)
/// 跳转相册列表
/// @param manager 照片管理者
/// @param models NSArray<HXPhotoModel *> *allList - 所选的所有模型数组
/// NSArray<HXPhotoModel *> *videoList - 所选的视频模型数组
/// NSArray<HXPhotoModel *> *photoList - 所选的照片模型数组
/// BOOL original - 是否原图
/// UIViewController *viewController 相册列表控制器
/// @param cancel 取消选择
- (void)hx_presentSelectPhotoControllerWithManager:(HXPhotoManager *_Nullable)manager
didDone:(void (^_Nullable)
(NSArray<HXPhotoModel *> * _Nullable allList,
NSArray<HXPhotoModel *> * _Nullable photoList,
NSArray<HXPhotoModel *> * _Nullable videoList,
BOOL isOriginal,
UIViewController * _Nullable viewController,
HXPhotoManager * _Nullable manager))models
cancel:(void (^_Nullable)
(UIViewController * _Nullable viewController,
HXPhotoManager * _Nullable manager))cancel;
/// 跳转相册列表
/// @param delegate HXCustomNavigationControllerDelegate
- (void)hx_presentSelectPhotoControllerWithManager:(HXPhotoManager * _Nullable)manager
delegate:(id _Nullable )delegate;
/// 跳转预览照片界面
/// @param manager 照片管理者
/// @param previewStyle 预览样式
/// @param currentIndex 当前预览的下标
/// @param photoView 照片展示视图 - 没有就不传
- (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager * _Nullable)manager
previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
currentIndex:(NSUInteger)currentIndex
photoView:(HXPhotoView * _Nullable)photoView;
- (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager * _Nullable)manager
previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
showBottomPageControl:(BOOL)showBottomPageControl
currentIndex:(NSUInteger)currentIndex;
- (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager * _Nullable)manager
previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
showBottomPageControl:(BOOL)showBottomPageControl
currentIndex:(NSUInteger)currentIndex
photoView:(HXPhotoView * _Nullable)photoView;
/// 跳转相机界面
/// @param manager 照片管理者
/// @param delegate 代理
- (void)hx_presentCustomCameraViewControllerWithManager:(HXPhotoManager * _Nullable)manager
delegate:(id _Nullable )delegate;
/// 跳转相机界面
/// @param manager 照片管理者
/// @param done 完成回调
/// @param cancel 取消回调
- (void)hx_presentCustomCameraViewControllerWithManager:(HXPhotoManager * _Nullable)manager
done:(HXCustomCameraViewControllerDidDoneBlock _Nullable )done
cancel:(HXCustomCameraViewControllerDidCancelBlock _Nullable )cancel;
/// 跳转照片编辑界面
/// @param manager 照片管理者,主要设置编辑参数
/// @param photomodel 需要编辑的照片模型
/// @param delegate 代理
/// @param done 完成回调
/// @param cancel 取消回调
- (void)hx_presentPhotoEditViewControllerWithManager:(HXPhotoManager * _Nonnull)manager
photoModel:(HXPhotoModel * _Nonnull)photomodel
delegate:(id _Nullable )delegate
done:(HXPhotoEditViewControllerDidDoneBlock _Nullable)done
cancel:(HXPhotoEditViewControllerDidCancelBlock _Nullable)cancel;
/// 跳转照片编辑界面
/// @param manager 照片管理者,主要设置编辑参数
/// @param editPhoto 需要编辑的照片
/// @param done 完成回调
/// @param cancel 取消回调
- (void)hx_presentPhotoEditViewControllerWithManager:(HXPhotoManager * _Nonnull)manager
editPhoto:(UIImage * _Nonnull)editPhoto
done:(HXPhotoEditViewControllerDidDoneBlock _Nullable)done
cancel:(HXPhotoEditViewControllerDidCancelBlock _Nullable)cancel;
/// 跳转视频编辑界面
/// @param manager 照片管理者,主要设置编辑参数
/// @param videoModel 需要编辑的视频模型
/// @param delegate 代理
/// @param done 完成后的回调
/// @param cancel 取消回调
- (void)hx_presentVideoEditViewControllerWithManager:(HXPhotoManager * _Nonnull)manager
videoModel:(HXPhotoModel * _Nonnull)videoModel
delegate:(id _Nullable )delegate
done:(HXVideoEditViewControllerDidDoneBlock _Nullable)done
cancel:(HXVideoEditViewControllerDidCancelBlock _Nullable)cancel;
/// 跳转视频编辑界面
/// @param manager 照片管理者,主要设置编辑参数
/// @param videoURL 需要编辑的视频本地地址
/// @param done 完成后的回调
/// @param cancel 取消回调
- (void)hx_presentVideoEditViewControllerWithManager:(HXPhotoManager * _Nonnull)manager
videoURL:(NSURL * _Nonnull)videoURL
done:(HXVideoEditViewControllerDidDoneBlock _Nullable)done
cancel:(HXVideoEditViewControllerDidCancelBlock _Nullable)cancel;
/// 跳转仿微信照片编辑界面
/// @param configuration 配置
/// @param photomodel 需要编辑的照片模型
/// 如果需要在原有编辑的基础上进行编辑直接将之前编辑model传入即可
/// 如果不需要在原有基础上编辑,请将 model.photoEdit 置为nil
/// @param delegate 代理
/// @param finish 完成后的回调
/// @param cancel 取消回调
- (void)hx_presentWxPhotoEditViewControllerWithConfiguration:(HXPhotoEditConfiguration * _Nonnull)configuration
photoModel:(HXPhotoModel * _Nonnull)photomodel
delegate:(id _Nullable)delegate
finish:(HX_PhotoEditViewControllerDidFinishBlock _Nullable)finish
cancel:(HX_PhotoEditViewControllerDidCancelBlock _Nullable)cancel;
/// 跳转仿微信照片编辑界面
/// @param configuration 配置
/// @param editImage 需要编辑的图片
/// @param photoEdit 之前编辑的数据为nil则重新开始编辑
/// @param finish 完成后的回调
/// @param cancel 取消回调
- (void)hx_presentWxPhotoEditViewControllerWithConfiguration:(HXPhotoEditConfiguration * _Nonnull)configuration
editImage:(UIImage * _Nonnull)editImage
photoEdit:(HXPhotoEdit * _Nullable)photoEdit
finish:(HX_PhotoEditViewControllerDidFinishBlock _Nullable)finish
cancel:(HX_PhotoEditViewControllerDidCancelBlock _Nullable)cancel;
- (BOOL)hx_navigationBarWhetherSetupBackground;
- (HXCustomNavigationController *_Nullable)hx_customNavigationController;
#pragma mark - < obsoleting >
/* <HXAlbumListViewControllerDelegate>
* delegate
*/
- (void)hx_presentAlbumListViewControllerWithManager:(HXPhotoManager *_Nullable)manager
delegate:(id _Nullable )delegate DEPRECATED_MSG_ATTRIBUTE("Use 'hx_presentSelectPhotoControllerWithManager:' instead");
@end

View File

@ -0,0 +1,252 @@
//
// UIViewController+HXExtension.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/11/24.
// Copyright © 2017 Silence. All rights reserved.
//
#import "UIViewController+HXExtension.h"
#import "HXPhotoPicker.h"
@implementation UIViewController (HXExtension)
- (void)hx_presentAlbumListViewControllerWithManager:(HXPhotoManager *)manager
delegate:(id)delegate {
NSSLog(@"Use 'hx_presentSelectPhotoControllerWithManager:'");
// HXAlbumListViewController *vc = [[HXAlbumListViewController alloc] initWithManager:manager];
// vc.delegate = delegate ? delegate : (id)self;
// HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithRootViewController:vc];
// nav.supportRotation = manager.configuration.supportRotation;
// nav.modalPresentationStyle = UIModalPresentationOverFullScreen;
// nav.modalPresentationCapturesStatusBarAppearance = YES;
// [self presentViewController:nav animated:YES completion:nil];
}
- (void)hx_presentSelectPhotoControllerWithManager:(HXPhotoManager *)manager
didDone:(void (^)(NSArray<HXPhotoModel *> *, NSArray<HXPhotoModel *> *, NSArray<HXPhotoModel *> *, BOOL, UIViewController *, HXPhotoManager *))models
cancel:(void (^)(UIViewController *, HXPhotoManager *))cancel {
viewControllerDidDoneBlock modelBlock = ^(NSArray<HXPhotoModel *> *allList, NSArray<HXPhotoModel *> *photoList, NSArray<HXPhotoModel *> *videoList, BOOL original, UIViewController *viewController, HXPhotoManager *manager) {
if (models) {
models(allList, photoList, videoList, original, viewController, manager);
}
};
viewControllerDidCancelBlock cancelBlock = ^(UIViewController *viewController, HXPhotoManager *manager) {
if (cancel) {
cancel(viewController, manager);
}
};
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithManager:manager doneBlock:modelBlock cancelBlock:cancelBlock];
[self presentViewController:nav animated:YES completion:nil];
}
- (void)hx_presentSelectPhotoControllerWithManager:(HXPhotoManager *_Nullable)manager
delegate:(id _Nullable )delegate {
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithManager:manager delegate:delegate];
[self presentViewController:nav animated:YES completion:nil];
}
- (void)hx_presentCustomCameraViewControllerWithManager:(HXPhotoManager *)manager
delegate:(id)delegate {
if(![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[self.view hx_showImageHUDText:[NSBundle hx_localizedStringForKey:@"无法使用相机!"]];
return;
}
HXWeakSelf
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
HXCustomCameraViewController *vc = [[HXCustomCameraViewController alloc] init];
vc.delegate = delegate ? delegate : (id)weakSelf;
vc.manager = manager;
vc.isOutside = YES;
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithRootViewController:vc];
nav.isCamera = YES;
nav.supportRotation = manager.configuration.supportRotation;
nav.modalPresentationStyle = UIModalPresentationOverFullScreen;
nav.modalPresentationCapturesStatusBarAppearance = YES;
[weakSelf presentViewController:nav animated:YES completion:nil];
}else {
[HXPhotoTools showUnusableCameraAlert:weakSelf];
}
});
}];
}
- (void)hx_presentCustomCameraViewControllerWithManager:(HXPhotoManager *)manager
done:(HXCustomCameraViewControllerDidDoneBlock)done
cancel:(HXCustomCameraViewControllerDidCancelBlock)cancel {
if(![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[self.view hx_showImageHUDText:[NSBundle hx_localizedStringForKey:@"无法使用相机!"]];
return;
}
HXWeakSelf
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
HXCustomCameraViewController *vc = [[HXCustomCameraViewController alloc] init];
vc.doneBlock = done;
vc.cancelBlock = cancel;
vc.manager = manager;
vc.isOutside = YES;
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithRootViewController:vc];
nav.isCamera = YES;
nav.supportRotation = manager.configuration.supportRotation;
nav.modalPresentationStyle = UIModalPresentationOverFullScreen;
nav.modalPresentationCapturesStatusBarAppearance = YES;
[weakSelf presentViewController:nav animated:YES completion:nil];
}else {
[HXPhotoTools showUnusableCameraAlert:weakSelf];
}
});
}];
}
- (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager *)manager
previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
showBottomPageControl:(BOOL)showBottomPageControl
currentIndex:(NSUInteger)currentIndex {
[self hx_presentPreviewPhotoControllerWithManager:manager previewStyle:previewStyle showBottomPageControl:showBottomPageControl currentIndex:currentIndex photoView:nil];
}
- (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager *)manager
previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
currentIndex:(NSUInteger)currentIndex
photoView:(HXPhotoView * _Nullable)photoView {
[self hx_presentPreviewPhotoControllerWithManager:manager previewStyle:previewStyle showBottomPageControl:YES currentIndex:currentIndex photoView:photoView];
}
- (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager *)manager
previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
showBottomPageControl:(BOOL)showBottomPageControl
currentIndex:(NSUInteger)currentIndex
photoView:(HXPhotoView * _Nullable)photoView {
HXPhotoPreviewViewController *vc = [[HXPhotoPreviewViewController alloc] init];
vc.disableaPersentInteractiveTransition = photoView.disableaInteractiveTransition;
vc.outside = YES;
vc.manager = manager ?: photoView.manager;
vc.exteriorPreviewStyle = photoView ? photoView.previewStyle : previewStyle;
vc.delegate = (id)self;
if (manager.afterSelectedArray) {
vc.modelArray = [NSMutableArray arrayWithArray:manager.afterSelectedArray];
}
if (currentIndex >= vc.modelArray.count) {
vc.currentModelIndex = vc.modelArray.count - 1;
}else if (currentIndex < 0) {
vc.currentModelIndex = 0;
}else {
vc.currentModelIndex = currentIndex;
}
if (photoView) {
vc.showBottomPageControl = photoView.previewShowDeleteButton;
}else {
vc.showBottomPageControl = showBottomPageControl;
}
vc.previewShowDeleteButton = photoView.previewShowDeleteButton;
vc.photoView = photoView;
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
vc.modalPresentationCapturesStatusBarAppearance = YES;
[self presentViewController:vc animated:YES completion:nil];
}
- (void)hx_presentWxPhotoEditViewControllerWithConfiguration:(HXPhotoEditConfiguration * _Nonnull)configuration
photoModel:(HXPhotoModel * _Nonnull)photomodel
delegate:(id _Nullable)delegate
finish:(HX_PhotoEditViewControllerDidFinishBlock _Nullable)finish
cancel:(HX_PhotoEditViewControllerDidCancelBlock _Nullable)cancel {
HX_PhotoEditViewController *vc = [[HX_PhotoEditViewController alloc] initWithConfiguration:configuration];
vc.delegate = delegate ?: self;
vc.photoModel = photomodel;
vc.finishBlock = finish;
vc.cancelBlock = cancel;
vc.supportRotation = YES;
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
vc.modalPresentationCapturesStatusBarAppearance = YES;
[self presentViewController:vc animated:YES completion:nil];
}
- (void)hx_presentWxPhotoEditViewControllerWithConfiguration:(HXPhotoEditConfiguration * _Nonnull)configuration
editImage:(UIImage * _Nonnull)editImage
photoEdit:(HXPhotoEdit * _Nullable)photoEdit
finish:(HX_PhotoEditViewControllerDidFinishBlock _Nullable)finish
cancel:(HX_PhotoEditViewControllerDidCancelBlock _Nullable)cancel {
HXPhotoModel *photoModel = [HXPhotoModel photoModelWithImage:editImage];
photoModel.photoEdit = photoEdit;
[self hx_presentWxPhotoEditViewControllerWithConfiguration:configuration
photoModel:photoModel
delegate:nil
finish:finish
cancel:cancel];
}
- (void)hx_presentPhotoEditViewControllerWithManager:(HXPhotoManager *)manager
photoModel:(HXPhotoModel *)photomodel
delegate:(id)delegate
done:(HXPhotoEditViewControllerDidDoneBlock)done
cancel:(HXPhotoEditViewControllerDidCancelBlock)cancel {
HXPhotoEditViewController *vc = [[HXPhotoEditViewController alloc] init];
vc.isInside = YES;
vc.delegate = delegate ?: self;
vc.manager = manager;
vc.model = photomodel;
vc.doneBlock = done;
vc.cancelBlock = cancel;
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
vc.modalPresentationCapturesStatusBarAppearance = YES;
[self presentViewController:vc animated:YES completion:nil];
}
- (void)hx_presentPhotoEditViewControllerWithManager:(HXPhotoManager * _Nonnull)manager
editPhoto:(UIImage * _Nonnull)editPhoto
done:(HXPhotoEditViewControllerDidDoneBlock _Nullable)done
cancel:(HXPhotoEditViewControllerDidCancelBlock _Nullable)cancel {
HXPhotoModel *photoModel = [HXPhotoModel photoModelWithImage:editPhoto];
[self hx_presentPhotoEditViewControllerWithManager:manager photoModel:photoModel delegate:nil done:done cancel:cancel];
}
- (void)hx_presentVideoEditViewControllerWithManager:(HXPhotoManager *)manager
videoModel:(HXPhotoModel *)videoModel
delegate:(id)delegate
done:(HXVideoEditViewControllerDidDoneBlock)done
cancel:(HXVideoEditViewControllerDidCancelBlock)cancel {
HXVideoEditViewController *vc = [[HXVideoEditViewController alloc] init];
vc.model = videoModel;
vc.delegate = delegate ?: self;
vc.manager = manager;
vc.isInside = YES;
vc.doneBlock = done;
vc.cancelBlock = cancel;
vc.modalPresentationStyle = UIModalPresentationOverFullScreen;
vc.modalPresentationCapturesStatusBarAppearance = YES;
[self presentViewController:vc animated:YES completion:nil];
}
- (void)hx_presentVideoEditViewControllerWithManager:(HXPhotoManager * _Nonnull)manager
videoURL:(NSURL * _Nonnull)videoURL
done:(HXVideoEditViewControllerDidDoneBlock _Nullable)done
cancel:(HXVideoEditViewControllerDidCancelBlock _Nullable)cancel {
HXPhotoModel *videoModel = [HXPhotoModel photoModelWithVideoURL:videoURL];
[self hx_presentVideoEditViewControllerWithManager:manager videoModel:videoModel delegate:nil done:done cancel:cancel];
}
- (BOOL)hx_navigationBarWhetherSetupBackground {
if ([self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefault]) {
return YES;
}else if ([self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsCompact]) {
return YES;
}else if ([self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsDefaultPrompt]) {
return YES;
}else if ([self.navigationController.navigationBar backgroundImageForBarMetrics:UIBarMetricsCompactPrompt]) {
return YES;
}else if (self.navigationController.navigationBar.backgroundColor) {
return YES;
}
return NO;
}
- (HXCustomNavigationController *)hx_customNavigationController {
if ([NSStringFromClass([self.navigationController class]) isEqualToString:@"HXCustomNavigationController"]) {
return (HXCustomNavigationController *)self.navigationController;
}
return nil;
}
@end

View File

@ -0,0 +1,65 @@
//
// HXDateAlbumViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXAlbumModel.h"
#import "HXPhotoManager.h"
#import "HXPickerResult.h"
@class HXAlbumListViewController;
@protocol HXAlbumListViewControllerDelegate <NSObject>
@optional
/**
@param albumListViewController self
*/
- (void)albumListViewControllerDidCancel:(HXAlbumListViewController *)albumListViewController;
/**
@param albumListViewController self
@param allList ()
@param photoList
@param videoList
@param original
*/
- (void)albumListViewController:(HXAlbumListViewController *)albumListViewController
didDoneAllList:(NSArray<HXPhotoModel *> *)allList
photos:(NSArray<HXPhotoModel *> *)photoList
videos:(NSArray<HXPhotoModel *> *)videoList
original:(BOOL)original;
- (void)albumListViewController:(HXAlbumListViewController *)albumListViewController
didDoneWithResult:(HXPickerResult *)result;
- (void)albumListViewControllerFinishDismissCompletion:(HXAlbumListViewController *)albumListViewController;
- (void)albumListViewControllerCancelDismissCompletion:(HXAlbumListViewController *)albumListViewController;
@end
@interface HXAlbumListViewController : UIViewController
@property (weak, nonatomic) id<HXAlbumListViewControllerDelegate> delegate;
@property (strong, nonatomic) HXPhotoManager *manager;
@property (copy, nonatomic) viewControllerDidDoneBlock doneBlock;
@property (copy, nonatomic) viewControllerDidCancelBlock cancelBlock;
- (instancetype)initWithManager:(HXPhotoManager *)manager;
@end
@interface HXAlbumListSingleViewCell : UITableViewCell
@property (strong, nonatomic) UIColor *bgColor;
@property (strong, nonatomic) UIColor *textColor;
@property (strong, nonatomic) UIColor *selectedBgColor;
@property (strong, nonatomic) UIColor *lineViewColor;
@property (strong, nonatomic) HXAlbumModel *model;
@property (copy, nonatomic) void (^getResultCompleteBlock)(NSInteger count, HXAlbumListSingleViewCell *myCell);
- (void)cancelRequest ;
@end

View File

@ -0,0 +1,632 @@
//
// HXDateAlbumViewController.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017 Silence. All rights reserved.
//
#import "HXAlbumListViewController.h"
#import "HXPhotoViewController.h"
#import "UIViewController+HXExtension.h"
#import "HXAssetManager.h"
@interface HXAlbumListViewController ()
<
HXPhotoViewControllerDelegate,
UITableViewDataSource,
UITableViewDelegate
>
@property (strong, nonatomic) UITableView *tableView;
@property (strong, nonatomic) NSMutableArray *albumModelArray;
@property (strong, nonatomic) UILabel *authorizationLb;
@property (assign, nonatomic) BOOL orientationDidChange;
@property (strong, nonatomic) NSIndexPath *beforeOrientationIndexPath;
@end
@implementation HXAlbumListViewController
- (instancetype)initWithManager:(HXPhotoManager *)manager {
self = [super init];
if (self) {
self.manager = manager;
}
return self;
}
- (void)requestData {
// 访
HXWeakSelf
self.hx_customNavigationController.reloadAsset = ^(BOOL initialAuthorization){
if (initialAuthorization) {
[weakSelf authorizationHandler];
}
};
[self authorizationHandler];
}
- (void)authorizationHandler {
PHAuthorizationStatus status = [HXPhotoTools authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
[self getAlbumModelList:YES];
}
#ifdef __IPHONE_14_0
else if (@available(iOS 14, *)) {
if (status == PHAuthorizationStatusLimited) {
[self getAlbumModelList:YES];
return;
}
#endif
else if (status == PHAuthorizationStatusDenied ||
status == PHAuthorizationStatusRestricted){
[self.hx_customNavigationController.view hx_handleLoading];
[self.view addSubview:self.authorizationLb];
[HXPhotoTools showNoAuthorizedAlertWithViewController:self status:status];
}
#ifdef __IPHONE_14_0
}else if (status == PHAuthorizationStatusDenied ||
status == PHAuthorizationStatusRestricted){
[self.hx_customNavigationController.view hx_handleLoading];
[self.view addSubview:self.authorizationLb];
[HXPhotoTools showNoAuthorizedAlertWithViewController:self status:status];
}
#endif
}
- (UIStatusBarStyle)preferredStatusBarStyle {
if ([HXPhotoCommon photoCommon].isDark) {
return UIStatusBarStyleLightContent;
}
return self.manager.configuration.statusBarStyle;
}
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
[self changeColor];
[self changeStatusBarStyle];
[self setNeedsStatusBarAppearanceUpdate];
UIColor *authorizationColor = self.manager.configuration.authorizationTipColor;
_authorizationLb.textColor = [HXPhotoCommon photoCommon].isDark ? [UIColor whiteColor] : authorizationColor;
}
}
#endif
}
- (void)viewDidLoad {
[super viewDidLoad];
self.extendedLayoutIncludesOpaqueBars = YES;
self.edgesForExtendedLayout = UIRectEdgeAll;
self.navigationController.popoverPresentationController.delegate = (id)self;
[self requestData];
[self setupUI];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customCameraViewControllerDidDoneClick) name:@"CustomCameraViewControllerDidDoneNotification" object:nil];
}
- (void)customCameraViewControllerDidDoneClick {
NSInteger i = 0;
for (HXAlbumModel *albumMd in self.albumModelArray) {
albumMd.cameraCount = [self.manager cameraCount];
if (i == 0 && !albumMd.localIdentifier) {
albumMd.tempImage = [self.manager firstCameraModel].thumbPhoto;
}
i++;
}
[self.tableView reloadData];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (self.orientationDidChange) {
[self changeSubviewFrame];
self.orientationDidChange = NO;
}
}
- (void)deviceOrientationChanged:(NSNotification *)notify {
self.orientationDidChange = YES;
}
- (void)changeSubviewFrame {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGFloat navBarHeight = hxNavigationBarHeight;
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown || HX_UI_IS_IPAD) {
navBarHeight = hxNavigationBarHeight;
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
}else if (orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft){
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
if ([UIApplication sharedApplication].statusBarHidden) {
navBarHeight = self.navigationController.navigationBar.hx_h;
}else {
navBarHeight = self.navigationController.navigationBar.hx_h + 20;
}
}
#pragma clang diagnostic pop
CGFloat leftMargin = 0;
CGFloat rightMargin = 0;
CGFloat bottomMargin = hxBottomMargin;
if (HX_IS_IPhoneX_All && (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)) {
leftMargin = 35;
rightMargin = 35;
bottomMargin = 0;
}
self.tableView.contentInset = UIEdgeInsetsMake(navBarHeight, leftMargin, bottomMargin, rightMargin);
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
}else {
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(navBarHeight, leftMargin, bottomMargin, rightMargin);
}
#else
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(navBarHeight, leftMargin, bottomMargin, rightMargin);
#endif
self.tableView.frame = self.view.bounds;
if (self.manager.configuration.albumListTableView) {
self.manager.configuration.albumListTableView(self.tableView);
}
self.navigationController.navigationBar.translucent = self.manager.configuration.navBarTranslucent;
if (self.manager.configuration.navigationBar) {
self.manager.configuration.navigationBar(self.navigationController.navigationBar, self);
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self changeStatusBarStyle];
if (self.manager.viewWillAppear) {
self.manager.viewWillAppear(self);
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.manager.viewWillDisappear) {
self.manager.viewWillDisappear(self);
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
if (self.manager.viewDidDisappear) {
self.manager.viewDidDisappear(self);
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
- (void)changeStatusBarStyle {
if ([HXPhotoCommon photoCommon].isDark) {
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
return;
}
[[UIApplication sharedApplication] setStatusBarStyle:self.manager.configuration.statusBarStyle animated:YES];
}
#pragma clang diagnostic pop
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (!self.albumModelArray.count) {
PHAuthorizationStatus status = [HXPhotoTools authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
[self getAlbumModelList:NO];
}
#ifdef __IPHONE_14_0
else if (@available(iOS 14, *)) {
if (status == PHAuthorizationStatusLimited) {
[self getAlbumModelList:NO];
}
}
#endif
}
if (self.manager.viewDidAppear) {
self.manager.viewDidAppear(self);
}
}
- (void)setupUI {
self.title = [NSBundle hx_localizedStringForKey:@"相册"];
[self changeColor];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSBundle hx_localizedStringForKey:@"取消"] style:UIBarButtonItemStyleDone target:self action:@selector(cancelClick)];
if (self.manager.configuration.navigationBar) {
self.manager.configuration.navigationBar(self.navigationController.navigationBar, self);
}
}
- (void)changeColor {
UIColor *backgroudColor;
UIColor *themeColor;
UIColor *navBarBackgroudColor;
UIColor *navigationTitleColor;
if ([HXPhotoCommon photoCommon].isDark) {
backgroudColor = [UIColor colorWithRed:0.075 green:0.075 blue:0.075 alpha:1];
themeColor = [UIColor whiteColor];
navBarBackgroudColor = [UIColor blackColor];
navigationTitleColor = [UIColor whiteColor];
}else {
backgroudColor = self.manager.configuration.albumListViewBgColor;
themeColor = self.manager.configuration.themeColor;
navBarBackgroudColor = self.manager.configuration.navBarBackgroudColor;
navigationTitleColor = self.manager.configuration.navigationTitleColor;
}
self.view.backgroundColor = backgroudColor;
self.tableView.backgroundColor = backgroudColor;
[self.navigationController.navigationBar setTintColor:themeColor];
self.navigationController.navigationBar.barTintColor = navBarBackgroudColor;
self.navigationController.navigationBar.barStyle = self.manager.configuration.navBarStyle;
if (self.manager.configuration.navBarBackgroundImage) {
[self.navigationController.navigationBar setBackgroundImage:self.manager.configuration.navBarBackgroundImage forBarMetrics:UIBarMetricsDefault];
}
// if (navBarBackgroudColor) {
// [self.navigationController.navigationBar setBackgroundColor:navBarBackgroudColor];
// [self.navigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
// }
if (self.manager.configuration.navigationTitleSynchColor) {
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : themeColor};
}else {
if (navigationTitleColor) {
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : navigationTitleColor};
}else {
self.navigationController.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor blackColor]};
}
}
if (@available(iOS 15.0, *)) {
UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
appearance.titleTextAttributes = self.navigationController.navigationBar.titleTextAttributes;
switch (self.manager.configuration.navBarStyle) {
case UIBarStyleDefault:
appearance.backgroundEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
break;
default:
appearance.backgroundEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
break;
}
self.navigationController.navigationBar.standardAppearance = appearance;
self.navigationController.navigationBar.scrollEdgeAppearance = appearance;
}
}
- (void)configTableView {
[self.view addSubview:self.tableView];
[self changeSubviewFrame];
}
- (void)cancelClick {
[self.manager cancelBeforeSelectedList];
if ([self.delegate respondsToSelector:@selector(albumListViewControllerDidCancel:)]) {
[self.delegate albumListViewControllerDidCancel:self];
}
if (self.cancelBlock) {
self.cancelBlock(self, self.manager);
}
self.manager.selectPhotoing = NO;
BOOL selectPhotoCancelDismissAnimated = self.manager.selectPhotoCancelDismissAnimated;
[self dismissViewControllerAnimated:selectPhotoCancelDismissAnimated completion:^{
if ([self.delegate respondsToSelector:@selector(albumListViewControllerCancelDismissCompletion:)]) {
[self.delegate albumListViewControllerCancelDismissCompletion:self];
}
}];
}
#pragma mark - < HXPhotoViewControllerDelegate >
- (void)photoViewController:(HXPhotoViewController *)photoViewController didDoneAllList:(NSArray<HXPhotoModel *> *)allList photos:(NSArray<HXPhotoModel *> *)photoList videos:(NSArray<HXPhotoModel *> *)videoList original:(BOOL)original {
if ([self.delegate respondsToSelector:@selector(albumListViewController:didDoneAllList:photos:videos:original:)]) {
[self.delegate albumListViewController:self didDoneAllList:allList photos:photoList videos:videoList original:original];
}
if (self.doneBlock) {
self.doneBlock(allList, photoList, videoList, original, self, self.manager);
}
}
- (void)photoViewController:(HXPhotoViewController *)photoViewController didDoneWithResult:(HXPickerResult *)result {
if ([self.delegate respondsToSelector:@selector(albumListViewController:didDoneWithResult:)]) {
[self.delegate albumListViewController:self
didDoneWithResult:result];
}
}
- (void)photoViewControllerDidCancel:(HXPhotoViewController *)photoViewController {
[self cancelClick];
}
- (void)photoViewControllerDidChangeSelect:(HXPhotoModel *)model selected:(BOOL)selected {
if (self.albumModelArray.count > 0) {
// HXAlbumModel *albumModel = self.albumModelArray[model.currentAlbumIndex];
// if (selected) {
// albumModel.selectedCount++;
// }else {
// albumModel.selectedCount--;
// }
// [self.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:model.currentAlbumIndex inSection:0]]];
}
}
- (void)pushPhotoListViewControllerWithAlbumModel:(HXAlbumModel *)albumModel animated:(BOOL) animated {
if (self.navigationController.topViewController != self) {
[self.navigationController popToViewController:self animated:NO];
}
HXPhotoViewController *vc = [[HXPhotoViewController alloc] init];
vc.manager = self.manager;
vc.title = albumModel.albumName;
vc.albumModel = albumModel;
vc.delegate = self;
[self.navigationController pushViewController:vc animated:animated];
}
- (void)getAlbumModelList:(BOOL)isFirst {
HXWeakSelf
if (isFirst) {
if (self.hx_customNavigationController.cameraRollAlbumModel) {
[self.view hx_handleLoading];
[self pushPhotoListViewControllerWithAlbumModel:self.hx_customNavigationController.cameraRollAlbumModel animated:NO];
}else {
self.hx_customNavigationController.requestCameraRollCompletion = ^{
[weakSelf.view hx_handleLoading];
[weakSelf pushPhotoListViewControllerWithAlbumModel:weakSelf.hx_customNavigationController.cameraRollAlbumModel animated:NO];
};
}
}else {
[self configTableView];
[self.view hx_showLoadingHUDText:nil];
if (self.hx_customNavigationController.albums) {
self.albumModelArray = self.hx_customNavigationController.albums;
[self.tableView reloadData];
[self.view hx_handleLoading:YES];
}else {
self.hx_customNavigationController.requestAllAlbumCompletion = ^{
weakSelf.albumModelArray = weakSelf.hx_customNavigationController.albums;
[weakSelf.tableView reloadData];
[weakSelf.view hx_handleLoading:YES];
};
}
}
}
#pragma mark - < UITableViewDataSource >
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.albumModelArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
HXAlbumListSingleViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"tableViewCellId"];
cell.bgColor = self.manager.configuration.albumListViewCellBgColor;
cell.textColor = self.manager.configuration.albumListViewCellTextColor;
cell.selectedBgColor = self.manager.configuration.albumListViewCellSelectBgColor;
cell.lineViewColor = self.manager.configuration.albumListViewCellLineColor;
cell.model = self.albumModelArray[indexPath.row];
HXWeakSelf
cell.getResultCompleteBlock = ^(NSInteger count, HXAlbumListSingleViewCell *myCell) {
if (count <= 0) {
if ([weakSelf.albumModelArray containsObject:myCell.model]) {
NSIndexPath *myIndexPath = [weakSelf.tableView indexPathForCell:myCell];
if (myIndexPath) {
[weakSelf.albumModelArray removeObject:myCell.model];
[weakSelf.tableView deleteRowsAtIndexPaths:@[myIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
}
};
return cell;
}
#pragma mark - < UITableViewDelegate >
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (self.navigationController.topViewController != self) {
return;
}
[self.hx_customNavigationController clearAssetCache];
HXAlbumModel *model = self.albumModelArray[indexPath.row];
[self pushPhotoListViewControllerWithAlbumModel:model animated:YES];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[(HXAlbumListSingleViewCell *)cell cancelRequest];
}
#pragma mark - < >
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.hx_w, self.view.hx_h) style:UITableViewStylePlain];
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.estimatedRowHeight = 0;
_tableView.estimatedSectionFooterHeight = 0;
_tableView.estimatedSectionHeaderHeight = 0;
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[_tableView registerClass:[HXAlbumListSingleViewCell class] forCellReuseIdentifier:@"tableViewCellId"];
#ifdef __IPHONE_11_0
if (@available(iOS 11.0, *)) {
// if ([self hx_navigationBarWhetherSetupBackground]) {
// self.navigationController.navigationBar.translucent = NO;
// }else {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
// }
#else
if ((NO)) {
#endif
} else {
// if ([self hx_navigationBarWhetherSetupBackground]) {
// self.navigationController.navigationBar.translucent = NO;
// }else {
self.automaticallyAdjustsScrollViewInsets = NO;
// }
}
}
return _tableView;
}
- (UILabel *)authorizationLb {
if (!_authorizationLb) {
_authorizationLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 200, self.view.frame.size.width, 100)];
_authorizationLb.text = [NSBundle hx_localizedStringForKey:@"无法访问照片\n请点击这里前往设置中允许访问照片"];
_authorizationLb.textAlignment = NSTextAlignmentCenter;
_authorizationLb.numberOfLines = 0;
UIColor *authorizationColor = self.manager.configuration.authorizationTipColor;
_authorizationLb.textColor = [HXPhotoCommon photoCommon].isDark ? [UIColor whiteColor] : authorizationColor;
_authorizationLb.font = [UIFont systemFontOfSize:15];
_authorizationLb.userInteractionEnabled = YES;
[_authorizationLb addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(goSetup)]];
}
return _authorizationLb;
}
- (void)dealloc {
self.manager.selectPhotoing = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"CustomCameraViewControllerDidDoneNotification" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
}
- (void)goSetup {
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if (@available(iOS 10.0, *)) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}else {
[[UIApplication sharedApplication] openURL:url];
}
}
@end
@interface HXAlbumListSingleViewCell ()
@property (strong, nonatomic) UIImageView *coverView1;
@property (strong, nonatomic) UILabel *albumNameLb;
@property (strong, nonatomic) UILabel *photoNumberLb;
@property (assign, nonatomic) PHImageRequestID requestId1;
@property (assign, nonatomic) PHImageRequestID requestId2;
@property (assign, nonatomic) PHImageRequestID requestId3;
@property (strong, nonatomic) UIView *lineView;
@property (strong, nonatomic) UIView *selectBgView;
@end
@implementation HXAlbumListSingleViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self setupUI];
}
return self;
}
- (void)setupUI {
[self.contentView addSubview:self.coverView1];
[self.contentView addSubview:self.albumNameLb];
[self.contentView addSubview:self.photoNumberLb];
[self.contentView addSubview:self.lineView];
}
- (void)cancelRequest {
if (self.requestId1) {
[[PHImageManager defaultManager] cancelImageRequest:self.requestId1];
self.requestId1 = -1;
}
}
- (void)setModel:(HXAlbumModel *)model {
_model = model;
[self changeColor];
self.albumNameLb.text = self.model.albumName;
if (!model.assetResult && model.localIdentifier) {
HXWeakSelf
[model getResultWithCompletion:^(HXAlbumModel *albumModel) {
if (albumModel == weakSelf.model) {
[weakSelf setAlbumImage];
}
}];
}else {
[self setAlbumImage];
}
if (!model.assetResult || !model.count) {
self.coverView1.image = model.tempImage ?: [UIImage hx_imageNamed:@"hx_yundian_tupian"];
}
}
- (void)setAlbumImage {
NSInteger photoCount = self.model.count;
HXWeakSelf
PHAsset *asset = self.model.assetResult.lastObject;
if (asset) {
self.requestId1 = [HXAssetManager requestThumbnailImageForAsset:asset targetWidth:300 completion:^(UIImage * _Nonnull result, NSDictionary<NSString *,id> * _Nonnull info) {
if (weakSelf.model.assetResult.lastObject == asset && result) {
weakSelf.coverView1.image = result;
}
}];
}
self.photoNumberLb.text = [@(photoCount + self.model.cameraCount).stringValue hx_countStrBecomeComma];
if (self.getResultCompleteBlock) {
self.getResultCompleteBlock(photoCount + self.model.cameraCount, self);
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
[super setHighlighted:highlighted animated:animated];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.coverView1.frame = CGRectMake(10, 5, self.hx_h - 10, self.hx_h - 10);
CGFloat albumNameLbX = CGRectGetMaxX(self.coverView1.frame) + 12;
CGFloat albumNameLbY = self.hx_h / 2 - 16;
self.albumNameLb.frame = CGRectMake(albumNameLbX, albumNameLbY, self.hx_w - albumNameLbX - 40, 14);
self.photoNumberLb.frame = CGRectMake(albumNameLbX, self.hx_h / 2 + 4, self.hx_w, 13);
self.lineView.frame = CGRectMake(10, self.hx_h - 0.5f, self.hx_w - 22, 0.5f);
self.selectBgView.frame = self.bounds;
}
- (void)dealloc {
// [self cancelRequest];
}
#pragma mark - < cell >
- (UIView *)lineView {
if (!_lineView) {
_lineView = [[UIView alloc] init];
}
return _lineView;
}
- (UIImageView *)coverView1 {
if (!_coverView1) {
_coverView1 = [[UIImageView alloc] init];
_coverView1.contentMode = UIViewContentModeScaleAspectFill;
_coverView1.clipsToBounds = YES;
_coverView1.layer.borderColor = [UIColor whiteColor].CGColor;
_coverView1.layer.borderWidth = 0.5f;
}
return _coverView1;
}
- (UILabel *)albumNameLb {
if (!_albumNameLb) {
_albumNameLb = [[UILabel alloc] init];
_albumNameLb.font = [UIFont hx_mediumSFUITextOfSize:13];
}
return _albumNameLb;
}
- (UILabel *)photoNumberLb {
if (!_photoNumberLb) {
_photoNumberLb = [[UILabel alloc] init];
_photoNumberLb.textColor = [UIColor lightGrayColor];
_photoNumberLb.font = [UIFont systemFontOfSize:12];
}
return _photoNumberLb;
}
- (UIView *)selectBgView {
if (!_selectBgView) {
_selectBgView = [[UIView alloc] init];
_selectBgView.backgroundColor = [UIColor colorWithRed:0.125 green:0.125 blue:0.125 alpha:1];
}
return _selectBgView;
}
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
[self changeColor];
}
}
#endif
}
- (void)changeAlbumNameTextColor {
if ([HXPhotoCommon photoCommon].isDark) {
self.albumNameLb.textColor = [UIColor whiteColor];
}else {
self.albumNameLb.textColor = self.textColor;
}
}
- (void)changeColor {
self.backgroundColor = [HXPhotoCommon photoCommon].isDark ? [UIColor colorWithRed:0.075 green:0.075 blue:0.075 alpha:1] : self.bgColor;
if (self.selectedBgColor) {
self.selectBgView.backgroundColor = self.selectedBgColor;
self.selectedBackgroundView = self.selectBgView;
}else {
self.selectedBackgroundView = [HXPhotoCommon photoCommon].isDark ? self.selectBgView : nil;
}
self.lineView.backgroundColor = [HXPhotoCommon photoCommon].isDark ? [[UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1] colorWithAlphaComponent:1] : self.lineViewColor;
[self changeAlbumNameTextColor];
}
@end

View File

@ -0,0 +1,82 @@
//
// HXCustomCameraController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/31.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
@class HXPhotoManager;
@protocol HXCustomCameraControllerDelegate <NSObject>
@optional;
- (void)deviceConfigurationFailedWithError:(NSError *)error;
- (void)mediaCaptureFailedWithError:(NSError *)error;
- (void)assetLibraryWriteFailedWithError:(NSError *)error;
- (void)videoStartRecording;
- (void)videoFinishRecording:(NSURL *)videoURL;
- (void)takePicturesComplete:(UIImage *)image;
- (void)takePicturesFailed;
- (void)handleDeviceMotion:(UIDeviceOrientation)deviceOrientation;
//- (void)rampedZoomToValue:(CGFloat)value;
@end
@interface HXCustomCameraController : NSObject
@property (weak, nonatomic) id<HXCustomCameraControllerDelegate> delegate;
@property (strong, nonatomic, readonly) AVCaptureSession *captureSession;
/// 相机界面默认前置摄像头
@property (assign, nonatomic) BOOL defaultFrontCamera;
@property (assign, nonatomic) NSTimeInterval videoMaximumDuration;
- (void)initSeesion;
- (void)setupPreviewLayer:(AVCaptureVideoPreviewLayer *)previewLayer startSessionCompletion:(void (^)(BOOL success))completion;
- (void)startSession;
- (void)stopSession;
- (void)initImageOutput;
- (void)initMovieOutput;
- (void)removeMovieOutput;
- (BOOL)addAudioInput;
- (BOOL)switchCameras;
- (BOOL)canSwitchCameras;
@property (nonatomic, readonly) NSUInteger cameraCount;
@property (nonatomic, readonly) BOOL cameraHasTorch;
@property (nonatomic, readonly) BOOL cameraHasFlash;
@property (nonatomic, readonly) BOOL cameraSupportsTapToFocus;
@property (nonatomic, readonly) BOOL cameraSupportsTapToExpose;
@property (nonatomic) AVCaptureTorchMode torchMode;
@property (nonatomic) AVCaptureFlashMode flashMode;
@property (copy, nonatomic) NSString *videoCodecKey;
@property (copy, nonatomic) NSString *sessionPreset;
- (void)focusAtPoint:(CGPoint)point;
- (void)exposeAtPoint:(CGPoint)point;
- (void)resetFocusAndExposureModes;
- (void)captureStillImage;
- (void)startRecording;
- (void)stopRecording;
- (BOOL)isRecording;
- (CMTime)recordedDuration;
- (void)startMontionUpdate;
- (void)stopMontionUpdate;
- (BOOL)cameraSupportsZoom;
- (CGFloat)maxZoomFactor;
- (CGFloat)currentZoomFacto;
- (void)setZoomValue:(CGFloat)zoomValue;
- (void)rampZoomToValue:(CGFloat)zoomValue;
- (void)cancelZoom;
@end

View File

@ -0,0 +1,599 @@
//
// HXCustomCameraController.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/31.
// Copyright © 2017 Silence. All rights reserved.
//
#import "HXCustomCameraController.h"
#import <CoreMotion/CoreMotion.h>
#import "HXPhotoTools.h"
#import "HXCustomPreviewView.h"
const CGFloat HXZoomRate = 1.0f;
@interface HXCustomCameraController ()<AVCaptureFileOutputRecordingDelegate>
@property (strong, nonatomic) dispatch_queue_t videoQueue;
@property (strong, nonatomic) AVCaptureSession *captureSession;
@property (weak, nonatomic) AVCaptureDeviceInput *activeVideoInput;
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutput;
@property (strong, nonatomic) AVCaptureMovieFileOutput *movieOutput;
@property (strong, nonatomic) NSURL *outputURL;
@property (strong, nonatomic) CMMotionManager *motionManager;
@property (nonatomic, assign) UIDeviceOrientation deviceOrientation;
@property (nonatomic, assign) UIDeviceOrientation imageOrientation;
@end
@implementation HXCustomCameraController
- (instancetype)init {
self = [super init];
if (self) {
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 1/15.0;
}
return self;
}
- (void)startMontionUpdate {
if (self.motionManager.deviceMotionAvailable) {
HXWeakSelf
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
[weakSelf performSelectorOnMainThread:@selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
}];
}
}
- (void)stopMontionUpdate {
[self.motionManager stopDeviceMotionUpdates];
}
- (void)dealloc {
}
///
- (void)handleDeviceMotion:(CMDeviceMotion *)deviceMotion {
double x = deviceMotion.gravity.x;
double y = deviceMotion.gravity.y;
if (fabs(y) >= fabs(x)) {
if (y >= 0) {
_imageOrientation = UIDeviceOrientationPortraitUpsideDown;
_deviceOrientation = UIDeviceOrientationPortraitUpsideDown;
if ([self.delegate respondsToSelector:@selector(handleDeviceMotion:)]) {
[self.delegate handleDeviceMotion:UIDeviceOrientationPortraitUpsideDown];
}
} else {
_imageOrientation = UIDeviceOrientationPortrait;
_deviceOrientation = UIDeviceOrientationPortrait;
if ([self.delegate respondsToSelector:@selector(handleDeviceMotion:)]) {
[self.delegate handleDeviceMotion:UIDeviceOrientationPortrait];
}
}
} else {
if (x >= 0) { // Home
_imageOrientation = UIDeviceOrientationLandscapeRight;
_deviceOrientation = UIDeviceOrientationLandscapeRight;
if ([self.delegate respondsToSelector:@selector(handleDeviceMotion:)]) {
[self.delegate handleDeviceMotion:UIDeviceOrientationLandscapeRight];
}
} else {
_imageOrientation = UIDeviceOrientationLandscapeLeft;
_deviceOrientation = UIDeviceOrientationLandscapeLeft; // Home
if ([self.delegate respondsToSelector:@selector(handleDeviceMotion:)]) {
[self.delegate handleDeviceMotion:UIDeviceOrientationLandscapeLeft];
}
}
}
}
- (void)initSeesion {
self.captureSession = [[AVCaptureSession alloc] init];
}
- (void)setupPreviewLayer:(AVCaptureVideoPreviewLayer *)previewLayer startSessionCompletion:(void (^)(BOOL success))completion {
if ([self.captureSession canSetSessionPreset:self.sessionPreset]) {
self.captureSession.sessionPreset = self.sessionPreset;
}else {
if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
}else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
}else {
self.captureSession.sessionPreset = AVCaptureSessionPreset640x480;
}
}
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (self.defaultFrontCamera) {
videoDevice = [self cameraWithPosition:AVCaptureDevicePositionFront];
}
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
if (videoInput) {
if ([self.captureSession canAddInput:videoInput]) {
[self.captureSession addInput:videoInput];
self.activeVideoInput = videoInput;
}
}else {
if (completion) {
completion(NO);
}
return;
}
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
if (completion) {
completion(YES);
}
}
- (AVCaptureMovieFileOutput *)movieOutput {
if (!_movieOutput) {
_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
CMTime maxDuration = CMTimeMakeWithSeconds(MAX(1, self.videoMaximumDuration), 30);
_movieOutput.maxRecordedDuration = maxDuration;
}
return _movieOutput;
}
- (AVCaptureStillImageOutput *)imageOutput {
if (!_imageOutput) {
_imageOutput = [[AVCaptureStillImageOutput alloc] init];
_imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
_imageOutput.highResolutionStillImageOutputEnabled = YES;
}
return _imageOutput;
}
- (void)initImageOutput {
if ([self.captureSession canAddOutput:self.imageOutput]) {
[self.captureSession addOutput:self.imageOutput];
}
}
- (void)initMovieOutput {
if ([self.captureSession canAddOutput:self.movieOutput]) {
[self.captureSession addOutput:self.movieOutput];
AVCaptureConnection *videoConnection = [self.movieOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoStabilizationSupported]) {
videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
}
}
}
- (void)removeMovieOutput {
[self.captureSession removeOutput:self.movieOutput];
}
- (BOOL)addAudioInput {
NSError *error;
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
if (audioDevice) {
if ([self.captureSession canAddInput:audioInput]) {
[self.captureSession addInput:audioInput];
}
}else {
return NO;
}
if (error) {
return NO;
}else {
return YES;
}
}
- (void)startSession {
AVCaptureSession *session = self.captureSession;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (![session isRunning]) {
[session startRunning];
}
});
// if (![self.captureSession isRunning]) {
// dispatch_async(self.videoQueue, ^{
// [self.captureSession startRunning];
// });
// }
}
- (void)stopSession {
AVCaptureSession *session = self.captureSession;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (session.running) {
[session stopRunning];
}
});
// if ([self.captureSession isRunning]) {
// dispatch_async(self.videoQueue, ^{
// [self.captureSession stopRunning];
// });
// }
}
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices) {
if (device.position == position) {
return device;
}
}
return nil;
}
- (AVCaptureDevice *)activeCamera {
return self.activeVideoInput.device;
}
- (AVCaptureDevice *)inactiveCamer {
AVCaptureDevice *device = nil;
if (self.cameraCount > 1) {
if ([self activeCamera].position == AVCaptureDevicePositionBack) {
device = [self cameraWithPosition:AVCaptureDevicePositionFront];
}else {
device = [self cameraWithPosition:AVCaptureDevicePositionBack];
}
}
return device;
}
- (BOOL)canSwitchCameras {
return self.cameraCount > 1;
}
- (NSUInteger)cameraCount {
return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
}
- (BOOL)switchCameras {
if (![self canSwitchCameras]) {
return NO;
}
NSError *error;
AVCaptureDevice *videoDevice = [self inactiveCamer];
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
if (videoInput) {
[self.captureSession beginConfiguration];
[self.captureSession removeInput:self.activeVideoInput];
if ([self.captureSession canAddInput:videoInput]) {
[self.captureSession addInput:videoInput];
self.activeVideoInput = videoInput;
}else {
[self.captureSession addInput:self.activeVideoInput];
}
[self.captureSession commitConfiguration];
}else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
return NO;
}
return YES;
}
- (BOOL)cameraSupportsTapToFocus {
return [[self activeCamera] isFocusPointOfInterestSupported];
}
- (void)focusAtPoint:(CGPoint)point {
AVCaptureDevice *device = [self activeCamera];
if (device.isFocusPointOfInterestSupported && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
NSError *error;
if ([device lockForConfiguration:&error]) {
if (device.smoothAutoFocusSupported) {
device.smoothAutoFocusEnabled = YES;
}
device.focusPointOfInterest = point;
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
[device unlockForConfiguration];
}else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
}
- (BOOL)cameraSupportsTapToExpose {
return [[self activeCamera] isExposurePointOfInterestSupported];
}
static const NSString *HXCustomCameraAdjustingExposureContext;
- (void)exposeAtPoint:(CGPoint)point {
AVCaptureDevice *device = [self activeCamera];
AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoExposure;
if (device.isExposurePointOfInterestSupported && [device isExposureModeSupported:exposureMode]) {
NSError *error;
if ([device lockForConfiguration:&error]) {
device.exposurePointOfInterest = point;
device.exposureMode = exposureMode;
// if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
// [device addObserver:self forKeyPath:@"adjustingExposure" options:NSKeyValueObservingOptionNew context:&HXCustomCameraAdjustingExposureContext];
// }
[device unlockForConfiguration];
}else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
}
- (void)resetFocusAndExposureModes {
AVCaptureDevice *device = [self activeCamera];
AVCaptureExposureMode exposureMode =
AVCaptureExposureModeContinuousAutoExposure;
AVCaptureFocusMode focusMode = AVCaptureFocusModeContinuousAutoFocus;
BOOL canResetFocus = [device isFocusPointOfInterestSupported] &&
[device isFocusModeSupported:focusMode];
BOOL canResetExposure = [device isExposurePointOfInterestSupported] &&
[device isExposureModeSupported:exposureMode];
CGPoint centerPoint = CGPointMake(0.5f, 0.5f);
NSError *error;
if ([device lockForConfiguration:&error]) {
if (device.smoothAutoFocusSupported) {
device.smoothAutoFocusEnabled = YES;
}
if (canResetFocus) {
device.focusMode = focusMode;
device.focusPointOfInterest = centerPoint;
}
if (canResetExposure) {
device.exposureMode = exposureMode;
device.exposurePointOfInterest = centerPoint;
}
[device unlockForConfiguration];
} else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
//
- (BOOL)cameraHasFlash {
return [[self activeCamera] hasFlash];
}
- (AVCaptureFlashMode)flashMode {
return [[self activeCamera] flashMode];
}
- (void)setFlashMode:(AVCaptureFlashMode)flashMode {
AVCaptureDevice *device = [self activeCamera];
if (device.flashMode != flashMode &&
[device isFlashModeSupported:flashMode]) {
NSError *error;
if ([device lockForConfiguration:&error]) {
device.flashMode = flashMode;
[device unlockForConfiguration];
} else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
}
//
- (BOOL)cameraHasTorch {
return [[self activeCamera] hasTorch];
}
- (AVCaptureTorchMode)torchMode {
return [[self activeCamera] torchMode];
}
- (void)setTorchMode:(AVCaptureTorchMode)torchMode {
AVCaptureDevice *device = [self activeCamera];
if (device.torchMode != torchMode &&
[device isTorchModeSupported:torchMode]) {
NSError *error;
if ([device lockForConfiguration:&error]) {
device.torchMode = torchMode;
[device unlockForConfiguration];
} else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
}
- (void)captureStillImage {
AVCaptureConnection *connection =
[self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
if (connection.isVideoOrientationSupported) {
connection.videoOrientation = [self currentVideoOrientation];
}
AVCaptureDevicePosition position = [[self activeCamera] position];
if (position == AVCaptureDevicePositionUnspecified ||
position == AVCaptureDevicePositionFront) {
connection.videoMirrored = YES;
}else {
connection.videoMirrored = NO;
}
HXWeakSelf
id handler = ^(CMSampleBufferRef sampleBuffer, NSError *error) {
if (sampleBuffer != NULL) {
NSData *imageData =
[AVCaptureStillImageOutput
jpegStillImageNSDataRepresentation:sampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
if ([weakSelf.delegate respondsToSelector:@selector(takePicturesComplete:)]) {
[weakSelf.delegate takePicturesComplete:image];
}
} else {
if ([weakSelf.delegate respondsToSelector:@selector(takePicturesFailed)]) {
[weakSelf.delegate takePicturesFailed];
}
}
};
[self.imageOutput captureStillImageAsynchronouslyFromConnection:connection
completionHandler:handler];
}
- (AVCaptureVideoOrientation)currentVideoOrientation {
AVCaptureVideoOrientation orientation;
switch (self.imageOrientation) {
case UIDeviceOrientationPortrait:
orientation = AVCaptureVideoOrientationPortrait;
break;
case UIDeviceOrientationLandscapeRight:
orientation = AVCaptureVideoOrientationLandscapeLeft;
break;
case UIDeviceOrientationPortraitUpsideDown:
orientation = AVCaptureVideoOrientationPortraitUpsideDown;
break;
default:
orientation = AVCaptureVideoOrientationLandscapeRight;
break;
}
return orientation;
}
- (BOOL)isRecording {
return self.movieOutput.isRecording;
}
- (void)startRecording {
if (![self isRecording]) {
AVCaptureConnection *videoConnection =
[self.movieOutput connectionWithMediaType:AVMediaTypeVideo];
//
if (HX_IOS11_Later && self.videoCodecKey) {
NSMutableDictionary* outputSettings = [NSMutableDictionary dictionary];
outputSettings[AVVideoCodecKey] = self.videoCodecKey;
[self.movieOutput setOutputSettings:outputSettings forConnection:videoConnection];
}
if ([videoConnection isVideoOrientationSupported]) {
videoConnection.videoOrientation = (AVCaptureVideoOrientation)_deviceOrientation;
}
AVCaptureDevice *device = [self activeCamera];
if (device.isSmoothAutoFocusSupported) {
NSError *error;
if ([device lockForConfiguration:&error]) {
device.smoothAutoFocusEnabled = NO;
[device unlockForConfiguration];
}
}
self.outputURL = [self uniqueURL];
[self.movieOutput startRecordingToOutputFileURL:self.outputURL
recordingDelegate:self];
}
}
- (CMTime)recordedDuration {
return self.movieOutput.recordedDuration;
}
- (NSURL *)uniqueURL {
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), [NSString stringWithFormat:@"hx%@.mov",[self videoOutFutFileName]]]];
}
- (NSString *)videoOutFutFileName {
return [NSString hx_fileName];
}
- (void)stopRecording {
if ([self isRecording]) {
[self.movieOutput stopRecording];
}
}
#pragma mark - AVCaptureFileOutputRecordingDelegate
//
- (void)captureOutput:(AVCaptureFileOutput *)output didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections {
if ([self.delegate respondsToSelector:@selector(videoStartRecording)]) {
[self.delegate videoStartRecording];
}
}
//
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error {
if ([error.userInfo[AVErrorRecordingSuccessfullyFinishedKey] boolValue]) {
if ([self.delegate respondsToSelector:@selector(videoFinishRecording:)]) {
[self.delegate videoFinishRecording:[self.outputURL copy]];
}
self.outputURL = nil;
return;
}
if (error) {
if ([self.delegate respondsToSelector:@selector(mediaCaptureFailedWithError:)]) {
[self.delegate mediaCaptureFailedWithError:error];
}
} else {
if ([self.delegate respondsToSelector:@selector(videoFinishRecording:)]) {
[self.delegate videoFinishRecording:[self.outputURL copy]];
}
}
self.outputURL = nil;
}
- (void)updateZoomingDelegate {
// CGFloat curZoomFactor = self.activeCamera.videoZoomFactor;
// CGFloat maxZoomFactor = [self maxZoomFactor];
// CGFloat value = log(curZoomFactor) / log(maxZoomFactor);
// if ([self.delegate respondsToSelector:@selector(rampedZoomToValue:)]) {
// [self.delegate rampedZoomToValue:value];
// }
}
- (BOOL)cameraSupportsZoom {
return self.activeCamera.activeFormat.videoMaxZoomFactor > 1.0f;
}
- (CGFloat)maxZoomFactor {
return MIN(self.activeCamera.activeFormat.videoMaxZoomFactor, 5.0f);
}
- (CGFloat)currentZoomFacto {
return self.activeCamera.videoZoomFactor;
}
- (void)setZoomValue:(CGFloat)zoomValue {
if (zoomValue > self.maxZoomFactor) {
zoomValue = self.maxZoomFactor;
}
if (!self.activeCamera.isRampingVideoZoom) {
NSError *error;
if ([self.activeCamera lockForConfiguration:&error]) {
// Provide linear feel to zoom slider
// CGFloat zoomFactor = pow([self maxZoomFactor], zoomValue);
self.activeCamera.videoZoomFactor = zoomValue;
[self.activeCamera unlockForConfiguration];
} else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
}
- (void)rampZoomToValue:(CGFloat)zoomValue {
// CGFloat zoomFactor = pow([self maxZoomFactor], zoomValue);
NSError *error;
if ([self.activeCamera lockForConfiguration:&error]) {
[self.activeCamera rampToVideoZoomFactor:zoomValue
withRate:HXZoomRate];
[self.activeCamera unlockForConfiguration];
} else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
- (void)cancelZoom {
NSError *error;
if ([self.activeCamera lockForConfiguration:&error]) {
[self.activeCamera cancelVideoZoomRamp];
[self.activeCamera unlockForConfiguration];
} else {
if ([self.delegate respondsToSelector:@selector(deviceConfigurationFailedWithError:)]) {
[self.delegate deviceConfigurationFailedWithError:error];
}
}
}
@end

View File

@ -0,0 +1,59 @@
//
// HXCustomCameraViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/9/30.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
typedef NS_ENUM(NSUInteger, HXCustomCameraBottomViewMode) {
HXCustomCameraBottomViewModePhoto, //!< 拍照
HXCustomCameraBottomViewModeVideo = 1, //!< 录制
};
@class HXPhotoManager,HXCustomCameraViewController,HXPhotoModel;
typedef void (^ HXCustomCameraViewControllerDidDoneBlock)(HXPhotoModel *model, HXCustomCameraViewController *viewController);
typedef void (^ HXCustomCameraViewControllerDidCancelBlock)(HXCustomCameraViewController *viewController);
@protocol HXCustomCameraViewControllerDelegate <NSObject>
@optional
/// 拍照/录制完成
/// @param viewController self
/// @param model 资源模型
- (void)customCameraViewController:(HXCustomCameraViewController *)viewController
didDone:(HXPhotoModel *)model;
/// 取消
/// @param viewController self
- (void)customCameraViewControllerDidCancel:(HXCustomCameraViewController *)viewController;
- (void)customCameraViewControllerFinishDismissCompletion:(HXCustomCameraViewController *)viewController;
- (void)customCameraViewControllerCancelDismissCompletion:(HXCustomCameraViewController *)viewController;
@end
@interface HXCustomCameraViewController : UIViewController
@property (weak, nonatomic) id<HXCustomCameraViewControllerDelegate> delegate;
@property (strong, nonatomic) HXPhotoManager *manager;
@property (assign, nonatomic) BOOL isOutside;
@property (copy, nonatomic) HXCustomCameraViewControllerDidDoneBlock doneBlock;
@property (copy, nonatomic) HXCustomCameraViewControllerDidCancelBlock cancelBlock;
#pragma mark - < other >
- (UIImage *)jumpImage;
- (CGRect)jumpRect;
- (void)hidePlayerView;
- (void)showPlayerView;
- (void)hiddenTopBottomView;
- (void)showTopBottomView;
@end
@interface HXCustomCameraPlayVideoView : UIView
@property (strong, nonatomic) NSURL *videoURL;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
- (void)stopPlay;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
//
// HXCustomNavigationController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/31.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoManager.h"
#import "HXPickerResult.h"
@class HXPhotoModel, HXCustomNavigationController;
@protocol HXCustomNavigationControllerDelegate <NSObject>
@optional
/**
@param photoNavigationViewController self
@param allList ()
@param photoList
@param videoList
@param original
*/
- (void)photoNavigationViewController:(HXCustomNavigationController *)photoNavigationViewController
didDoneAllList:(NSArray<HXPhotoModel *> *)allList
photos:(NSArray<HXPhotoModel *> *)photoList
videos:(NSArray<HXPhotoModel *> *)videoList
original:(BOOL)original;
- (void)photoNavigationViewController:(HXCustomNavigationController *)photoNavigationViewController
didDoneWithResult:(HXPickerResult *)result;
/**
@param photoNavigationViewController self
*/
- (void)photoNavigationViewControllerDidCancel:(HXCustomNavigationController *)photoNavigationViewController;
- (void)photoNavigationViewControllerFinishDismissCompletion:(HXCustomNavigationController *)photoNavigationViewController;
- (void)photoNavigationViewControllerCancelDismissCompletion:(HXCustomNavigationController *)photoNavigationViewController;
@end
@interface HXCustomNavigationController : UINavigationController
@property (strong, nonatomic) NSMutableArray *albums;
@property (strong, nonatomic) HXAlbumModel *cameraRollAlbumModel;
@property (copy, nonatomic) void (^requestCameraRollPhotoListCompletion)(void);
@property (copy, nonatomic) void (^requestCameraRollCompletion)(void);
@property (copy, nonatomic) void (^requestAllAlbumCompletion)(void);
@property (copy, nonatomic) void (^ reloadAsset)(BOOL initialAuthorization);
//@property (copy, nonatomic) void (^ photoLibraryDidChange)(HXAlbumModel *albumModel);
@property (assign ,nonatomic) BOOL isCamera;
@property (weak, nonatomic) id<HXCustomNavigationControllerDelegate> hx_delegate;
@property (assign, nonatomic) BOOL supportRotation;
@property (strong, nonatomic) HXPhotoManager *manager;
@property (copy, nonatomic) viewControllerDidDoneBlock doneBlock;
@property (copy, nonatomic) viewControllerDidCancelBlock cancelBlock;
- (instancetype)initWithManager:(HXPhotoManager *)manager;
- (instancetype)initWithManager:(HXPhotoManager *)manager
delegate:(id<HXCustomNavigationControllerDelegate>)delegate;
- (instancetype)initWithManager:(HXPhotoManager *)manager
doneBlock:(viewControllerDidDoneBlock)doneBlock
cancelBlock:(viewControllerDidCancelBlock)cancelBlock;
- (instancetype)initWithManager:(HXPhotoManager *)manager
delegate:(id<HXCustomNavigationControllerDelegate>)delegate
doneBlock:(viewControllerDidDoneBlock)doneBlock
cancelBlock:(viewControllerDidCancelBlock)cancelBlock;
- (void)clearAssetCache;
@end

View File

@ -0,0 +1,385 @@
//
// HXCustomNavigationController.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/31.
// Copyright © 2017 Silence. All rights reserved.
//
#import "HXCustomNavigationController.h"
#import "HXAlbumListViewController.h"
#import "HXPhotoViewController.h"
#import "HXPhotoTools.h"
#import "HXAssetManager.h"
@interface HXCustomNavigationController ()<HXAlbumListViewControllerDelegate, HXPhotoViewControllerDelegate>
@property (assign, nonatomic) BOOL didPresentImagePicker;
@property (assign, nonatomic) BOOL initialAuthorization;
@property (strong, nonatomic) NSTimer *timer;
@property (weak, nonatomic) UIImageView *imageView;
@property (assign, nonatomic) PHImageRequestID requestID;
@end
@implementation HXCustomNavigationController
- (instancetype)initWithManager:(HXPhotoManager *)manager {
return [self initWithManager:manager delegate:nil doneBlock:nil cancelBlock:nil];
}
- (instancetype)initWithManager:(HXPhotoManager *)manager
delegate:(id<HXCustomNavigationControllerDelegate>)delegate {
return [self initWithManager:manager delegate:delegate doneBlock:nil cancelBlock:nil];
}
- (instancetype)initWithManager:(HXPhotoManager *)manager
doneBlock:(viewControllerDidDoneBlock)doneBlock
cancelBlock:(viewControllerDidCancelBlock)cancelBlock {
return [self initWithManager:manager delegate:nil doneBlock:doneBlock cancelBlock:cancelBlock];
}
- (instancetype)initWithManager:(HXPhotoManager *)manager
delegate:(id<HXCustomNavigationControllerDelegate>)delegate
doneBlock:(viewControllerDidDoneBlock)doneBlock
cancelBlock:(viewControllerDidCancelBlock)cancelBlock {
[manager selectedListTransformBefore];
manager.selectPhotoing = YES;
if (manager.configuration.albumShowMode == HXPhotoAlbumShowModeDefault) {
HXAlbumListViewController *vc = [[HXAlbumListViewController alloc] initWithManager:manager];
self = [super initWithRootViewController:vc];
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
self.modalPresentationCapturesStatusBarAppearance = YES;
if (self) {
self.hx_delegate = delegate;
self.manager = manager;
[self requestAuthorization];
self.doneBlock = doneBlock;
self.cancelBlock = cancelBlock;
vc.doneBlock = self.doneBlock;
vc.cancelBlock = self.cancelBlock;
vc.delegate = self;
}
}else if (manager.configuration.albumShowMode == HXPhotoAlbumShowModePopup) {
HXPhotoViewController *vc = [[HXPhotoViewController alloc] init];
vc.manager = manager;
self = [super initWithRootViewController:vc];
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
self.modalPresentationCapturesStatusBarAppearance = YES;
if (self) {
self.hx_delegate = delegate;
self.manager = manager;
[self requestAuthorization];
self.doneBlock = doneBlock;
self.cancelBlock = cancelBlock;
vc.doneBlock = self.doneBlock;
vc.cancelBlock = self.cancelBlock;
vc.delegate = self;
}
}
return self;
}
- (void)requestAuthorization {
self.initialAuthorization = NO;
HXWeakSelf
#ifdef __IPHONE_14_0
if (@available(iOS 14, *)) {
[HXPhotoCommon photoCommon].photoLibraryDidChange = ^{
if (!weakSelf.initialAuthorization) {
if (weakSelf.timer) {
[weakSelf.timer invalidate];
weakSelf.timer = nil;
}
[weakSelf imagePickerDidFinish];
}
};
}
#endif
PHAuthorizationStatus status = [HXPhotoTools authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
[self requestModel];
return;
}
#ifdef __IPHONE_14_0
else if (@available(iOS 14, *)) {
if (status == PHAuthorizationStatusLimited) {
[self requestModel];
return;
}
}
#endif
if (status == PHAuthorizationStatusNotDetermined) {
self.initialAuthorization = YES;
}
[HXPhotoTools requestAuthorization:nil handler:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
[self requestModel];
if (self.reloadAsset) {
self.reloadAsset(self.initialAuthorization);
}
}
#ifdef __IPHONE_14_0
else if (@available(iOS 14, *)) {
if (status == PHAuthorizationStatusLimited) {
self.didPresentImagePicker = YES;
}
#endif
else if (status == PHAuthorizationStatusRestricted ||
status == PHAuthorizationStatusDenied) {
if (self.reloadAsset) {
self.reloadAsset(weakSelf.initialAuthorization);
}
}
#ifdef __IPHONE_14_0
}else if (status == PHAuthorizationStatusRestricted ||
status == PHAuthorizationStatusDenied) {
if (self.reloadAsset) {
self.reloadAsset(self.initialAuthorization);
}
}
#endif
}];
}
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
if (!self.initialAuthorization) {
[super presentViewController:viewControllerToPresent animated:flag completion:completion];
return;
}
#ifdef __IPHONE_14_0
if (@available(iOS 14, *)) {
if ([viewControllerToPresent isKindOfClass:[UIImagePickerController class]]) {
UIImagePickerController *imagePickerController = (UIImagePickerController *)viewControllerToPresent;
if (imagePickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
HXWeakSelf
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
if ([weakSelf.presentedViewController isKindOfClass:[UIImagePickerController class]]) {
weakSelf.didPresentImagePicker = YES;
}else {
if (weakSelf.didPresentImagePicker) {
weakSelf.didPresentImagePicker = NO;
[timer invalidate];
weakSelf.timer = nil;
[weakSelf imagePickerDidFinish];
}
}
}];
}
}
}
#endif
[super presentViewController:viewControllerToPresent animated:flag completion:completion];
}
- (void)imagePickerDidFinish {
[HXPhotoCommon photoCommon].cameraRollLocalIdentifier = nil;
[HXPhotoCommon photoCommon].cameraRollResult = nil;
self.cameraRollAlbumModel = nil;
self.albums = nil;
[self requestModel];
if (self.reloadAsset) {
self.reloadAsset(self.initialAuthorization);
}
if (self.initialAuthorization) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"HXPhotoRequestAuthorizationCompletion" object:nil];
self.initialAuthorization = NO;
}
}
- (void)requestModel {
[self.view hx_showLoadingHUDText:nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
HXWeakSelf
[self.manager getCameraRollAlbumCompletion:^(HXAlbumModel *albumModel) {
weakSelf.cameraRollAlbumModel = albumModel;
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.requestCameraRollCompletion) {
weakSelf.requestCameraRollCompletion();
}
});
}];
[self.manager getAllAlbumModelWithCompletion:^(NSMutableArray<HXAlbumModel *> *albums) {
weakSelf.albums = albums.mutableCopy;
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.requestAllAlbumCompletion) {
weakSelf.requestAllAlbumCompletion();
}
});
}];
});
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.manager.viewWillAppear) {
self.manager.viewWillAppear(self);
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.manager.viewDidAppear) {
self.manager.viewDidAppear(self);
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (_timer) {
self.didPresentImagePicker = NO;
[self.timer invalidate];
self.timer = nil;
}
if (self.manager.viewWillDisappear) {
self.manager.viewWillDisappear(self);
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
if (self.manager.viewDidDisappear) {
self.manager.viewDidDisappear(self);
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if ([HXPhotoCommon photoCommon].clearAssetRequestID) {
[[PHImageManager defaultManager] cancelImageRequest:[HXPhotoCommon photoCommon].clearAssetRequestID];
[HXPhotoCommon photoCommon].clearAssetRequestID = -1;
}
}
#pragma mark - < HXAlbumListViewControllerDelegate >
- (void)albumListViewControllerCancelDismissCompletion:(HXAlbumListViewController *)albumListViewController {
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewControllerCancelDismissCompletion:)]) {
[self.hx_delegate photoNavigationViewControllerCancelDismissCompletion:self];
}
}
- (void)albumListViewControllerDidCancel:(HXAlbumListViewController *)albumListViewController {
[self clearAssetCacheWithAddOnWindow:!self.manager.selectPhotoCancelDismissAnimated];
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewControllerDidCancel:)]) {
[self.hx_delegate photoNavigationViewControllerDidCancel:self];
}
}
- (void)albumListViewController:(HXAlbumListViewController *)albumListViewController didDoneAllList:(NSArray<HXPhotoModel *> *)allList photos:(NSArray<HXPhotoModel *> *)photoList videos:(NSArray<HXPhotoModel *> *)videoList original:(BOOL)original {
if (!self.manager.configuration.requestImageAfterFinishingSelection) {
[self clearAssetCacheWithAddOnWindow:!self.manager.selectPhotoFinishDismissAnimated];
}
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewController:didDoneAllList:photos:videos:original:)]) {
[self.hx_delegate photoNavigationViewController:self didDoneAllList:allList photos:photoList videos:videoList original:original];
}
}
- (void)albumListViewController:(HXAlbumListViewController *)albumListViewController didDoneWithResult:(HXPickerResult *)result {
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewController:didDoneWithResult:)]) {
[self.hx_delegate photoNavigationViewController:self didDoneWithResult:result];
}
}
#pragma mark - < HXPhotoViewControllerDelegate >
- (void)photoViewControllerFinishDismissCompletion:(HXPhotoViewController *)photoViewController {
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewControllerFinishDismissCompletion:)]) {
[self.hx_delegate photoNavigationViewControllerFinishDismissCompletion:self];
}
}
- (void)photoViewController:(HXPhotoViewController *)photoViewController didDoneWithResult:(HXPickerResult *)result {
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewController:didDoneWithResult:)]) {
[self.hx_delegate photoNavigationViewController:self didDoneWithResult:result];
}
}
- (void)photoViewControllerCancelDismissCompletion:(HXPhotoViewController *)photoViewController {
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewControllerCancelDismissCompletion:)]) {
[self.hx_delegate photoNavigationViewControllerCancelDismissCompletion:self];
}
}
- (void)photoViewControllerDidCancel:(HXPhotoViewController *)photoViewController {
[self clearAssetCacheWithAddOnWindow:!self.manager.selectPhotoCancelDismissAnimated];
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewControllerDidCancel:)]) {
[self.hx_delegate photoNavigationViewControllerDidCancel:self];
}
}
- (void)photoViewController:(HXPhotoViewController *)photoViewController didDoneAllList:(NSArray<HXPhotoModel *> *)allList photos:(NSArray<HXPhotoModel *> *)photoList videos:(NSArray<HXPhotoModel *> *)videoList original:(BOOL)original {
if (!self.manager.configuration.requestImageAfterFinishingSelection) {
[self clearAssetCacheWithAddOnWindow:!self.manager.selectPhotoFinishDismissAnimated];
}
if ([self.hx_delegate respondsToSelector:@selector(photoNavigationViewController:didDoneAllList:photos:videos:original:)]) {
[self.hx_delegate photoNavigationViewController:self didDoneAllList:allList photos:photoList videos:videoList original:original];
}
}
- (BOOL)shouldAutorotate{
if (self.isCamera) {
return NO;
}
if (self.manager.configuration.supportRotation) {
return YES;
}else {
return NO;
}
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return [self.topViewController preferredStatusBarStyle];
}
- (BOOL)prefersStatusBarHidden {
return [self.topViewController prefersStatusBarHidden];
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationFade;
}
//
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
if (self.isCamera) {
return UIInterfaceOrientationMaskPortrait;
}
if (self.manager.configuration.supportRotation) {
return UIInterfaceOrientationMaskAll;
}else {
return UIInterfaceOrientationMaskPortrait;
}
}
- (void)clearAssetCache {
[self clearAssetCacheWithAddOnWindow:NO];
}
- (void)clearAssetCacheWithAddOnWindow:(BOOL)addOnWindow {
PHAsset *asset = self.cameraRollAlbumModel.assetResult.firstObject;
if (asset) {
[[PHImageManager defaultManager] cancelImageRequest:self.requestID];
[HXPhotoCommon photoCommon].clearAssetRequestID = -1;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
options.resizeMode = PHImageRequestOptionsResizeModeFast;
options.synchronous = NO;
options.networkAccessAllowed = NO;
HXWeakSelf
self.requestID = [HXAssetManager requestImageDataForAsset:asset options:options completion:^(NSData * _Nonnull imageData, UIImageOrientation orientation, NSDictionary<NSString *,id> * _Nonnull info) {
[HXPhotoCommon photoCommon].clearAssetRequestID = -1;
if (imageData) {
if (addOnWindow || !weakSelf) {
[HXCustomNavigationController addImageViewOnWindowWithImageData:imageData];
}else {
[weakSelf addImageViewWithImageData:imageData addOnWindow:addOnWindow];
}
}
}];
[HXPhotoCommon photoCommon].clearAssetRequestID = self.requestID;
}
}
+ (void)addImageViewOnWindowWithImageData:(NSData *)imageData {
UIImage *image = [UIImage imageWithData:imageData];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.alpha = 0;
imageView.userInteractionEnabled = NO;
imageView.frame = [UIScreen mainScreen].bounds;
[[UIApplication sharedApplication].keyWindow addSubview:imageView];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[imageView removeFromSuperview];
});
}
- (void)addImageViewWithImageData:(NSData *)imageData addOnWindow:(BOOL)addOnWindow {
[self.imageView removeFromSuperview];
UIImage *image = [UIImage imageWithData:imageData];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.alpha = 0;
imageView.userInteractionEnabled = NO;
imageView.frame = self.view.bounds;
[self.view addSubview:imageView];
self.imageView = imageView;
}
- (void)dealloc {
if (_manager) {
self.manager.selectPhotoing = NO;
}
if (_timer) {
[_timer invalidate];
_timer = nil;
}
if (HXShowLog) NSSLog(@"%@ dealloc", self);
}
@end

View File

@ -0,0 +1,49 @@
//
// HXPhoto3DTouchViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/9/25.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoTools.h"
#if __has_include(<SDWebImage/UIImageView+WebCache.h>)
#import <SDWebImage/UIImageView+WebCache.h>
#import <SDWebImage/SDAnimatedImageView.h>
#import <SDWebImage/SDAnimatedImageView+WebCache.h>
#import <SDWebImage/UIImage+GIF.h>
#elif __has_include("UIImageView+WebCache.h")
#import "UIImageView+WebCache.h"
#import "UIImage+GIF.h"
#import "SDAnimatedImageView.h"
#import "SDAnimatedImageView+WebCache.h"
#endif
#if __has_include(<YYWebImage/YYWebImage.h>)
#import <YYWebImage/YYWebImage.h>
#elif __has_include("YYWebImage.h")
#import "YYWebImage.h"
#elif __has_include(<YYKit/YYKit.h>)
#import <YYKit/YYKit.h>
#elif __has_include("YYKit.h")
#import "YYKit.h"
#endif
@interface HXPhoto3DTouchViewController : UIViewController
@property (strong, nonatomic) HXPhotoModel *model;
@property (strong, nonatomic) UIImage *image;
@property (strong, nonatomic) UIImageView *imageView;
#if HasSDWebImage
@property (strong, nonatomic) SDAnimatedImageView *sdImageView;
#elif HasYYKitOrWebImage
@property (strong, nonatomic) YYAnimatedImageView *animatedImageView;
#endif
@property (strong, nonatomic) NSIndexPath *indexPath;
@property (copy, nonatomic) NSArray<id<UIPreviewActionItem>> *(^ previewActionItemsBlock)(void);
@property (copy, nonatomic) void (^downloadImageComplete)(HXPhoto3DTouchViewController *vc, HXPhotoModel *model);
@end

View File

@ -0,0 +1,399 @@
//
// HXPhoto3DTouchViewController.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/9/25.
// Copyright © 2017 Silence. All rights reserved.
//
#import "HXPhoto3DTouchViewController.h"
#import <PhotosUI/PhotosUI.h>
#import "UIImage+HXExtension.h"
#import "HXCircleProgressView.h"
#import "UIImageView+HXExtension.h"
@interface HXPhoto3DTouchViewController ()<PHLivePhotoViewDelegate>
@property (strong, nonatomic) PHLivePhotoView *livePhotoView;
@property (strong, nonatomic) AVPlayer *player;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
@property (strong, nonatomic) HXCircleProgressView *progressView;
@property (strong, nonatomic) UIActivityIndicatorView *loadingView;
@property (assign, nonatomic) PHImageRequestID requestId;
@end
@implementation HXPhoto3DTouchViewController
- (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
NSArray *items = @[];
if (self.previewActionItemsBlock) items = self.previewActionItemsBlock();
return items;
}
- (void)viewDidLoad {
[super viewDidLoad];
#if HasSDWebImage
self.sdImageView.hx_size = self.model.previewViewSize;
self.sdImageView.image = self.image;
[self.view addSubview:self.sdImageView];
self.progressView.center = CGPointMake(self.sdImageView.hx_size.width / 2, self.sdImageView.hx_size.height / 2);
#elif HasYYKitOrWebImage
self.animatedImageView.hx_size = self.model.previewViewSize;
self.animatedImageView.image = self.image;
[self.view addSubview:self.animatedImageView];
self.progressView.center = CGPointMake(self.animatedImageView.hx_size.width / 2, self.animatedImageView.hx_size.height / 2);
#else
self.imageView.hx_size = self.model.previewViewSize;
self.imageView.image = self.image;
[self.view addSubview:self.imageView];
self.progressView.center = CGPointMake(self.imageView.hx_size.width / 2, self.imageView.hx_size.height / 2);
#endif
[self.view addSubview:self.progressView];
[self.view addSubview:self.loadingView];
self.loadingView.center = self.progressView.center;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
switch (self.model.type) {
case HXPhotoModelMediaTypeVideo:
[self loadVideo];
break;
case HXPhotoModelMediaTypeCameraVideo:
[self loadVideo];
break;
case HXPhotoModelMediaTypePhotoGif:
[self loadGifPhoto];
break;
case HXPhotoModelMediaTypeLivePhoto:
[self loadLivePhoto];
break;
default:
[self loadPhoto];
break;
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.requestId) {
[[PHImageManager defaultManager] cancelImageRequest:self.requestId];
}
if (_livePhotoView) {
self.livePhotoView.delegate = nil;
[self.livePhotoView stopPlayback];
[self.livePhotoView removeFromSuperview];
self.livePhotoView.livePhoto = nil;
self.livePhotoView = nil;
}
if (_progressView) {
[self.progressView removeFromSuperview];
}
if (_loadingView) {
[self.loadingView stopAnimating];
}
#if HasSDWebImage
if (_sdImageView) {
[self.view addSubview:self.sdImageView];
}
#elif HasYYKitOrWebImage
if (_animatedImageView) {
[self.view addSubview:self.animatedImageView];
}
#else
if (_imageView) {
[self.view addSubview:self.imageView];
}
#endif
if (_player) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
[self.player pause];
[self.player seekToTime:kCMTimeZero];
self.player = nil;
}
if (_playerLayer) {
self.playerLayer.player = nil;
[self.playerLayer removeFromSuperlayer];
}
}
- (void)loadPhoto {
HXWeakSelf
if (self.model.type == HXPhotoModelMediaTypeCameraPhoto) {
if (self.model.networkPhotoUrl) {
self.progressView.hidden = self.model.downloadComplete;
CGFloat progress = (CGFloat)self.model.receivedSize / self.model.expectedSize;
self.progressView.progress = progress;
#if HasSDWebImage
[self.sdImageView sd_setImageWithURL:self.model.networkPhotoUrl placeholderImage:self.model.thumbPhoto options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
weakSelf.model.receivedSize = receivedSize;
weakSelf.model.expectedSize = expectedSize;
CGFloat progress = (CGFloat)receivedSize / expectedSize;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.progressView.progress = progress;
});
} completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
if (error != nil) {
weakSelf.model.downloadError = YES;
weakSelf.model.downloadComplete = YES;
[weakSelf.progressView showError];
}else {
if (image) {
if (weakSelf.downloadImageComplete) {
weakSelf.downloadImageComplete(weakSelf, weakSelf.model);
}
weakSelf.model.imageSize = image.size;
weakSelf.model.thumbPhoto = image;
weakSelf.model.previewPhoto = image;
weakSelf.model.downloadComplete = YES;
weakSelf.model.downloadError = NO;
weakSelf.model.imageSize = image.size;
weakSelf.progressView.progress = 1;
weakSelf.progressView.hidden = YES;
weakSelf.sdImageView.image = image;
}
}
}];
#elif HasYYKitOrWebImage
[self.animatedImageView hx_setImageWithModel:self.model progress:^(CGFloat progress, HXPhotoModel *model) {
if (weakSelf.model == model) {
weakSelf.progressView.progress = progress;
}
} completed:^(UIImage *image, NSError *error, HXPhotoModel *model) {
if (weakSelf.model == model) {
if (error != nil) {
[weakSelf.progressView showError];
}else {
if (image) {
if (weakSelf.downloadImageComplete) {
weakSelf.downloadImageComplete(weakSelf, weakSelf.model);
}
weakSelf.progressView.progress = 1;
weakSelf.progressView.hidden = YES;
weakSelf.animatedImageView.image = image;
}
}
}
}];
#else
[self.imageView hx_setImageWithModel:self.model progress:^(CGFloat progress, HXPhotoModel *model) {
if (weakSelf.model == model) {
weakSelf.progressView.progress = progress;
}
} completed:^(UIImage *image, NSError *error, HXPhotoModel *model) {
if (weakSelf.model == model) {
if (error != nil) {
[weakSelf.progressView showError];
}else {
if (image) {
if (weakSelf.downloadImageComplete) {
weakSelf.downloadImageComplete(weakSelf, weakSelf.model);
}
weakSelf.progressView.progress = 1;
weakSelf.progressView.hidden = YES;
weakSelf.imageView.image = image;
}
}
}
}];
#endif
}else {
#if HasSDWebImage
self.sdImageView.image = self.model.thumbPhoto;
#elif HasYYKitOrWebImage
self.animatedImageView.image = self.model.thumbPhoto;
#else
self.imageView.image = self.model.thumbPhoto;
#endif
}
return;
}
[self loadImageDataWithCompletion:^(NSData *imageData) {
UIImage *image = [UIImage imageWithData:imageData];
#if HasSDWebImage
weakSelf.sdImageView.image = image;
#elif HasYYKitOrWebImage
weakSelf.animatedImageView.image = image;
#else
weakSelf.imageView.image = image;
#endif
}];
}
- (void)loadImageDataWithCompletion:(void (^)(NSData *imageData))completion {
HXWeakSelf
self.requestId = [self.model requestImageDataStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
weakSelf.requestId = iCloudRequestId;
if (weakSelf.model.isICloud) {
weakSelf.progressView.hidden = NO;
}
} progressHandler:^(double progress, HXPhotoModel *model) {
if (weakSelf.model.isICloud) {
weakSelf.progressView.hidden = NO;
}
weakSelf.progressView.progress = progress;
} success:^(NSData *imageData, UIImageOrientation orientation, HXPhotoModel *model, NSDictionary *info) {
weakSelf.progressView.hidden = YES;
if (completion) {
completion(imageData);
}
} failed:^(NSDictionary *info, HXPhotoModel *model) {
// [weakSelf.progressView showError];
}];
}
- (void)loadGifPhoto {
HXWeakSelf
[self loadImageDataWithCompletion:^(NSData *imageData) {
#if HasSDWebImage
UIImage *gifImage = [UIImage sd_imageWithGIFData:imageData];
if (gifImage.images.count > 0) {
weakSelf.sdImageView.image = nil;
weakSelf.sdImageView.image = gifImage;
}
#elif HasYYKitOrWebImage
YYImage *gifImage = [YYImage imageWithData:imageData];
weakSelf.animatedImageView.image = nil;
weakSelf.animatedImageView.image = gifImage;
#else
UIImage *gifImage = [UIImage hx_animatedGIFWithData:imageData];
if (gifImage.images.count > 0) {
weakSelf.imageView.image = nil;
weakSelf.imageView.image = gifImage;
}
#endif
}];
}
- (void)loadLivePhoto {
self.livePhotoView = [[PHLivePhotoView alloc] initWithFrame:CGRectMake(0, 0, self.model.previewViewSize.width, self.model.previewViewSize.height)];
self.livePhotoView.delegate = self;
self.livePhotoView.clipsToBounds = YES;
self.livePhotoView.hidden = YES;
self.livePhotoView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:self.livePhotoView];
HXWeakSelf
self.requestId = [self.model requestLivePhotoWithSize:CGSizeMake(self.model.previewViewSize.width * 1.5, self.model.previewViewSize.height * 1.5) startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
weakSelf.requestId = iCloudRequestId;
if (weakSelf.model.isICloud) {
weakSelf.progressView.hidden = NO;
}
} progressHandler:^(double progress, HXPhotoModel *model) {
if (weakSelf.model.isICloud) {
weakSelf.progressView.hidden = NO;
}
weakSelf.progressView.progress = progress;
} success:^(PHLivePhoto *livePhoto, HXPhotoModel *model, NSDictionary *info) {
weakSelf.progressView.hidden = YES;
weakSelf.livePhotoView.hidden = NO;
weakSelf.livePhotoView.livePhoto = livePhoto;
[weakSelf.livePhotoView startPlaybackWithStyle:PHLivePhotoViewPlaybackStyleFull];
#if HasSDWebImage
[weakSelf.sdImageView removeFromSuperview];
#elif HasYYKitOrWebImage
[weakSelf.animatedImageView removeFromSuperview];
#else
[weakSelf.imageView removeFromSuperview];
#endif
} failed:^(NSDictionary *info, HXPhotoModel *model) {
}];
}
- (void)livePhotoView:(PHLivePhotoView *)livePhotoView didEndPlaybackWithStyle:(PHLivePhotoViewPlaybackStyle)playbackStyle {
[self.livePhotoView startPlaybackWithStyle:PHLivePhotoViewPlaybackStyleFull];
}
- (void)loadVideo {
HXWeakSelf
self.requestId = [self.model requestAVAssetStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
weakSelf.requestId = iCloudRequestId;
[weakSelf.loadingView startAnimating];
} progressHandler:^(double progress, HXPhotoModel *model) {
if (weakSelf.model.isICloud) {
weakSelf.progressView.hidden = NO;
}
weakSelf.progressView.progress = progress;
} success:^(AVAsset *avAsset, AVAudioMix *audioMix, HXPhotoModel *model, NSDictionary *info) {
weakSelf.progressView.hidden = YES;
weakSelf.player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:avAsset]];
[weakSelf playVideo];
[weakSelf.loadingView stopAnimating];
[[NSNotificationCenter defaultCenter] addObserver:weakSelf selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:weakSelf.player.currentItem];
} failed:^(NSDictionary *info, HXPhotoModel *model) {
[weakSelf.loadingView stopAnimating];
}];
}
- (void)pausePlayerAndShowNaviBar {
[self.player.currentItem seekToTime:CMTimeMake(0, 1)];
[self.player play];
}
- (void)playVideo {
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.playerLayer.frame = CGRectMake(0, 0, self.model.previewViewSize.width, self.model.previewViewSize.height);
[self.view.layer insertSublayer:self.playerLayer atIndex:0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.player play];
#if HasSDWebImage
[self.sdImageView removeFromSuperview];
#elif HasYYKitOrWebImage
[self.animatedImageView removeFromSuperview];
#else
[self.imageView removeFromSuperview];
#endif
});
}
#if HasSDWebImage
- (SDAnimatedImageView *)sdImageView {
if (!_sdImageView) {
_sdImageView = [[SDAnimatedImageView alloc] init];
_sdImageView.clipsToBounds = YES;
_sdImageView.contentMode = UIViewContentModeScaleAspectFill;
_sdImageView.hx_x = 0;
_sdImageView.hx_y = 0;
}
return _sdImageView;
}
#elif HasYYKitOrWebImage
- (YYAnimatedImageView *)animatedImageView {
if (!_animatedImageView) {
_animatedImageView = [[YYAnimatedImageView alloc] init];
_animatedImageView.clipsToBounds = YES;
_animatedImageView.contentMode = UIViewContentModeScaleAspectFill;
_animatedImageView.hx_x = 0;
_animatedImageView.hx_y = 0;
}
return _animatedImageView;
}
#endif
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [[UIImageView alloc] init];
_imageView.clipsToBounds = YES;
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.hx_x = 0;
_imageView.hx_y = 0;
}
return _imageView;
}
- (HXCircleProgressView *)progressView {
if (!_progressView) {
_progressView = [[HXCircleProgressView alloc] init];
_progressView.hidden = YES;
}
return _progressView;
}
- (UIActivityIndicatorView *)loadingView {
if (!_loadingView) {
_loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
}
return _loadingView;
}
//- (PHLivePhotoView *)livePhotoView {
// if (!_livePhotoView) {
// _livePhotoView = [[PHLivePhotoView alloc] init];
// _livePhotoView.clipsToBounds = YES;
// _livePhotoView.contentMode = UIViewContentModeScaleAspectFill;
// }
// return _livePhotoView;
//}
@end

View File

@ -0,0 +1,85 @@
//
// HXPhotoEditViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/27.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoManager.h"
@class HXPhotoEditViewController;
typedef void (^ HXPhotoEditViewControllerDidDoneBlock)(HXPhotoModel *beforeModel, HXPhotoModel *afterModel, HXPhotoEditViewController *viewController);
typedef void (^ HXPhotoEditViewControllerDidCancelBlock)(HXPhotoEditViewController *viewController);
@protocol HXPhotoEditViewControllerDelegate <NSObject>
@optional
/// 编辑完成
/// @param photoEditViewController 照片编辑控制器
/// @param beforeModel 编辑之前的模型
/// @param afterModel 编辑之后的模型
- (void)photoEditViewControllerDidClipClick:(HXPhotoEditViewController *)photoEditViewController beforeModel:(HXPhotoModel *)beforeModel afterModel:(HXPhotoModel *)afterModel;
/// 取消编辑
/// @param photoEditViewController 照片编辑控制器
- (void)photoEditViewControllerDidCancel:(HXPhotoEditViewController *)photoEditViewController;
@end
@interface HXPhotoEditViewController : UIViewController<UIViewControllerTransitioningDelegate>
@property (weak, nonatomic) id<HXPhotoEditViewControllerDelegate> delegate;
/// 需要编辑的照片模型
@property (strong, nonatomic) HXPhotoModel *model;
/// 照片管理类
@property (strong, nonatomic) HXPhotoManager *manager;
@property (copy, nonatomic) HXPhotoEditViewControllerDidDoneBlock doneBlock;
@property (copy, nonatomic) HXPhotoEditViewControllerDidCancelBlock cancelBlock;
@property (assign, nonatomic) BOOL outside;
@property (assign, nonatomic) BOOL isInside;
@property (assign, nonatomic) BOOL imageRequestComplete;
@property (assign, nonatomic) BOOL transitionCompletion;
@property (assign, nonatomic) BOOL isCancel;
@property (strong, nonatomic, readonly) UIImage *originalImage;
- (void)completeTransition:(UIImage *)image;
- (void)showBottomView;
- (void)hideImageView;
- (UIImage *)getCurrentImage;
- (CGRect)getImageFrame;
@end
@class HXEditRatio;
@protocol HXPhotoEditBottomViewDelegate <NSObject>
@optional
- (void)bottomViewDidCancelClick;
- (void)bottomViewDidRestoreClick;
- (void)bottomViewDidRotateClick;
- (void)bottomViewDidClipClick;
- (void)bottomViewDidSelectRatioClick:(HXEditRatio *)ratio;
@end
@interface HXPhotoEditBottomView : UIView
@property (weak, nonatomic) id<HXPhotoEditBottomViewDelegate> delegate;
@property (assign, nonatomic) BOOL enabled;
- (instancetype)initWithManager:(HXPhotoManager *)manager;
@end
@interface HXEditGridLayer : UIView
@property (nonatomic, assign) CGRect clippingRect;
@property (nonatomic, strong) UIColor *bgColor;
@property (nonatomic, strong) UIColor *gridColor;
@end
@interface HXEditCornerView : UIView
@property (nonatomic, strong) UIColor *bgColor;
@end
@interface HXEditRatio : NSObject
@property (nonatomic, assign) BOOL isLandscape;
@property (nonatomic, readonly) CGFloat ratio;
@property (nonatomic, strong) NSString *titleFormat;
- (id)initWithValue1:(CGFloat)value1 value2:(CGFloat)value2;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
//
// HXPhotoPreviewViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <PhotosUI/PhotosUI.h>
#import "HXPhotoManager.h"
#import "HXPhotoView.h"
#import "HXPhotoPreviewImageViewCell.h"
#import "HXPhotoPreviewVideoViewCell.h"
#import "HXPhotoPreviewLivePhotoCell.h"
@class
HXPhotoPreviewViewController,
HXPhotoPreviewBottomView,
HXPhotoPreviewViewCell;
@protocol HXPhotoPreviewViewControllerDelegate <NSObject>
@optional
/// 选择某个model
/// 根据 model.selected 来判断是否选中
/// @param previewController 照片预览控制器
/// @param model 当前选择的模型
- (void)photoPreviewControllerDidSelect:(HXPhotoPreviewViewController *)previewController
model:(HXPhotoModel *)model;
/// 点击
/// @param previewController 照片预览控制器
- (void)photoPreviewControllerDidDone:(HXPhotoPreviewViewController *)previewController;
/// 预览界面编辑完成之后的回调
/// @param previewController 照片预览控制器
/// @param model 编辑之后的模型
/// @param beforeModel 编辑之前的模型
- (void)photoPreviewDidEditClick:(HXPhotoPreviewViewController *)previewController
model:(HXPhotoModel *)model
beforeModel:(HXPhotoModel *)beforeModel;
/// 单选模式下选择了某个model
/// @param previewController 照片预览控制器
/// @param model 当前选择的model
- (void)photoPreviewSingleSelectedClick:(HXPhotoPreviewViewController *)previewController
model:(HXPhotoModel *)model;
/// 预览界面加载iCloud上的照片完成后的回调
/// @param previewController 照片预览控制器
/// @param model 当前model
- (void)photoPreviewDownLoadICloudAssetComplete:(HXPhotoPreviewViewController *)previewController
model:(HXPhotoModel *)model;
/// 在HXPhotoView上预览时编辑model完成之后的回调
/// @param previewController 照片预览控制器
/// @param beforeModel 编辑之前的model
/// @param afterModel 编辑之后的model
- (void)photoPreviewSelectLaterDidEditClick:(HXPhotoPreviewViewController *)previewController
beforeModel:(HXPhotoModel *)beforeModel
afterModel:(HXPhotoModel *)afterModel;
/// 在HXPhotoView上预览时删除model的回调
/// @param previewController 照片预览控制器
/// @param model 被删除的model
/// @param index model下标
- (void)photoPreviewDidDeleteClick:(HXPhotoPreviewViewController *)previewController
deleteModel:(HXPhotoModel *)model
deleteIndex:(NSInteger)index;
/// 预览时网络图片下载完成的回调,用于刷新前一个界面的展示
/// @param previewController 照片预览控制器
/// @param model 当前预览的model
- (void)photoPreviewCellDownloadImageComplete:(HXPhotoPreviewViewController *)previewController
model:(HXPhotoModel *)model;
/// 取消预览
/// @param previewController self
/// @param model 取消时展示的model
- (void)photoPreviewControllerDidCancel:(HXPhotoPreviewViewController *)previewController
model:(HXPhotoModel *)model;
- (void)photoPreviewControllerFinishDismissCompletion:(HXPhotoPreviewViewController *)previewController;
- (void)photoPreviewControllerCancelDismissCompletion:(HXPhotoPreviewViewController *)previewController;
@end
/// 单独使用 HXPhotoPreviewViewController 来预览图片
/// 请使用 <UIViewController+HXExtension> 中的方法
@interface HXPhotoPreviewViewController : UIViewController<UIViewControllerTransitioningDelegate,UINavigationControllerDelegate>
@property (weak, nonatomic) id<HXPhotoPreviewViewControllerDelegate> delegate;
@property (strong, nonatomic) HXPhotoManager *manager;
@property (strong, nonatomic) NSMutableArray *modelArray;
@property (assign, nonatomic) NSInteger currentModelIndex;
@property (assign, nonatomic) BOOL outside;
@property (assign, nonatomic) BOOL selectPreview;
@property (strong, nonatomic) UICollectionView *collectionView;
@property (strong, nonatomic) HXPhotoPreviewBottomView *bottomView;
@property (strong, nonatomic) HXPhotoView *photoView;
/// 停止取消
@property (assign, nonatomic) BOOL stopCancel;
/// 预览时显示删除按钮
@property (assign, nonatomic) BOOL previewShowDeleteButton;
/// 预览大图时是否禁用手势返回
@property (assign, nonatomic) BOOL disableaPersentInteractiveTransition;
/// 使用HXPhotoView预览大图时的风格样式
@property (assign, nonatomic) HXPhotoViewPreViewShowStyle exteriorPreviewStyle;
/// 预览时是否显示底部pageControl
@property (assign, nonatomic) BOOL showBottomPageControl;
/// 处理ios8 导航栏转场动画崩溃问题
@property (strong, nonatomic) UIViewController *photoViewController;
- (HXPhotoPreviewViewCell *)currentPreviewCell:(HXPhotoModel *)model;
- (HXPhotoPreviewViewCell *)currentPreviewCell;
- (void)changeStatusBarWithHidden:(BOOL)hidden;
- (void)setSubviewAlphaAnimate:(BOOL)animete duration:(NSTimeInterval)duration;
- (void)setupDarkBtnAlpha:(CGFloat)alpha;
- (void)setCellImage:(UIImage *)image;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
//
// HXPhotoViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/10/14.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoManager.h"
#import "HXCustomCollectionReusableView.h"
#import "HXPhotoLimitView.h"
#import "HXPickerResult.h"
@class
HXPhotoViewController ,
HXPhotoViewCell ,
HXPhotoBottomView ,
HXCustomPreviewView ,
HXAlbumListViewController,
HXCustomCameraController;
@protocol HXPhotoViewControllerDelegate <NSObject>
@optional
/**
@param photoViewController self
*/
- (void)photoViewControllerDidCancel:(HXPhotoViewController *)photoViewController;
/**
@param photoViewController self
@param allList ()
@param photoList
@param videoList
@param original
*/
- (void)photoViewController:(HXPhotoViewController *)photoViewController
didDoneAllList:(NSArray<HXPhotoModel *> *)allList
photos:(NSArray<HXPhotoModel *> *)photoList
videos:(NSArray<HXPhotoModel *> *)videoList
original:(BOOL)original;
- (void)photoViewController:(HXPhotoViewController *)photoViewController
didDoneWithResult:(HXPickerResult *)result;
/**
@param model
@param selected
*/
- (void)photoViewControllerDidChangeSelect:(HXPhotoModel *)model
selected:(BOOL)selected;
- (void)photoViewControllerFinishDismissCompletion:(HXPhotoViewController *)photoViewController;
- (void)photoViewControllerCancelDismissCompletion:(HXPhotoViewController *)photoViewController;
@end
@interface HXPhotoViewController : UIViewController
@property (copy, nonatomic) viewControllerDidDoneBlock doneBlock;
@property (copy, nonatomic) viewControllerDidCancelBlock cancelBlock;
@property (weak, nonatomic) id<HXPhotoViewControllerDelegate> delegate;
@property (strong, nonatomic) HXPhotoManager *manager;
@property (strong, nonatomic) HXAlbumModel *albumModel;
@property (strong, nonatomic) HXPhotoBottomView *bottomView;
@property (strong, nonatomic) HXPhotoLimitView *limitView;
- (HXPhotoViewCell *)currentPreviewCell:(HXPhotoModel *)model;
- (BOOL)scrollToModel:(HXPhotoModel *)model;
- (void)scrollToPoint:(HXPhotoViewCell *)cell rect:(CGRect)rect;
- (void)startGetAllPhotoModel;
@end
@protocol HXPhotoViewCellDelegate <NSObject>
@optional
- (void)photoViewCell:(HXPhotoViewCell *)cell didSelectBtn:(UIButton *)selectBtn;
- (void)photoViewCellRequestICloudAssetComplete:(HXPhotoViewCell *)cell;
@end
@interface HXPhotoViewCell : UICollectionViewCell
@property (weak, nonatomic) id<HXPhotoViewCellDelegate> delegate;
@property (assign, nonatomic) NSInteger section;
@property (assign, nonatomic) NSInteger item;
@property (assign, nonatomic) BOOL canSelect;
@property (strong, nonatomic, readonly) UIImageView *imageView;
@property (strong, nonatomic) CALayer *selectMaskLayer;
@property (strong, nonatomic) HXPhotoModel *model;
@property (assign, nonatomic) BOOL singleSelected;
@property (strong, nonatomic) UIColor *selectBgColor;
@property (strong, nonatomic) UIColor *selectedTitleColor;
@property (strong, nonatomic) UIColor *darkSelectBgColor;
@property (strong, nonatomic) UIColor *darkSelectedTitleColor;
@property (strong, nonatomic, readonly) CALayer *videoMaskLayer;
@property (strong, nonatomic, readonly) UIButton *selectBtn;
- (void)resetNetworkImage;
- (void)cancelRequest;
- (void)startRequestICloudAsset;
- (void)bottomViewPrepareAnimation;
- (void)bottomViewStartAnimation;
- (void)setModel:(HXPhotoModel *)model emptyImage:(BOOL)emptyImage;
- (void)setModelDataWithHighQuality:(BOOL)highQuality completion:(void (^)(HXPhotoViewCell *myCell))completion;
@end
@interface HXPhotoCameraViewCell : UICollectionViewCell
@property (strong, nonatomic) HXPhotoModel *model;
@property (strong, nonatomic, readonly) HXCustomCameraController *cameraController;
@property (strong, nonatomic) UIImage *cameraImage;
@property (assign, nonatomic) BOOL cameraSelected;
@property (assign, nonatomic) BOOL startSession;
@property (strong, nonatomic) UIColor *bgColor;
- (void)starRunning;
- (void)stopRunning;
@end
@interface HXPhotoLimitViewCell : UICollectionViewCell
@property (strong, nonatomic) UIColor *bgColor;
@property (strong, nonatomic) UIColor *bgDarkColor;
@property (strong, nonatomic) UIColor *lineColor;
@property (strong, nonatomic) UIColor *lineDarkColor;
@property (strong, nonatomic) UIColor *textColor;
@property (strong, nonatomic) UIColor *textDarkColor;
@property (strong, nonatomic) UIFont *textFont;
- (void)config;
@end
@interface HXPhotoViewSectionFooterView : UICollectionReusableView
@property (assign, nonatomic) NSInteger photoCount;
@property (assign, nonatomic) NSInteger videoCount;
@property (strong, nonatomic) UIColor *bgColor;
@property (strong, nonatomic) UIColor *textColor;
@end
@protocol HXPhotoBottomViewDelegate <NSObject>
@optional
- (void)photoBottomViewDidPreviewBtn;
- (void)photoBottomViewDidDoneBtn;
- (void)photoBottomViewDidEditBtn;
@end
@interface HXPhotoBottomView : UIView
@property (weak, nonatomic) id<HXPhotoBottomViewDelegate> delegate;
@property (strong, nonatomic) HXPhotoManager *manager;
@property (assign, nonatomic) BOOL previewBtnEnabled;
@property (assign, nonatomic) BOOL doneBtnEnabled;
@property (assign, nonatomic) NSInteger selectCount;
@property (strong, nonatomic) UIButton *originalBtn;
@property (strong, nonatomic) UIToolbar *bgView;
- (void)requestPhotosBytes;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
//
// HXVideoEditViewController.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/12/31.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoManager.h"
@class
HXVideoEditViewController,
HXVideoEditBottomView,
HXEditFrameView;
typedef void (^ HXVideoEditViewControllerDidDoneBlock)(HXPhotoModel *beforeModel, HXPhotoModel *afterModel, HXVideoEditViewController *viewController);
typedef void (^ HXVideoEditViewControllerDidCancelBlock)(HXVideoEditViewController *viewController);
@protocol HXVideoEditViewControllerDelegate <NSObject>
@optional
/// 编辑完成
/// @param videoEditViewController 视频编辑控制器
/// @param beforeModel 编辑之前的模型
/// @param afterModel 编辑之后的模型
- (void)videoEditViewControllerDidDoneClick:(HXVideoEditViewController *)videoEditViewController beforeModel:(HXPhotoModel *)beforeModel afterModel:(HXPhotoModel *)afterModel;
/// 取消编辑
/// @param videoEditViewController 视频编辑控制器
- (void)videoEditViewControllerDidCancelClick:(HXVideoEditViewController *)videoEditViewController;
@end
@interface HXVideoEditViewController : UIViewController<UIViewControllerTransitioningDelegate>
@property (weak, nonatomic) id<HXVideoEditViewControllerDelegate> delegate;
/// 需要编辑的模型
@property (strong, nonatomic) HXPhotoModel *model;
/// 照片管理类
@property (strong, nonatomic) HXPhotoManager *manager;
@property (copy, nonatomic) HXVideoEditViewControllerDidDoneBlock doneBlock;
@property (copy, nonatomic) HXVideoEditViewControllerDidCancelBlock cancelBlock;
@property (assign, nonatomic) BOOL outside;
@property (assign, nonatomic) BOOL isInside;
@property (strong, nonatomic) UIView *videoView;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
@property (strong, nonatomic) AVAsset *avAsset;
@property (strong, nonatomic) UIImageView *bgImageView;
@property (assign, nonatomic) BOOL requestComplete;
@property (assign, nonatomic) BOOL transitionCompletion;
@property (assign, nonatomic) BOOL isCancel;
- (void)completeTransition;
- (void)showBottomView;
- (CGRect)getVideoRect;
@end
@protocol HXVideoEditBottomViewDelegate <NSObject>
@optional
- (void)videoEditBottomViewDidCancelClick:(HXVideoEditBottomView *)bottomView;
- (void)videoEditBottomViewDidDoneClick:(HXVideoEditBottomView *)bottomView;
- (void)videoEditBottomViewValidRectChanged:(HXVideoEditBottomView *)bottomView;
- (void)videoEditBottomViewValidRectEndChanged:(HXVideoEditBottomView *)bottomView;
- (void)videoEditBottomViewIndicatorLinePanGestureBegan:(HXVideoEditBottomView *)bottomView frame:(CGRect)frame second:(CGFloat)second;
- (void)videoEditBottomViewIndicatorLinePanGestureChanged:(HXVideoEditBottomView *)bottomView second:(CGFloat)second;
- (void)videoEditBottomViewIndicatorLinePanGestureEnd:(HXVideoEditBottomView *)bottomView frame:(CGRect)frame second:(CGFloat)second;
@end
@interface HXVideoEditBottomView : UIView
@property (strong, nonatomic) UIButton *playBtn;
@property (assign, nonatomic) CGFloat itemHeight;
@property (assign, nonatomic) CGFloat itemWidth;
@property (assign, nonatomic) CGFloat validRectX;
@property (strong, nonatomic) HXPhotoModel *model;
@property (strong, nonatomic) UICollectionView *collectionView;
@property (strong, nonatomic) HXEditFrameView *editView;
@property (strong, nonatomic) AVAsset *avAsset;
@property (assign, nonatomic) CGFloat interval;
@property (assign, nonatomic) CGFloat contentWidth;
@property (assign, nonatomic) CGFloat singleItemSecond;
@property (strong, nonatomic) NSMutableArray *dataArray;
@property (strong, nonatomic) UIView *indicatorLine;
@property (weak, nonatomic) id<HXVideoEditBottomViewDelegate> delegate;
@property (copy, nonatomic) void (^ scrollViewDidScroll)(void);
@property (copy, nonatomic) void (^ startTimer)(void);
@property (strong, nonatomic) UILabel *startTimeLb;
@property (strong, nonatomic) UILabel *endTimeLb;
@property (strong, nonatomic) UILabel *totalTimeLb;
- (instancetype)initWithManager:(HXPhotoManager *)manager;
- (void)removeLineView;
- (void)startLineAnimationWithDuration:(NSTimeInterval)duration;
- (void)panGestureStarAnimationWithDuration:(NSTimeInterval)duration;
- (void)updateTimeLbsFrame;
@end
@interface HXVideoEditBottomViewCell : UICollectionViewCell
@property (strong, nonatomic) UIImageView *imageView;
@end
@protocol HXEditFrameViewDelegate <NSObject>
- (void)editViewValidRectChanged;
- (void)editViewValidRectEndChanged;
@end
@interface HXEditFrameView : UIView
@property (assign, nonatomic) CGFloat itemHeight;
@property (assign, nonatomic) CGFloat itemWidth;
@property (assign, nonatomic) CGFloat validRectX;
@property (nonatomic, assign) CGRect validRect;
@property (assign, nonatomic) CGFloat contentWidth;
@property (assign, nonatomic) NSTimeInterval videoTime;
@property (nonatomic, weak) id <HXEditFrameViewDelegate> delegate;
- (instancetype)initWithManager:(HXPhotoManager *)manager;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
//
// HXAssetManager.h
// HXPhotoPickerExample
//
// Created by Silence on 2020/11/5.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <Photos/Photos.h>
#import "HXPhotoTypes.h"
NS_ASSUME_NONNULL_BEGIN
@class HXAlbumModel;
@interface HXAssetManager : NSObject
/// 获取智能相册
+ (PHFetchResult<PHAssetCollection *> *)fetchSmartAlbumsWithOptions:(PHFetchOptions * _Nullable)options;
/// 获取用户创建的相册
+ (PHFetchResult<PHAssetCollection *> *)fetchUserAlbumsWithOptions:(PHFetchOptions * _Nullable)options;
/// 获取相机胶卷
+ (PHAssetCollection *)fetchCameraRollAlbumWithOptions:(PHFetchOptions * _Nullable)options;
/// 获取所有相册
+ (void)enumerateAllAlbumsWithOptions:(PHFetchOptions * _Nullable)options
usingBlock:(void (^)(PHAssetCollection *collection))enumerationBlock;
/// 获取所有相册模型
+ (void)enumerateAllAlbumModelsWithOptions:(PHFetchOptions * _Nullable)options
usingBlock:(void (^)(HXAlbumModel *albumModel))enumerationBlock;
/// 是否相机胶卷
+ (BOOL)isCameraRollAlbum:(PHAssetCollection *)assetCollection;
/// 获取PHAssetCollection
/// @param localIdentifier 本地标识符
+ (PHAssetCollection *)fetchAssetCollectionWithIndentifier:(NSString *)localIdentifier;
/// 获取PHAsset
/// @param localIdentifier 本地标识符
+ (PHAsset *)fetchAssetWithLocalIdentifier:(NSString *)localIdentifier;
/// 获取PHAsset
/// @param assetCollection 相册
/// @param options 选项
+ (PHFetchResult<PHAsset *> *)fetchAssetsInAssetCollection:(PHAssetCollection *)assetCollection
options:(PHFetchOptions *)options;
/// Asset的原图
+ (UIImage *)originImageForAsset:(PHAsset *)asset;
/// 请求视频地址
+ (void)requestVideoURL:(PHAsset *)asset
completion:(void (^ _Nullable)(NSURL * _Nullable videoURL))completion;
/// 请求获取image
/// @param asset 需要获取的资源
/// @param targetSize 指定返回的大小
/// @param contentMode 内容模式
/// @param options 选项
/// @param completion 完成请求后调用的 block
+ (PHImageRequestID)requestImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHImageRequestOptions * _Nullable)options
completion:(void (^ _Nullable)(UIImage *result, NSDictionary<NSString *, id> *info))completion;
/// 异步请求 Asset 的缩略图,不会产生网络请求
/// @param asset 需要获取的资源
/// @param targetWidth 指定返回的缩略图的宽度
/// @param completion 完成请求后调用的 block 会被多次调用
+ (PHImageRequestID)requestThumbnailImageForAsset:(PHAsset *)asset
targetWidth:(CGFloat)targetWidth
completion:(void (^ _Nullable)(UIImage *result, NSDictionary<NSString *, id> *info))completion;
/// 异步请求 Asset 的缩略图,不会产生网络请求
/// @param targetWidth 指定返回的缩略图的宽度
/// @param deliveryMode 交付模式
/// @param completion 完成请求后调用的 block
+ (PHImageRequestID)requestThumbnailImageForAsset:(PHAsset *)asset
targetWidth:(CGFloat)targetWidth
deliveryMode:(PHImageRequestOptionsDeliveryMode)deliveryMode
completion:(void (^ _Nullable)(UIImage *result, NSDictionary<NSString *, id> *info))completion;
/// 异步请求 Asset 的展示图
/// @param targetSize 指定返回展示的大小
/// @param networkAccessAllowed 允许网络请求
/// @param progressHandler 存在iCloud上并且允许了网络请求才有回调不在主线程上执行
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestPreviewImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(UIImage *result, NSDictionary<NSString *, id> *info))completion;
/// 请求获取imageData
/// @param asset 需要获取的资源
/// @param options 选项
/// @param completion 完成请求后调用的 block
+ (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset
options:(PHImageRequestOptions * _Nullable)options
completion:(void (^ _Nullable)(NSData *imageData, UIImageOrientation orientation, NSDictionary<NSString *, id> *info))completion;
/// 异步请求 Asset 的imageData
/// @param version 请求版本如果是GIF建议设置PHImageRequestOptionsVersionOriginal
/// @param resizeMode 调整模式
/// @param networkAccessAllowed 允许网络请求
/// @param progressHandler 存在iCloud上并且允许了网络请求才有回调不在主线程上执行
/// @param completion 完成请求后调用的 block只回调一次
/// @return 返回请求图片的请求 id
+ (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset
version:(PHImageRequestOptionsVersion)version
resizeMode:(PHImageRequestOptionsResizeMode)resizeMode
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(NSData *imageData, UIImageOrientation orientation, NSDictionary<NSString *, id> *info))completion;
/// 请求获取LivePhoto
/// @param targetSize 指定返回的大小
/// @param contentMode 内容模式
/// @param options 选项
/// @param completion 完成请求后调用的 block
+ (PHLivePhotoRequestID)requestLivePhotoForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHLivePhotoRequestOptions * _Nullable)options
completion:(void (^ _Nullable)(PHLivePhoto *livePhoto, NSDictionary<NSString *,id> * _Nonnull info))completion;
/// 异步请求 LivePhoto
/// @param targetSize 指定返回的大小
/// @param networkAccessAllowed 允许网络请求
/// @param progressHandler 存在iCloud上并且允许了网络请求才有回调不在主线程上执行
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHLivePhotoRequestID)requestPreviewLivePhotoForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(PHLivePhoto *livePhoto, NSDictionary<NSString *,id> * _Nonnull info))completion;
/// 请求获取 AVAsset
/// @param options 选项
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestAVAssetForAsset:(PHAsset *)asset
options:(PHVideoRequestOptions * _Nullable)options
completion:(void (^ _Nullable)(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info))completion;
/// 请求获取 AVAsset
/// @param networkAccessAllowed 允许网络请求
/// @param progressHandler 存在iCloud上并且允许了网络请求才有回调不在主线程上执行
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestAVAssetForAsset:(PHAsset *)asset
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info))completion;
/// 请求获取 AVPlayerItem
/// @param options 选项
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestPlayerItemForAsset:(PHAsset *)asset
options:(PHVideoRequestOptions * _Nullable)options
completion:(void (^ _Nullable)(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info))completion;
/// 请求获取 AVPlayerItem
/// @param networkAccessAllowed 允许网络请求
/// @param progressHandler 存在iCloud上并且允许了网络请求才有回调不在主线程上执行
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestPlayerItemForAsset:(PHAsset *)asset
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info))completion;
/// 请求获取 AVAssetExportSession
/// @param options 选项
/// @param exportPreset 导出质量
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestExportSessionForAsset:(PHAsset *)asset
options:(PHVideoRequestOptions * _Nullable)options
exportPreset:(NSString *)exportPreset
completion:(void (^ _Nullable)(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info))completion;
/// 请求获取 AVAssetExportSession
/// @param exportPreset 导出质量
/// @param networkAccessAllowed 允许网络请求
/// @param progressHandler 存在iCloud上并且允许了网络请求才有回调不在主线程上执行
/// @param completion 完成请求后调用的 block只会回调一次
+ (PHImageRequestID)requestExportSessionForAsset:(PHAsset *)asset
exportPreset:(NSString *)exportPreset
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info))completion;
/// 获取视频地址,可压缩
/// @param asset 对应的 PHAsset 对象
/// @param fileURL 指定导出地址
/// @param exportPreset 导出视频分辨率
/// @param videoQuality 导出视频质量[0-10]
/// @param resultHandler 导出结果
+ (void)requestVideoURLForAsset:(PHAsset *)asset
toFile:(NSURL * _Nullable)fileURL
exportPreset:(HXVideoExportPreset)exportPreset
videoQuality:(NSInteger)videoQuality
resultHandler:(void (^ _Nullable)(NSURL * _Nullable))resultHandler;
/// 获取是否成功完成
/// @param info 获取时返回的信息
+ (BOOL)downloadFininedForInfo:(NSDictionary *)info;
/// 资源是否在iCloud上
/// @param info 获取时返回的信息
+ (BOOL)isInCloudForInfo:(NSDictionary *)info;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,550 @@
//
// HXAssetManager.m
// HXPhotoPickerExample
//
// Created by Silence on 2020/11/5.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXAssetManager.h"
#import "HXAlbumModel.h"
#import "NSString+HXExtension.h"
#import "PHAsset+HXExtension.h"
#import "HXPhotoTools.h"
@implementation HXAssetManager
+ (PHFetchResult<PHAssetCollection *> *)fetchSmartAlbumsWithOptions:(PHFetchOptions * _Nullable)options {
return [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAny options:options];
}
+ (PHFetchResult<PHAssetCollection *> *)fetchUserAlbumsWithOptions:(PHFetchOptions * _Nullable)options {
return [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:options];
}
+ (void)enumerateAllAlbumsWithOptions:(PHFetchOptions * _Nullable)options
usingBlock:(void (^)(PHAssetCollection *collection))enumerationBlock {
PHFetchResult *smartAlbums = [self fetchSmartAlbumsWithOptions:options];
PHFetchResult *userAlbums = [self fetchUserAlbumsWithOptions:options];
NSArray *allAlbum = [NSArray arrayWithObjects:smartAlbums, userAlbums, nil];
for (PHFetchResult *result in allAlbum) {
for (PHAssetCollection *collection in result) {
if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
if (collection.estimatedAssetCount <= 0) continue;
if (collection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumAllHidden) continue;
if (collection.assetCollectionSubtype == 215) continue;
if (collection.assetCollectionSubtype == 212) continue;
if (collection.assetCollectionSubtype == 204) continue;
if (collection.assetCollectionSubtype == 1000000201) continue;
if (enumerationBlock) {
enumerationBlock(collection);
}
}
}
}
+ (void)enumerateAllAlbumModelsWithOptions:(PHFetchOptions * _Nullable)options
usingBlock:(void (^)(HXAlbumModel *albumModel))enumerationBlock {
[self enumerateAllAlbumsWithOptions:nil usingBlock:^(PHAssetCollection *collection) {
HXAlbumModel *albumModel = [[HXAlbumModel alloc] initWithCollection:collection options:options];
if (enumerationBlock) {
enumerationBlock(albumModel);
}
}];
}
///
+ (PHAssetCollection *)fetchCameraRollAlbumWithOptions:(PHFetchOptions * _Nullable)options {
PHFetchResult *smartAlbums = [self fetchSmartAlbumsWithOptions:options];
for (PHAssetCollection *collection in smartAlbums) {
if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
if (collection.estimatedAssetCount <= 0) continue;
if ([self isCameraRollAlbum:collection]) {
return collection;
}
}
return nil;
}
+ (BOOL)isCameraRollAlbum:(PHAssetCollection *)assetCollection {
NSString *versionStr = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@""];
if (versionStr.length <= 1) {
versionStr = [versionStr stringByAppendingString:@"00"];
} else if (versionStr.length <= 2) {
versionStr = [versionStr stringByAppendingString:@"0"];
}
CGFloat version = versionStr.floatValue;
if (version >= 800 && version <= 802) {
return assetCollection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumRecentlyAdded;
} else {
return assetCollection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumUserLibrary;
}
}
+ (PHAssetCollection *)fetchAssetCollectionWithIndentifier:(NSString *)localIdentifier {
return [[PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[localIdentifier] options:nil] firstObject];
}
#pragma mark - < PHAsset >
+ (PHAsset *)fetchAssetWithLocalIdentifier:(NSString *)localIdentifier {
return [[PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil] firstObject];
}
+ (PHFetchResult<PHAsset *> *)fetchAssetsInAssetCollection:(PHAssetCollection *)assetCollection
options:(PHFetchOptions *)options {
return [PHAsset fetchAssetsInAssetCollection:assetCollection options:options];
}
+ (UIImage *)originImageForAsset:(PHAsset *)asset {
__block UIImage *resultImage = nil;
PHImageRequestOptions *phImageRequestOptions = [[PHImageRequestOptions alloc] init];
phImageRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
phImageRequestOptions.networkAccessAllowed = YES;
phImageRequestOptions.synchronous = YES;
[[PHImageManager defaultManager] requestImageDataForAsset:asset options:phImageRequestOptions resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
resultImage = [UIImage imageWithData:imageData];
}];
return resultImage;
}
+ (void)requestVideoURL:(PHAsset *)asset completion:(void (^)(NSURL * _Nullable))completion {
[self requestAVAssetForAsset:asset networkAccessAllowed:YES progressHandler:nil completion:^(AVAsset * _Nonnull avAsset, AVAudioMix * _Nonnull audioMix, NSDictionary * _Nonnull info) {
[asset hx_checkForModificationsWithAssetPathMethodCompletion:^(BOOL isAdjusted) {
NSString *fileName = [[NSString hx_fileName] stringByAppendingString:@".mp4"];
NSString *fullPathToFile = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSURL *videoURL = [NSURL fileURLWithPath:fullPathToFile];
if (isAdjusted) {
if ([avAsset isKindOfClass:AVURLAsset.class]) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
[fileManager copyItemAtURL:[(AVURLAsset *)avAsset URL] toURL:videoURL error:&error];
if (error == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(videoURL);
}
});
return;
}
}
NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
NSString *presetName;
if ([presets containsObject:AVAssetExportPresetHighestQuality]) {
presetName = AVAssetExportPresetHighestQuality;
}else {
presetName = AVAssetExportPresetMediumQuality;
}
AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:presetName];
session.outputURL = videoURL;
session.shouldOptimizeForNetworkUse = YES;
NSArray *supportedTypeArray = session.supportedFileTypes;
if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
session.outputFileType = AVFileTypeMPEG4;
} else if (supportedTypeArray.count == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(nil);
}
});
return;
}else {
session.outputFileType = [supportedTypeArray objectAtIndex:0];
}
[session exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if ([session status] == AVAssetExportSessionStatusCompleted) {
if (completion) {
completion(videoURL);
}
}else if ([session status] == AVAssetExportSessionStatusFailed ||
[session status] == AVAssetExportSessionStatusCancelled) {
if (completion) {
completion(nil);
}
}
});
}];
}else {
PHAssetResource *videoResource = [PHAssetResource assetResourcesForAsset:asset].firstObject;
PHAssetResourceRequestOptions *options = [[PHAssetResourceRequestOptions alloc] init];
options.networkAccessAllowed = YES;
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:videoURL options:options completionHandler:^(NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (!error) {
if (completion) {
completion(videoURL);
}
}else {
completion(nil);
}
});
}];
}
}];
}];
}
+ (CGSize)getAssetTargetSizeWithAsset:(PHAsset *)asset width:(CGFloat)width {
if (!asset) {
return CGSizeMake(width, width);
}
CGFloat scale = 0.8f;
CGFloat aspectRatio = asset.pixelWidth / (CGFloat)asset.pixelHeight;
CGFloat initialWidth = width;
CGFloat height;
if (asset.pixelWidth < width) {
width = width * 0.5f;
}
height = width / aspectRatio;
CGFloat maxHeight = [UIScreen mainScreen].bounds.size.height;
if (height > maxHeight) {
width = maxHeight / height * width * scale;
height = maxHeight * scale;
}
if (height < initialWidth && width >= initialWidth) {
width = initialWidth / height * width * scale;
height = initialWidth * scale;
}
CGSize size = CGSizeMake(width, height);
return size;
}
+ (PHImageRequestID)requestImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHImageRequestOptions *)options
completion:(void (^)(UIImage *result, NSDictionary<NSString *, id> *info))completion {
if (!asset) {
completion(nil, nil);
return -1;
}
return [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(result, info);
});
}
}];
}
+ (PHImageRequestID)requestThumbnailImageForAsset:(PHAsset *)asset
targetWidth:(CGFloat)targetWidth
completion:(void (^)(UIImage *result, NSDictionary<NSString *, id> *info))completion {
return [self requestThumbnailImageForAsset:asset targetWidth:targetWidth deliveryMode:PHImageRequestOptionsDeliveryModeOpportunistic completion:^(UIImage * _Nonnull result, NSDictionary<NSString *,id> * _Nonnull info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(result, info);
});
}
}];
}
+ (PHImageRequestID)requestThumbnailImageForAsset:(PHAsset *)asset
targetWidth:(CGFloat)targetWidth
deliveryMode:(PHImageRequestOptionsDeliveryMode)deliveryMode
completion:(void (^)(UIImage *result, NSDictionary<NSString *, id> *info))completion {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.resizeMode = PHImageRequestOptionsResizeModeFast;
options.deliveryMode = deliveryMode;
return [self requestImageForAsset:asset targetSize:[self getAssetTargetSizeWithAsset:asset width:targetWidth] contentMode:PHImageContentModeAspectFill options:options completion:^(UIImage * _Nonnull result, NSDictionary<NSString *,id> * _Nonnull info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(result, info);
});
}
}];
}
+ (PHImageRequestID)requestPreviewImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(UIImage *result, NSDictionary<NSString *, id> *info))completion; {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.resizeMode = PHImageRequestOptionsResizeModeFast;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = networkAccessAllowed;
options.progressHandler = progressHandler;
return [self requestImageForAsset:asset targetSize:targetSize contentMode:PHImageContentModeAspectFill options:options completion:^(UIImage * _Nonnull result, NSDictionary<NSString *,id> * _Nonnull info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(result, info);
});
}
}];
}
+ (void)requestVideoURLForAsset:(PHAsset *)asset
toFile:(NSURL * _Nullable)fileURL
exportPreset:(HXVideoExportPreset)exportPreset
videoQuality:(NSInteger)videoQuality
resultHandler:(void (^ _Nullable)(NSURL * _Nullable))resultHandler {
[self requestAVAssetForAsset:asset networkAccessAllowed:YES progressHandler:nil completion:^(AVAsset * _Nonnull asset, AVAudioMix * _Nonnull audioMix, NSDictionary * _Nonnull info) {
if (asset == nil) {
if (resultHandler) {
resultHandler(nil);
}
return;
}
NSString *fileName = [[NSString hx_fileName] stringByAppendingString:@".mp4"];
NSString *fullPathToFile = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSURL *videoURL = fileURL ?: [NSURL fileURLWithPath:fullPathToFile];
NSString *presetName = [self presetName:exportPreset];
NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset];
if (![presets containsObject:presetName]) {
if (resultHandler) {
resultHandler(nil);
}
return;
}
AVAssetExportSession *session = [AVAssetExportSession exportSessionWithAsset:asset presetName:presetName];
session.outputURL = videoURL;
session.shouldOptimizeForNetworkUse = YES;
NSArray *supportedTypeArray = session.supportedFileTypes;
if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
session.outputFileType = AVFileTypeMPEG4;
}else if (supportedTypeArray.count == 0) {
if (resultHandler) {
resultHandler(nil);
}
return;
}else {
session.outputFileType = [supportedTypeArray objectAtIndex:0];
}
if (videoQuality > 0) {
session.fileLengthLimit = [self exportSessionFileLengthLimitWithSeconds:CMTimeGetSeconds(asset.duration) exportPreset:exportPreset videoQuality:videoQuality];
}
[session exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (session.status == AVAssetExportSessionStatusCompleted) {
if (resultHandler) {
resultHandler(videoURL);
}
}else {
if (resultHandler) {
resultHandler(nil);
}
}
});
}];
}];
}
+ (NSString *)presetName:(HXVideoExportPreset)exportPreset {
switch (exportPreset) {
case HXVideoEditorExportPresetLowQuality:
return AVAssetExportPresetLowQuality;
case HXVideoEditorExportPresetMediumQuality:
return AVAssetExportPresetMediumQuality;
case HXVideoEditorExportPresetHighQuality:
return AVAssetExportPresetHighestQuality;
case HXVideoEditorExportPresetRatio_640x480:
return AVAssetExportPreset640x480;
case HXVideoEditorExportPresetRatio_960x540:
return AVAssetExportPreset960x540;
case HXVideoEditorExportPresetRatio_1280x720:
return AVAssetExportPreset1280x720;
default:
return AVAssetExportPresetMediumQuality;
}
}
+ (NSInteger)exportSessionFileLengthLimitWithSeconds:(CGFloat)seconds
exportPreset:(HXVideoExportPreset)exportPreset
videoQuality:(NSInteger)videoQuality {
if (videoQuality > 0) {
CGFloat ratioParam = 0;
if (exportPreset == HXVideoEditorExportPresetRatio_640x480) {
ratioParam = 0.02;
}else if (exportPreset == HXVideoEditorExportPresetRatio_960x540) {
ratioParam = 0.04;
}else if (exportPreset == HXVideoEditorExportPresetRatio_1280x720) {
ratioParam = 0.08;
}
NSInteger quality = MIN(videoQuality, 10);
return seconds * ratioParam * quality * 1000 * 1000;
}
return 0;
}
+ (UIImageOrientation)imageOrientationWithCGImageOrientation:(CGImagePropertyOrientation)orientation {
UIImageOrientation sureOrientation;
if (orientation == kCGImagePropertyOrientationUp) {
sureOrientation = UIImageOrientationUp;
} else if (orientation == kCGImagePropertyOrientationUpMirrored) {
sureOrientation = UIImageOrientationUpMirrored;
} else if (orientation == kCGImagePropertyOrientationDown) {
sureOrientation = UIImageOrientationDown;
} else if (orientation == kCGImagePropertyOrientationDownMirrored) {
sureOrientation = UIImageOrientationDownMirrored;
} else if (orientation == kCGImagePropertyOrientationLeftMirrored) {
sureOrientation = UIImageOrientationLeftMirrored;
} else if (orientation == kCGImagePropertyOrientationRight) {
sureOrientation = UIImageOrientationRight;
} else if (orientation == kCGImagePropertyOrientationRightMirrored) {
sureOrientation = UIImageOrientationRightMirrored;
} else if (orientation == kCGImagePropertyOrientationLeft) {
sureOrientation = UIImageOrientationLeft;
} else {
sureOrientation = UIImageOrientationUp;
}
return sureOrientation;
}
+ (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset
options:(PHImageRequestOptions *)options
completion:(void (^)(NSData *imageData, UIImageOrientation orientation, NSDictionary<NSString *, id> *info))completion {
PHImageRequestID requestID;
if (@available(iOS 13.0, *)) {
requestID = [[PHImageManager defaultManager] requestImageDataAndOrientationForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, CGImagePropertyOrientation orientation, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(imageData, [self imageOrientationWithCGImageOrientation:orientation], info);
});
}
}];
}else {
requestID = [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(imageData, orientation, info);
});
}
}];
}
return requestID;
}
+ (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset
version:(PHImageRequestOptionsVersion)version
resizeMode:(PHImageRequestOptionsResizeMode)resizeMode
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler)progressHandler
completion:(void (^)(NSData *imageData, UIImageOrientation orientation, NSDictionary<NSString *, id> *info))completion {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.version = version;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.resizeMode = resizeMode;
options.networkAccessAllowed = networkAccessAllowed;
options.progressHandler = progressHandler;
return [self requestImageDataForAsset:asset options:options completion:^(NSData * _Nonnull imageData, UIImageOrientation orientation, NSDictionary<NSString *,id> * _Nonnull info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(imageData, orientation, info);
});
}
}];
}
+ (PHLivePhotoRequestID)requestLivePhotoForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(PHLivePhotoRequestOptions *)options
completion:(void (^)(PHLivePhoto *livePhoto, NSDictionary<NSString *,id> * _Nonnull info))completion {
return [[PHImageManager defaultManager] requestLivePhotoForAsset:asset targetSize:targetSize contentMode:contentMode options:options resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(livePhoto, info);
});
}
}];
}
+ (PHLivePhotoRequestID)requestPreviewLivePhotoForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler)progressHandler
completion:(void (^)(PHLivePhoto *livePhoto, NSDictionary<NSString *,id> * _Nonnull info))completion {
PHLivePhotoRequestOptions *options = [[PHLivePhotoRequestOptions alloc] init];
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = networkAccessAllowed;
options.progressHandler = progressHandler;
return [self requestLivePhotoForAsset:asset targetSize:targetSize contentMode:PHImageContentModeAspectFill options:options completion:^(PHLivePhoto * _Nonnull livePhoto, NSDictionary<NSString *,id> * _Nonnull info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(livePhoto, info);
});
}
}];
}
+ (PHImageRequestID)requestAVAssetForAsset:(PHAsset *)asset
options:(PHVideoRequestOptions *)options
completion:(void (^)(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info))completion {
return [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(asset, audioMix, info);
});
}
}];
}
+ (PHImageRequestID)requestAVAssetForAsset:(PHAsset *)asset
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler)progressHandler
completion:(void (^)(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info))completion {
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = networkAccessAllowed;
options.progressHandler = progressHandler;
return [self requestAVAssetForAsset:asset options:options completion:^(AVAsset * _Nonnull asset, AVAudioMix * _Nonnull audioMix, NSDictionary * _Nonnull info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(asset, audioMix, info);
});
}
}];
}
+ (PHImageRequestID)requestPlayerItemForAsset:(PHAsset *)asset
options:(PHVideoRequestOptions * _Nullable)options
completion:(void (^ _Nullable)(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info))completion {
return [[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:options resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(playerItem, info);
});
}
}];
}
+ (PHImageRequestID)requestPlayerItemForAsset:(PHAsset *)asset
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info))completion {
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = networkAccessAllowed;
options.progressHandler = progressHandler;
return [self requestPlayerItemForAsset:asset options:options completion:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(playerItem, info);
});
}
}];
}
+ (PHImageRequestID)requestExportSessionForAsset:(PHAsset *)asset
options:(PHVideoRequestOptions * _Nullable)options
exportPreset:(NSString *)exportPreset
completion:(void (^ _Nullable)(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info))completion {
return [[PHImageManager defaultManager] requestExportSessionForVideo:asset options:options exportPreset:exportPreset resultHandler:^(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(exportSession, info);
});
}
}];
}
+ (PHImageRequestID)requestExportSessionForAsset:(PHAsset *)asset
exportPreset:(NSString *)exportPreset
networkAccessAllowed:(BOOL)networkAccessAllowed
progressHandler:(PHAssetImageProgressHandler _Nullable)progressHandler
completion:(void (^ _Nullable)(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info))completion {
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = networkAccessAllowed;
options.progressHandler = progressHandler;
return [self requestExportSessionForAsset:asset options:options exportPreset:exportPreset completion:^(AVAssetExportSession * _Nullable exportSession, NSDictionary * _Nullable info) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(exportSession, info);
});
}
}];
}
+ (BOOL)downloadFininedForInfo:(NSDictionary *)info {
BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey] && ![[info objectForKey:PHImageResultIsDegradedKey] boolValue]);
return downloadFinined;
}
+ (BOOL)isInCloudForInfo:(NSDictionary *)info {
return [[info objectForKey:PHImageResultIsInCloudKey] boolValue];
}
@end

View File

@ -0,0 +1,86 @@
//
// HXPhotoCommon.h
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/8.
// Copyright © 2019年 Silence. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HXPhotoConfiguration.h"
#import "HXPhotoDefine.h"
#import "HXPhotoModel.h"
#import "HXAlbumModel.h"
#if __has_include(<AFNetworking/AFNetworking.h>)
#import <AFNetworking/AFNetworking.h>
#elif __has_include("AFNetworking.h")
#import "AFNetworking.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoCommon : NSObject
@property (strong, nonatomic, nullable) NSBundle *languageBundle;
@property (strong, nonatomic, nullable) NSBundle *photoPickerBundle;
/// 默认 AVAudioSessionCategoryPlayback
@property (copy, nonatomic) AVAudioSessionCategory audioSessionCategory;
/// 小图请求大小
@property (assign, nonatomic) CGFloat requestWidth;
/// 相册风格
@property (assign, nonatomic) HXPhotoStyle photoStyle;
/// 相册语言
@property (assign, nonatomic) HXPhotoLanguageType languageType;
/// 相机预览图片
@property (strong, nonatomic) UIImage * _Nullable cameraImage;
/// 相机预览imageURL
@property (strong, nonatomic) NSURL * _Nullable cameraImageURL;
/// 查看LivePhoto是否自动播放为NO时需要长按才可播放
@property (assign, nonatomic) BOOL livePhotoAutoPlay;
/// 预览视频时是否自动播放
@property (assign, nonatomic) HXVideoAutoPlayType videoAutoPlayType;
/// 预览视频时是否先下载视频再播放
@property (assign, nonatomic) BOOL downloadNetworkVideo;
/// 状态栏
@property (assign, nonatomic) BOOL isVCBasedStatusBarAppearance;
/// ios13之后的3DTouch
@property (assign, nonatomic) BOOL isHapticTouch;
/// 点击完成按钮时是否需要下载网络视频
@property (assign, nonatomic) BOOL requestNetworkAfter;
#if HasAFNetworking
@property (assign, nonatomic) AFNetworkReachabilityStatus netStatus;
@property (copy, nonatomic) void (^ reachabilityStatusChangeBlock)(AFNetworkReachabilityStatus netStatus);
#endif
/// 相机胶卷相册唯一标识符
@property (copy, nonatomic) NSString * _Nullable cameraRollLocalIdentifier;
/// 相机胶卷相册Result
@property (copy, nonatomic) PHFetchResult * _Nullable cameraRollResult;
/// 选择类型
@property (assign, nonatomic) NSInteger selectType;
/// 创建日期排序
@property (assign, nonatomic) BOOL creationDateSort;
/// 相册发生改变时,主要作用在选择部分可查看资源时
@property (copy, nonatomic) void (^photoLibraryDidChange)(void);
/// 设备id
@property (copy, nonatomic) NSString *UUIDString;
@property (assign, nonatomic) PHImageRequestID clearAssetRequestID;
/// 下载视频
/// @param videoURL 网络视频地址
/// @param progress 下载进度
/// @param success 下载成功
/// @param failure 下载失败
- (NSURLSessionDownloadTask * _Nullable)downloadVideoWithURL:(NSURL *)videoURL
progress:(void (^ _Nullable)(float progress, long long downloadLength, long long totleLength, NSURL * _Nullable videoURL))progress
downloadSuccess:(void (^ _Nullable)(NSURL * _Nullable filePath, NSURL * _Nullable videoURL))success
downloadFailure:(void (^ _Nullable)(NSError * _Nullable error, NSURL * _Nullable videoURL))failure;
+ (instancetype)photoCommon;
+ (void)deallocPhotoCommon;
- (void)saveCamerImage;
- (BOOL)isDark;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,325 @@
//
// HXPhotoCommon.m
// HXPhotoPickerExample
//
// Created by Silence on 2019/1/8.
// Copyright © 2019 Silence. All rights reserved.
//
#import "HXPhotoCommon.h"
#import "HXPhotoTools.h"
static dispatch_once_t once;
static dispatch_once_t once1;
static id instance;
@interface HXPhotoCommon ()<NSURLConnectionDataDelegate, PHPhotoLibraryChangeObserver>
#if HasAFNetworking
@property (strong, nonatomic) AFURLSessionManager *sessionManager;
#endif
@property (assign, nonatomic) BOOL hasAuthorization;
@end
@implementation HXPhotoCommon
+ (instancetype)photoCommon {
if (instance == nil) {
dispatch_once(&once, ^{
instance = [[HXPhotoCommon alloc] init];
});
}
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
if (instance == nil) {
dispatch_once(&once1, ^{
instance = [super allocWithZone:zone];
});
}
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.audioSessionCategory = AVAudioSessionCategoryPlayback;
self.isVCBasedStatusBarAppearance = [[[NSBundle mainBundle]objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"] boolValue];
#if HasAFNetworking
self.sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[self listenNetWorkStatus];
#endif
NSURL *imageURL = [[NSUserDefaults standardUserDefaults] URLForKey:HXCameraImageKey];
if (imageURL) {
self.cameraImageURL = imageURL;
}
self.cameraRollLocalIdentifier = [[NSUserDefaults standardUserDefaults] objectForKey:@"HXCameraRollLocalIdentifier"];
PHAuthorizationStatus status = [HXPhotoTools authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}
#ifdef __IPHONE_14_0
else if (@available(iOS 14, *)) {
if (status == PHAuthorizationStatusLimited) {
self.cameraRollLocalIdentifier = nil;
self.cameraRollResult = nil;
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(requestAuthorizationCompletion) name:@"HXPhotoRequestAuthorizationCompletion" object:nil];
}
}
#endif
else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(requestAuthorizationCompletion) name:@"HXPhotoRequestAuthorizationCompletion" object:nil];
}
}
return self;
}
- (void)setCameraRollLocalIdentifier:(NSString *)cameraRollLocalIdentifier {
_cameraRollLocalIdentifier = cameraRollLocalIdentifier;
if (cameraRollLocalIdentifier) {
[[NSUserDefaults standardUserDefaults] setObject:cameraRollLocalIdentifier forKey:@"HXCameraRollLocalIdentifier"];
}
}
- (void)requestAuthorizationCompletion {
if (!self.hasAuthorization) {
PHAuthorizationStatus status = [HXPhotoTools authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
self.hasAuthorization = YES;
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}
#ifdef __IPHONE_14_0
else if (@available(iOS 14, *)) {
if (status == PHAuthorizationStatusLimited) {
self.cameraRollLocalIdentifier = nil;
self.cameraRollResult = nil;
self.hasAuthorization = YES;
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}
}
#endif
}
}
#pragma mark - < PHPhotoLibraryChangeObserver >
- (void)photoLibraryDidChange:(PHChange *)changeInstance {
// if (!self.cameraRollResult) {
// [self photoListReload];
// return;
// }
PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.cameraRollResult];
if (collectionChanges) {
if ([collectionChanges hasIncrementalChanges]) {
if (collectionChanges.insertedObjects.count > 0 ||
collectionChanges.removedObjects.count > 0 ||
collectionChanges.changedObjects.count > 0 ||
[collectionChanges hasMoves]) {
PHFetchResult *result = collectionChanges.fetchResultAfterChanges;
self.cameraRollResult = result;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"HXPhotoViewNeedReloadNotification" object:nil];
});
}
}else {
PHFetchResult *result = collectionChanges.fetchResultAfterChanges;
self.cameraRollResult = result;
[self photoListReload];
}
}
}
- (void)photoListReload {
dispatch_async(dispatch_get_main_queue(), ^{
#ifdef __IPHONE_14_0
if (@available(iOS 14, *)) {
PHAuthorizationStatus status = [HXPhotoTools authorizationStatus];
if (status == PHAuthorizationStatusLimited) {
if (self.photoLibraryDidChange) {
self.photoLibraryDidChange();
}
}
}
#endif
[[NSNotificationCenter defaultCenter] postNotificationName:@"HXPhotoViewNeedReloadNotification" object:nil];
});
}
- (NSBundle *)languageBundle {
if (!_languageBundle) {
NSString *language = [NSLocale preferredLanguages].firstObject;
HXPhotoLanguageType type = self.languageType;
switch (type) {
case HXPhotoLanguageTypeSc : {
language = @"zh-Hans"; //
} break;
case HXPhotoLanguageTypeTc : {
language = @"zh-Hant"; //
} break;
case HXPhotoLanguageTypeJa : {
//
language = @"ja";
} break;
case HXPhotoLanguageTypeKo : {
//
language = @"ko";
} break;
case HXPhotoLanguageTypeEn : {
language = @"en";
} break;
default : {
if ([language hasPrefix:@"en"]) {
language = @"en";
} else if ([language hasPrefix:@"zh"]) {
if ([language rangeOfString:@"Hans"].location != NSNotFound) {
language = @"zh-Hans"; //
} else { // zh-Hant\zh-HK\zh-TW
language = @"zh-Hant"; //
}
} else if ([language hasPrefix:@"ja"]){
//
language = @"ja";
}else if ([language hasPrefix:@"ko"]) {
//
language = @"ko";
}else {
language = @"en";
}
}break;
}
[HXPhotoCommon photoCommon].languageBundle = [NSBundle bundleWithPath:[[NSBundle hx_photoPickerBundle] pathForResource:language ofType:@"lproj"]];
}
return _languageBundle;
}
- (NSString *)UUIDString {
if (!_UUIDString) {
_UUIDString = [[NSUUID UUID] UUIDString];
}
return _UUIDString;
}
- (BOOL)isDark {
if (self.photoStyle == HXPhotoStyleDark) {
return YES;
}else if (self.photoStyle == HXPhotoStyleInvariant) {
return NO;
}
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return YES;
}
}
#endif
return NO;
}
- (void)saveCamerImage {
if (self.cameraImage) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData;
NSString *suffix;
if (UIImagePNGRepresentation(self.cameraImage)) {
//png
imageData = UIImagePNGRepresentation(self.cameraImage);
suffix = @"png";
}else {
//JPEG
imageData = UIImageJPEGRepresentation(self.cameraImage, 0.5);
suffix = @"jpeg";
}
NSString *fileName = [HXCameraImageKey stringByAppendingString:[NSString stringWithFormat:@".%@",suffix]];
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPathToFile = [array.firstObject stringByAppendingPathComponent:fileName];
if ([imageData writeToFile:fullPathToFile atomically:YES]) {
NSURL *imageURL = [NSURL fileURLWithPath:fullPathToFile];
[[NSUserDefaults standardUserDefaults] setURL:imageURL forKey:HXCameraImageKey];
self.cameraImageURL = imageURL;
}
self.cameraImage = nil;
});
}
}
- (void)setCameraImage:(UIImage *)cameraImage {
if (!cameraImage) {
_cameraImage = nil;
}
UIImage *image = [cameraImage hx_scaleImagetoScale:0.5];
if (image) {
_cameraImage = image;
}else {
_cameraImage = cameraImage;
}
}
/** */
- (void)listenNetWorkStatus {
#if HasAFNetworking
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
self.netStatus = manager.networkReachabilityStatus;
[manager startMonitoring];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
self.netStatus = status;
if (self.reachabilityStatusChangeBlock) {
self.reachabilityStatusChangeBlock(status);
}
}];
#endif
}
- (NSURLSessionDownloadTask * _Nullable)downloadVideoWithURL:(NSURL *)videoURL
progress:(void (^)(float progress, long long downloadLength, long long totleLength, NSURL * _Nullable videoURL))progress
downloadSuccess:(void (^)(NSURL * _Nullable filePath, NSURL * _Nullable videoURL))success
downloadFailure:(void (^)(NSError * _Nullable error, NSURL * _Nullable videoURL))failure {
NSString *videoFilePath = [HXPhotoTools getVideoURLFilePath:videoURL];
NSURL *videoFileURL = [NSURL fileURLWithPath:videoFilePath];
if ([HXPhotoTools fileExistsAtVideoURL:videoURL]) {
if (success) {
success(videoFileURL, videoURL);
}
return nil;
}
#if HasAFNetworking
NSURLRequest *request = [NSURLRequest requestWithURL:videoURL];
/* */
NSURLSessionDownloadTask *downloadTask = [self.sessionManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
if (progress) {
progress(downloadProgress.fractionCompleted, downloadProgress.completedUnitCount, downloadProgress.totalUnitCount, videoURL);
}
});
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return videoFileURL;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (!error) {
if (success) {
success(filePath, videoURL);
}
}else {
[[NSFileManager defaultManager] removeItemAtURL:videoFileURL error:nil];
if (failure) {
failure(error, videoURL);
}
}
});
}];
[downloadTask resume];
return downloadTask;
#else
/// pod "HXPhotoPicker/SDWebImage_AF" "HXPhotoPicker/YYWebImage_AF"
NSSLog(@"没有导入AFNetworking网络框架无法下载视频");
return nil;
#endif
}
+ (void)deallocPhotoCommon {
once = 0;
once1 = 0;
instance = nil;
}
- (void)dealloc {
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"HXPhotoRequestAuthorizationCompletion" object:nil];
#if HasAFNetworking
[[AFNetworkReachabilityManager sharedManager] stopMonitoring];
#endif
}
@end

View File

@ -0,0 +1,705 @@
//
// HXPhotoConfiguration.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/11/21.
// Copyright © 2017年 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoTypes.h"
#import "HXPhotoEditConfiguration.h"
@class
HXPhotoBottomView,
HXPhotoPreviewBottomView,
HXPhotoManager,
HXPhotoModel,
HXPhotoPreviewViewController;
@interface HXPhotoConfiguration : NSObject
/// 配置类型
/// 一键配置UI和选择逻辑
@property (assign, nonatomic) HXConfigurationType type;
/// 查看LivePhoto是否自动播放为NO时需要长按才可播放
@property (assign, nonatomic) BOOL livePhotoAutoPlay;
/// 预览大图时允许不先加载小图,直接加载原图
@property (assign, nonatomic) BOOL allowPreviewDirectLoadOriginalImage;
/// 允许滑动的方式选择资源 - 默认允许
/// 类似系统相册和QQ滑动选择逻辑
@property (assign, nonatomic) BOOL allowSlidingSelection;
/// 照片列表取消按钮的位置
/// 只在 albumShowMode = HXPhotoAlbumShowModePopup 时有效
@property (assign, nonatomic) HXPhotoListCancelButtonLocationType photoListCancelLocation;
/// 照片编辑配置
@property (strong, nonatomic) HXPhotoEditConfiguration *photoEditConfigur;
/// 相机界面是否开启定位 默认 YES
@property (assign, nonatomic) BOOL cameraCanLocation;
/// 在系统相册删除资源时是否同步删除已选的同一资源
//@property (assign, nonatomic) BOOL followSystemDeleteAssetToDeleteSelectAsset;
/// 是否使用仿微信的照片编辑 默认YES
@property (assign, nonatomic) BOOL useWxPhotoEdit;
/// 选择网络视频时超出限制时长时是否裁剪 默认NO
@property (assign, nonatomic) BOOL selectNetworkVideoCanEdit;
/// 相机拍照点击完成之后是否跳转编辑界面进行编辑
@property (assign, nonatomic) BOOL cameraPhotoJumpEdit;
/// 旧版照片编辑才有效
/// 照片编辑时底部比例选项
/// 默认: @[@{@"原始值" : @"{0, 0}"},
/// @{@"正方形" : @"{1, 1}"},
/// @{@"2:3" : @"{2, 3}"},
/// @{@"3:4" : @"{3, 4}"},
/// @{@"9:16" : @"{9, 16}"},
/// @{@"16:9" : @"{16, 9}"}]
@property (copy, nonatomic) NSArray *photoEditCustomRatios;
/// 编辑后的照片/视频是否添加到系统相册中
/// 只对旧版编辑有效
/// 默认为NO
@property (assign, nonatomic) BOOL editAssetSaveSystemAblum;
/// 预览视频时是否先下载视频再播放
/// 只有当项目有AFNetworking网络框架的时候才有用
/// pod导入时为 HXPhotoPicker/SDWebImage_AF 或 HXPhotoPicker/YYWebImage_AF
@property (assign, nonatomic) BOOL downloadNetworkVideo;
/// 预览视频时是否自动播放
@property (assign, nonatomic) HXVideoAutoPlayType videoAutoPlayType;
/// 相机聚焦框、完成按钮、录制进度的颜色
@property (strong, nonatomic) UIColor *cameraFocusBoxColor;
/// 选择视频时超出限制时长是否自动跳转编辑界面
/// 视频可以编辑时有效
@property (assign, nonatomic) BOOL selectVideoBeyondTheLimitTimeAutoEdit;
/// 选择视频时是否限制照片大小
@property (assign, nonatomic) BOOL selectVideoLimitSize;
/// 限制视频的大小 单位b 字节
/// 默认 0字节 不限制
/// 网络视频不限制
@property (assign, nonatomic) NSUInteger limitVideoSize;
/// 选择照片时是否限制照片大小
@property (assign, nonatomic) BOOL selectPhotoLimitSize;
/// 限制照片的大小 单位b 字节
/// 默认 0字节 不限制
/// 网络图片不限制
@property (assign, nonatomic) NSUInteger limitPhotoSize;
/// 相机界面默认前置摄像头
@property (assign, nonatomic) BOOL defaultFrontCamera;
/// 在照片列表选择照片完后点击完成时是否请求图片和视频地址
/// 如果需要下载网络图片 [HXPhotoCommon photoCommon].requestNetworkAfter 设置为YES;
/// 选中了原图则是原图,没选中则是高清图
/// 并赋值给model的 thumbPhoto / previewPhoto / videoURL 属性
/// 如果资源为视频 thumbPhoto 和 previewPhoto 就是视频封面
/// model.videoURL 为视频地址
@property (assign, nonatomic) BOOL requestImageAfterFinishingSelection;
/// 当原图按钮隐藏时获取地址时是否请求原图
/// 为YES时 requestImageAfterFinishingSelection 获取的原图
/// 为NO时 requestImageAfterFinishingSelection 获取的不是原图
@property (assign, nonatomic) BOOL requestOriginalImage;
/// 当 requestImageAfterFinishingSelection = YES 并且选中的原图,导出的视频是否为最高质量
/// 如果视频很大的话,导出高质量会很耗时
/// 默认为NO
@property (assign, nonatomic) BOOL exportVideoURLForHighestQuality;
/// 自定义相机内部拍照/录制类型
@property (assign, nonatomic) HXPhotoCustomCameraType customCameraType;
/// 跳转预览界面时动画起始的view使用方法参考demo12里的外部预览功能
@property (copy, nonatomic) UIView * (^customPreviewFromView)(NSInteger currentIndex);
/// 跳转预览界面时动画起始的frame
@property (copy, nonatomic) CGRect (^customPreviewFromRect)(NSInteger currentIndex);
/// 跳转预览界面时展现动画的image使用方法参考demo12里的外部预览功能
@property (copy, nonatomic) UIImage * (^customPreviewFromImage)(NSInteger currentIndex);
/// 退出预览界面时终点view使用方法参考demo12里的外部预览功能
@property (copy, nonatomic) UIView * (^customPreviewToView)(NSInteger currentIndex);
/// 暗黑模式下照片列表cell上选择按钮选中之后的数字标题颜色
@property (strong, nonatomic) UIColor *cellDarkSelectTitleColor;
/// 暗黑模式下照片列表cell上选择按钮选中之后的按钮背景颜色
@property (strong, nonatomic) UIColor *cellDarkSelectBgColor;
/// 暗黑模式下预览大图右上角选择按钮选中之后的数字标题颜色
@property (strong, nonatomic) UIColor *previewDarkSelectTitleColor;
/// 暗黑模式下预览大图右上角选择按钮选中之后的按钮背景颜色
@property (strong, nonatomic) UIColor *previewDarkSelectBgColor;
/// 相册风格
@property (assign, nonatomic) HXPhotoStyle photoStyle;
/// 拍摄的画质 默认 AVCaptureSessionPreset1280x720
@property (copy, nonatomic) NSString *sessionPreset;
/// 使用框架自带的相机录制视频时设置的编码格式, ios11以上
/// iphone7及以上时系统默认AVVideoCodecHEVC
/// HEVC仅支持iPhone 7及以上设备
/// iphone6、6s 都不支持H.265软解H265视频只有声音没有画面
/// 当iphone7以下机型出现只有声音没有画面的问题请将这个值设置为AVVideoCodecH264
/// 替换成系统相机也可以解决
@property (copy, nonatomic) NSString *videoCodecKey;
/// 原图按钮显示已选照片的大小
@property (assign, nonatomic) BOOL showOriginalBytes;
/// 原图按钮显示已选照片大小时是否显示加载菊花
@property (assign, nonatomic) BOOL showOriginalBytesLoading;
/// 导出裁剪视频的质量
/// iPhoneX -> AVAssetExportPresetHighestQuality
@property (copy, nonatomic) NSString *editVideoExportPresetName DEPRECATED_MSG_ATTRIBUTE("Invalid attribute, use editVideoExportPreset and videoQuality");
/// 导出裁剪视频的分辨率 默认 HXVideoEditorExportPresetRatio_960x540
@property (assign, nonatomic) HXVideoEditorExportPreset editVideoExportPreset;
/// 导出裁剪视频的质量[0-10] 默认 6
@property (assign, nonatomic) NSInteger videoQuality;
/// 编辑视频时裁剪的最小秒数如果小于1秒则为1秒
@property (assign, nonatomic) NSInteger minVideoClippingTime;
/// 编辑视频时裁剪的最大秒数 - default 15s
/// 如果超过视频时长,则为视频时长
@property (assign, nonatomic) NSInteger maxVideoClippingTime;
/// 预览大图时的长按响应事件
/// previewViewController.outside
/// yes -> use HXPhotoView preview
/// no -> use HXPhotoViewController preview
@property (copy, nonatomic) void (^ previewRespondsToLongPress)(UILongPressGestureRecognizer *longPress, HXPhotoModel *photoModel, HXPhotoManager *manager, HXPhotoPreviewViewController *previewViewController);
/// 语言类型
@property (assign, nonatomic) HXPhotoLanguageType languageType;
/// 如果选择完照片返回之后
/// 原有界面继承UIScrollView的视图都往下偏移一个导航栏距离的话
/// 那么请将这个属性设置为YES即可恢复。
/// v2.3.7 之后的版本内部自动修复了
@property (assign, nonatomic) BOOL restoreNavigationBar DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/// 照片列表是否按照片创建日期排序
@property (assign, nonatomic) BOOL creationDateSort;
/// 相册列表展示方式
@property (assign, nonatomic) HXPhotoAlbumShowMode albumShowMode;
/// 模型数组保存草稿时存在本地的文件名称 default HXPhotoPickerModelArray
/// 如果有多个地方保存了草稿请设置不同的fileName
@property (copy, nonatomic) NSString *localFileName;
/// 只针对 照片、视频不能同时选并且视频只能选择1个的时候隐藏掉视频cell右上角的选择按钮
@property (assign, nonatomic) BOOL specialModeNeedHideVideoSelectBtn;
/// 视频是否可以编辑 default YES
@property (assign, nonatomic) BOOL videoCanEdit;
/// 是否替换照片编辑界面 default NO
@property (assign, nonatomic) BOOL replacePhotoEditViewController;
/// 图片编辑完成调用这个block 传入模型
/// beforeModel 编辑之前的模型
/// afterModel 编辑之后的模型
@property (copy, nonatomic) void (^usePhotoEditComplete)(HXPhotoModel *beforeModel, HXPhotoModel *afterModel);
/// 是否替换视频编辑界面 default NO
@property (assign, nonatomic) BOOL replaceVideoEditViewController;
/// 将要跳转编辑界面 在block内实现跳转
/// isOutside 是否是HXPhotoView预览时的编辑
/// beforeModel 编辑之前的模型
@property (copy, nonatomic) void (^shouldUseEditAsset)(UIViewController *viewController, BOOL isOutside, HXPhotoManager *manager, HXPhotoModel *beforeModel);
/// 视频编辑完成调用这个block 传入模型
/// beforeModel 编辑之前的模型
/// afterModel 编辑之后的模型
@property (copy, nonatomic) void (^useVideoEditComplete)(HXPhotoModel *beforeModel, HXPhotoModel *afterModel);
/// 照片是否可以编辑 default YES
@property (assign, nonatomic) BOOL photoCanEdit;
/// push动画时长 default 0.45f
@property (assign, nonatomic) NSTimeInterval pushTransitionDuration;
/// pop动画时长 default 0.35f
@property (assign, nonatomic) NSTimeInterval popTransitionDuration;
/// 手势松开时返回的动画时长 default 0.35f
@property (assign, nonatomic) NSTimeInterval popInteractiveTransitionDuration;
/// 旧版照片编辑才有效
/// 是否可移动的裁剪框
@property (assign, nonatomic) BOOL movableCropBox;
/// 旧版照片编辑才有效
/// 可移动的裁剪框是否可以编辑大小
@property (assign, nonatomic) BOOL movableCropBoxEditSize;
/// 旧版照片编辑才有效
/// 可移动裁剪框的比例 (w,h) 一定要是宽比高哦!!!
/// 当 movableCropBox = YES && movableCropBoxEditSize = YES 如果不设置比例即可自由编辑大小
@property (assign, nonatomic) CGPoint movableCropBoxCustomRatio;
/// 是否替换相机控制器
/// 使用自己的相机时需要调用下面两个block
@property (assign, nonatomic) BOOL replaceCameraViewController;
/**
block内实现跳转
demo1 使
*/
@property (copy, nonatomic) void (^shouldUseCamera)(UIViewController *viewController, HXPhotoConfigurationCameraType cameraType, HXPhotoManager *manager);
/**
block
*/
@property (copy, nonatomic) void (^useCameraComplete)(HXPhotoModel *model);
#pragma mark - < UI相关 >
/// 相册权限为选择部分时照片列表添加cell的背景颜色
@property (strong, nonatomic) UIColor *photoListLimitCellBackgroundColor;
/// 相册权限为选择部分时照片列表添加cell暗黑模式下的背景颜色
@property (strong, nonatomic) UIColor *photoListLimitCellBackgroundDarkColor;
/// 相册权限为选择部分时照片列表添加cell的加号颜色
@property (strong, nonatomic) UIColor *photoListLimitCellLineColor;
/// 相册权限为选择部分时照片列表添加cell暗黑模式下的加号颜色
@property (strong, nonatomic) UIColor *photoListLimitCellLineDarkColor;
/// 相册权限为选择部分时照片列表添加cell的文字颜色
@property (strong, nonatomic) UIColor *photoListLimitCellTextColor;
/// 相册权限为选择部分时照片列表添加cell暗黑模式下的文字颜色
@property (strong, nonatomic) UIColor *photoListLimitCellTextDarkColor;
/// 相册权限为选择部分时照片列表添加cell的文字字体
@property (strong, nonatomic) UIFont *photoListLimitCellTextFont;
/// 限制提示视图:背景样式
@property (assign, nonatomic) UIBlurEffectStyle photoListLimitBlurStyle;
/// 限制提示视图:文本颜色
@property (strong, nonatomic) UIColor *photoListLimitTextColor;
/// 限制提示视图:设置按钮颜色
@property (strong, nonatomic) UIColor *photoListLimitSettingColor;
/// 限制提示视图:关闭按钮颜色
@property (strong, nonatomic) UIColor *photoListLimitCloseColor;
/// 照片列表上相机cell上的相机未预览时的图标
@property (copy, nonatomic) NSString *photoListTakePhotoNormalImageNamed;
/// 照片列表上相机cell上的相机还是预览时的图标
@property (copy, nonatomic) NSString *photoListTakePhotoSelectImageNamed;
/// 照片列表上相机cell的背景颜色
@property (strong, nonatomic) UIColor *photoListTakePhotoBgColor;
/// 未授权时界面上提示文字显示的颜色
@property (strong, nonatomic) UIColor *authorizationTipColor;
/**
*/
@property (assign, nonatomic) CGFloat popupTableViewHeight;
/**
*/
@property (strong, nonatomic) UIColor *popupTableViewBgColor;
/**
*/
@property (assign, nonatomic) CGFloat popupTableViewHorizontalHeight;
/**
Cell选中的颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellSelectColor;
/**
Cell选中时的图标颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellSelectIconColor;
/**
Cell高亮的颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellHighlightedColor;
/**
Cell底部线的颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellLineColor;
/**
Cell的背景颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellBgColor;
/**
Cell上相册名称的颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellAlbumNameColor;
/**
Cell上相册名称的字体
*/
@property (strong, nonatomic) UIFont *popupTableViewCellAlbumNameFont;
/**
Cell上照片数量的颜色
*/
@property (strong, nonatomic) UIColor *popupTableViewCellPhotoCountColor;
/**
Cell上照片数量的字体
*/
@property (strong, nonatomic) UIFont *popupTableViewCellPhotoCountFont;
/**
Cell的高度
*/
@property (assign, nonatomic) CGFloat popupTableViewCellHeight;
/**
default YES
*/
@property (assign, nonatomic) BOOL showBottomPhotoDetail;
/**
default YES
*/
@property (assign, nonatomic) BOOL doneBtnShowDetail;
/**
YES
- NO
*/
@property (assign, nonatomic) BOOL supportRotation;
/**
UIStatusBarStyleDefault
*/
@property (assign, nonatomic) UIStatusBarStyle statusBarStyle;
/**
cell选中时的背景颜色
*/
@property (strong, nonatomic) UIColor *cellSelectedBgColor;
/**
cell选中时的文字颜色
*/
@property (strong, nonatomic) UIColor *cellSelectedTitleColor;
/**
*/
@property (strong, nonatomic) UIColor *selectedTitleColor;
/// 预览界面选择按钮的背景颜色
@property (strong, nonatomic) UIColor *previewSelectedBtnBgColor;
/// sectionHeader悬浮时的标题颜色
/// 3.0.3之后的版本已移除此功能
@property (strong, nonatomic) UIColor *sectionHeaderSuspensionTitleColor DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/// sectionHeader悬浮时的背景色
/// 3.0.3之后的版本已移除此功能
@property (strong, nonatomic) UIColor *sectionHeaderSuspensionBgColor DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/// 导航栏标题颜色
@property (strong, nonatomic) UIColor *navigationTitleColor;
/// 照片列表导航栏标题箭头颜色
@property (strong, nonatomic) UIColor *navigationTitleArrowColor;
/// 暗黑模式下照片列表导航栏标题箭头颜色
@property (strong, nonatomic) UIColor *navigationTitleArrowDarkColor;
/// 导航栏是否半透明
@property (assign, nonatomic) BOOL navBarTranslucent;
/// 导航栏背景颜色
@property (strong, nonatomic) UIColor *navBarBackgroudColor;
/// 导航栏样式
@property(nonatomic,assign) UIBarStyle navBarStyle;
/// 导航栏背景图片
@property (strong, nonatomic) UIImage *navBarBackgroundImage;
#pragma mark - < 自定义titleView >
/// 自定义照片列表导航栏titleView
/// albumShowMode == HXPhotoAlbumShowModePopup 时才有效
@property (copy, nonatomic) UIView *(^ photoListTitleView)(NSString *title);
/// 更新照片列表导航栏title
@property (copy, nonatomic) void (^ updatePhotoListTitle)(NSString *title);
/// 照片列表改变titleView选中状态
@property (copy, nonatomic) void (^ photoListChangeTitleViewSelected)(BOOL selected);
/// 获取照片列表导航栏titleView的选中状态
@property (copy, nonatomic) BOOL (^ photoListTitleViewSelected)(void);
/// 照片列表titleView点击事件
@property (copy, nonatomic) void (^ photoListTitleViewAction)(BOOL selected);
/// 照片列表背景颜色
@property (strong, nonatomic) UIColor *photoListViewBgColor;
/// 照片列表底部照片数量文字颜色
@property (strong, nonatomic) UIColor *photoListBottomPhotoCountTextColor;
/// 预览照片界面背景颜色
@property (strong, nonatomic) UIColor *previewPhotoViewBgColor;
/// 相册列表背景颜色
@property (strong, nonatomic) UIColor *albumListViewBgColor;
/// 相册列表cell背景颜色
@property (strong, nonatomic) UIColor *albumListViewCellBgColor;
/// 相册列表cell上文字颜色
@property (strong, nonatomic) UIColor *albumListViewCellTextColor;
/// 相册列表cell选中颜色
@property (strong, nonatomic) UIColor *albumListViewCellSelectBgColor;
/// 相册列表cell底部线颜色
@property (strong, nonatomic) UIColor *albumListViewCellLineColor;
/// 3.0.3之后的版本已移除此功能
@property (assign, nonatomic) BOOL sectionHeaderTranslucent DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/// 导航栏标题颜色是否与主题色同步 默认NO
/// 同步会过滤掉手动设置的导航栏标题颜色
@property (assign, nonatomic) BOOL navigationTitleSynchColor;
/// 底部视图的背景颜色
@property (strong, nonatomic) UIColor *bottomViewBgColor;
/// 底部视图的样式
@property(nonatomic,assign) UIBarStyle bottomViewBarStyle;
/// 底部完成按钮背景颜色
@property (strong, nonatomic) UIColor *bottomDoneBtnBgColor;
/// 底部完成按钮暗黑模式下的背景颜色
@property (strong, nonatomic) UIColor *bottomDoneBtnDarkBgColor;
/// 底部完成按钮禁用状态下的背景颜色
@property (strong, nonatomic) UIColor *bottomDoneBtnEnabledBgColor;
/// 底部完成按钮文字颜色
@property (strong, nonatomic) UIColor *bottomDoneBtnTitleColor;
/// 底部视图是否半透明效果 默认YES
@property (assign, nonatomic) BOOL bottomViewTranslucent;
/// 主题颜色 默认 tintColor
@property (strong, nonatomic) UIColor *themeColor;
/// 预览界面底部已选照片的选中颜色
@property (strong, nonatomic) UIColor *previewBottomSelectColor;
/// 是否可以改变原图按钮的tinColor
@property (assign, nonatomic) BOOL changeOriginalTinColor;
/// 原图按钮普通状态下的按钮图标名
/// 改变主题颜色后建议也改下原图按钮的图标
@property (copy, nonatomic) NSString *originalNormalImageName;
/// 原图按钮图片的tintColor,设置这个颜色可改变图片的颜色
@property (strong, nonatomic) UIColor *originalBtnImageTintColor;
/// 原图按钮选中状态下的按钮图标名
/// 改变主题颜色后建议也改下原图按钮的图标
@property (copy, nonatomic) NSString *originalSelectedImageName;
/// 是否隐藏原图按钮 默认 NO
@property (assign, nonatomic) BOOL hideOriginalBtn;
/// sectionHeader 是否显示照片的位置信息
/// 3.0.3之后的版本已移除此功能
@property (assign, nonatomic) BOOL sectionHeaderShowPhotoLocation DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/**
cell是否显示预览
320 -> NO
other -> YES
*/
@property (assign, nonatomic) BOOL cameraCellShowPreview;
/**
*/
//@property (assign, nonatomic) BOOL horizontalHideStatusBar;
/**
6
*/
@property (assign, nonatomic) NSInteger horizontalRowCount;
/**
section NO
*/
@property (assign, nonatomic) BOOL showDateSectionHeader;
/// 照片列表倒序
@property (assign, nonatomic) BOOL reverseDate;
#pragma mark - < 基本配置 >
/**
4 iphone 4s / 5 3
*/
@property (assign, nonatomic) NSUInteger rowCount;
/**
-
0
1 9
5 5
*/
@property (assign, nonatomic) NSUInteger maxNum;
/**
9 -
0maxNum
*/
@property (assign, nonatomic) NSUInteger photoMaxNum;
/**
1 -
0maxNum
*/
@property (assign, nonatomic) NSUInteger videoMaxNum;
/**
*/
@property (assign, nonatomic) BOOL openCamera;
/**
GIF图片功能 -
*/
@property (assign, nonatomic) BOOL lookGifPhoto;
/**
LivePhoto功能呢 - NO
*/
@property (assign, nonatomic) BOOL lookLivePhoto;
/**
NO
*/
@property (assign, nonatomic) BOOL selectTogether;
/**
- 60s
*/
@property (assign, nonatomic) NSTimeInterval videoMaximumDuration;
/**
- 3s
*/
@property (assign, nonatomic) NSTimeInterval videoMinimumDuration;
/**
* / -
:
..
- NO
*/
@property (assign, nonatomic) BOOL deleteTemporaryPhoto;
/**
* / NO
* - (9.0)
*/
@property (assign, nonatomic) BOOL saveSystemAblum;
/// 拍摄的照片/视频保存到指定相册的名称 默认 DisplayName
/// 需9.0以上系统才可以保存到自定义相册 , 以下的系统只保存到相机胶卷...
@property (copy, nonatomic) NSString *customAlbumName;
/// 视频能选择的最大秒数 - 默认 3分钟/180秒
/// 当视频超过能选的最大时长,如果视频可以编辑那么在列表选择的时候会自动跳转视频裁剪界面
@property (assign, nonatomic) NSInteger videoMaximumSelectDuration;
/// 视频能选择的最小秒数 - 默认 0秒 - 不限制
@property (assign, nonatomic) NSInteger videoMinimumSelectDuration;
/// 是否为单选模式 默认 NO HXPhotoView 不支持
@property (assign, nonatomic) BOOL singleSelected;
/// 单选模式下选择图片时是否直接跳转到编辑界面 - 默认 NO
@property (assign, nonatomic) BOOL singleJumpEdit;
/// 是否开启3DTouch预览功能 默认 YES
@property (assign, nonatomic) BOOL open3DTouchPreview;
/// 下载iCloud上的资源
/// 3.0.3 之后的版本已无效
@property (assign, nonatomic) BOOL downloadICloudAsset DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/// 是否过滤iCloud上的资源
/// 3.0.3 之后的版本已无效
@property (assign, nonatomic) BOOL filtrationICloudAsset DEPRECATED_MSG_ATTRIBUTE("Invalid attribute");
/// 列表小图照片清晰度 越大越清晰、越消耗性能
/// 设置太大的话获取图片资源时耗时长且内存消耗大可能会引起界面卡顿
@property (assign, nonatomic) CGFloat clarityScale;
#pragma mark - < block返回的视图 >
/// 设置导航栏
@property (copy, nonatomic) void (^navigationBar)(UINavigationBar *navigationBar, UIViewController *viewController);
/// 照片列表底部View
@property (copy, nonatomic) void (^photoListBottomView)(HXPhotoBottomView *bottomView);
/// 预览界面底部View
@property (copy, nonatomic) void (^previewBottomView)(HXPhotoPreviewBottomView *bottomView);
/// 相册列表的collectionView
/// 旋转屏幕时也会调用
@property (copy, nonatomic) void (^albumListCollectionView)(UICollectionView *collectionView);
/// 相册列表的tableView
/// 旋转屏幕时也会调用
@property (copy, nonatomic) void (^albumListTableView)(UITableView *tableView);
/// 弹窗样式的相册列表
/// 旋转屏幕时也会调用
@property (copy, nonatomic) void (^popupAlbumTableView)(UITableView *tableView);
/// 相片列表的collectionView
/// 旋转屏幕时也会调用
@property (copy, nonatomic) void (^photoListCollectionView)(UICollectionView *collectionView);
/// 预览界面的collectionView
/// 旋转屏幕时也会调用
@property (copy, nonatomic) void (^previewCollectionView)(UICollectionView *collectionView);
@end

View File

@ -0,0 +1,333 @@
//
// HXPhotoConfiguration.m
// HXPhotoPickerExample
//
// Created by Silence on 2017/11/21.
// Copyright © 2017 Silence. All rights reserved.
//
#import "HXPhotoConfiguration.h"
#import "HXPhotoTools.h"
#import "UIColor+HXExtension.h"
@implementation HXPhotoConfiguration
- (instancetype)init {
self = [super init];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
self.open3DTouchPreview = YES;
self.openCamera = YES;
self.lookLivePhoto = NO;
self.lookGifPhoto = YES;
self.selectTogether = NO;
self.showOriginalBytesLoading = NO;
self.exportVideoURLForHighestQuality = NO;
self.maxNum = 10;
self.photoMaxNum = 9;
self.videoMaxNum = 1;
self.showBottomPhotoDetail = YES;
self.videoMaximumSelectDuration = 3 * 60.f;
self.videoMinimumSelectDuration = 0.f;
self.videoMaximumDuration = 60.f;
self.videoMinimumDuration = 3.f;
if ([UIScreen mainScreen].bounds.size.width != 320 && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
self.cameraCellShowPreview = YES;
}
self.customAlbumName = [NSBundle mainBundle].infoDictionary[(NSString *)kCFBundleNameKey];
self.horizontalRowCount = 6;
self.supportRotation = YES;
self.pushTransitionDuration = 0.45f;
self.popTransitionDuration = 0.35f;
self.popInteractiveTransitionDuration = 0.45f;
self.doneBtnShowDetail = YES;
self.videoCanEdit = YES;
self.photoCanEdit = YES;
self.localFileName = @"HXPhotoPickerModelArray";
self.languageType = HXPhotoLanguageTypeSys;
self.popupTableViewCellHeight = 65.f;
if (HX_IS_IPhoneX_All) {
// self.editVideoExportPresetName = AVAssetExportPresetHighestQuality;
self.popupTableViewHeight = 450;
}else {
// self.editVideoExportPresetName = AVAssetExportPresetMediumQuality;
self.popupTableViewHeight = 350;
}
self.editVideoExportPreset = HXVideoEditorExportPresetRatio_960x540;
self.videoQuality = 6;
self.popupTableViewHorizontalHeight = 250;
self.albumShowMode = HXPhotoAlbumShowModeDefault;
self.cellDarkSelectTitleColor = [UIColor whiteColor];
self.cellDarkSelectBgColor = [UIColor colorWithRed:0.15 green:0.15 blue:0.15 alpha:1];
self.previewDarkSelectBgColor = [UIColor whiteColor];
self.previewDarkSelectTitleColor = [UIColor blackColor];
[HXPhotoCommon photoCommon].photoStyle = HXPhotoStyleDefault;
self.defaultFrontCamera = NO;
self.popupTableViewBgColor = [UIColor whiteColor];
self.albumListViewBgColor = [UIColor whiteColor];
self.photoListViewBgColor = [UIColor whiteColor];
self.previewPhotoViewBgColor = [UIColor whiteColor];
self.albumListViewCellBgColor = [UIColor whiteColor];
self.albumListViewCellTextColor = [UIColor blackColor];
self.albumListViewCellSelectBgColor = nil;
self.albumListViewCellLineColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.15];
self.photoListBottomPhotoCountTextColor = [UIColor colorWithRed:51.f / 255.f green:51.f / 255.f blue:51.f / 255.f alpha:1];
self.limitPhotoSize = 0;
self.limitVideoSize = 0;
self.selectPhotoLimitSize = NO;
self.selectVideoLimitSize = NO;
self.navBarTranslucent = YES;
self.bottomViewTranslucent = YES;
self.selectVideoBeyondTheLimitTimeAutoEdit = NO;
self.videoAutoPlayType = HXVideoAutoPlayTypeOnce;
self.previewSelectedBtnBgColor = self.themeColor;
self.changeOriginalTinColor = YES;
self.downloadNetworkVideo = YES;
self.cameraCanLocation = YES;
self.editAssetSaveSystemAblum = NO;
self.photoEditCustomRatios = @[@{@"原始值" : @"{0, 0}"}, @{@"正方形" : @"{1, 1}"}, @{@"2:3" : @"{2, 3}"}, @{@"3:4" : @"{3, 4}"}, @{@"9:16" : @"{9, 16}"}, @{@"16:9" : @"{16, 9}"}];
self.useWxPhotoEdit = YES;
if (HX_IS_IPhoneX_All) {
_clarityScale = 1.9;
}else {
_clarityScale = 1.5;
}
if ([UIScreen mainScreen].bounds.size.width == 320) {
self.rowCount = 3;
}else {
if ([HXPhotoTools isIphone6]) {
self.rowCount = 3;
}else {
self.rowCount = 4;
}
}
self.allowSlidingSelection = YES;
self.livePhotoAutoPlay = YES;
self.photoListLimitBlurStyle = UIBlurEffectStyleLight;
self.photoListLimitTextColor = [UIColor hx_colorWithHexStr:@"#666666"];
self.photoListLimitSettingColor = self.themeColor;
self.photoListLimitCloseColor = self.themeColor;
self.photoListLimitCellBackgroundColor = [UIColor hx_colorWithHexStr:@"#f1f1f1"];
self.photoListLimitCellBackgroundDarkColor = [UIColor hx_colorWithHexStr:@"#333333"];
self.photoListLimitCellLineColor = [UIColor hx_colorWithHexStr:@"#999999"];
self.photoListLimitCellLineDarkColor = [UIColor hx_colorWithHexStr:@"#ffffff"];
self.photoListLimitCellTextColor = [UIColor hx_colorWithHexStr:@"#999999"];
self.photoListLimitCellTextDarkColor = [UIColor hx_colorWithHexStr:@"#ffffff"];
self.photoListLimitCellTextFont = [UIFont hx_mediumPingFangOfSize:14];
}
- (void)setLivePhotoAutoPlay:(BOOL)livePhotoAutoPlay {
_livePhotoAutoPlay = livePhotoAutoPlay;
[HXPhotoCommon photoCommon].livePhotoAutoPlay = livePhotoAutoPlay;
}
- (UIColor *)cameraFocusBoxColor {
if (!_cameraFocusBoxColor) {
_cameraFocusBoxColor = [UIColor colorWithRed:0 green:0.47843137254901963 blue:1 alpha:1];
}
return _cameraFocusBoxColor;
}
- (void)setVideoAutoPlayType:(HXVideoAutoPlayType)videoAutoPlayType {
_videoAutoPlayType = videoAutoPlayType;
[HXPhotoCommon photoCommon].videoAutoPlayType = videoAutoPlayType;
}
- (void)setDownloadNetworkVideo:(BOOL)downloadNetworkVideo {
_downloadNetworkVideo = downloadNetworkVideo;
[HXPhotoCommon photoCommon].downloadNetworkVideo = downloadNetworkVideo;
}
- (void)setPhotoStyle:(HXPhotoStyle)photoStyle {
_photoStyle = photoStyle;
[HXPhotoCommon photoCommon].photoStyle = photoStyle;
}
- (void)setCreationDateSort:(BOOL)creationDateSort {
if ([HXPhotoCommon photoCommon].creationDateSort != creationDateSort) {
[HXPhotoCommon photoCommon].cameraRollResult = nil;
}
_creationDateSort = creationDateSort;
[HXPhotoCommon photoCommon].creationDateSort = creationDateSort;
}
- (void)setLanguageType:(HXPhotoLanguageType)languageType {
if ([HXPhotoCommon photoCommon].languageType != languageType) {
[HXPhotoCommon photoCommon].languageBundle = nil;
}
_languageType = languageType;
[HXPhotoCommon photoCommon].languageType = languageType;
}
- (void)setClarityScale:(CGFloat)clarityScale {
if (clarityScale <= 0.f) {
if (HX_IS_IPhoneX_All) {
_clarityScale = 1.9;
}else {
_clarityScale = 1.5;
}
}else {
_clarityScale = clarityScale;
}
CGFloat width = ([UIScreen mainScreen].bounds.size.width - 1 * self.rowCount - 1 ) / self.rowCount;
[HXPhotoCommon photoCommon].requestWidth = width * clarityScale;
}
- (void)setRowCount:(NSUInteger)rowCount {
_rowCount = rowCount;
CGFloat width = ([UIScreen mainScreen].bounds.size.width - 1 * rowCount - 1 ) / rowCount;
[HXPhotoCommon photoCommon].requestWidth = width * self.clarityScale;
}
- (UIColor *)themeColor {
if (!_themeColor) {
_themeColor = [UIColor colorWithRed:0 green:0.47843137254901963 blue:1 alpha:1];
}
return _themeColor;
}
- (NSString *)originalNormalImageName {
if (!_originalNormalImageName) {
_originalNormalImageName = @"hx_original_normal";
}
return _originalNormalImageName;
}
- (NSString *)originalSelectedImageName {
if (!_originalSelectedImageName) {
_originalSelectedImageName = @"hx_original_selected";
}
return _originalSelectedImageName;
}
- (void)setVideoMaximumSelectDuration:(NSInteger)videoMaximumSelectDuration {
if (videoMaximumSelectDuration <= 0) {
videoMaximumSelectDuration = MAXFLOAT;
}
_videoMaximumSelectDuration = videoMaximumSelectDuration;
}
- (void)setVideoMaximumDuration:(NSTimeInterval)videoMaximumDuration {
if (videoMaximumDuration <= self.videoMinimumDuration) {
videoMaximumDuration = self.videoMinimumDuration + 1.f;
}
_videoMaximumDuration = videoMaximumDuration;
}
- (CGPoint)movableCropBoxCustomRatio {
return _movableCropBoxCustomRatio;
}
- (NSInteger)minVideoClippingTime {
if (_minVideoClippingTime < 1) {
_minVideoClippingTime = 1;
}
return _minVideoClippingTime;
}
- (NSInteger)maxVideoClippingTime {
if (!_maxVideoClippingTime) {
_maxVideoClippingTime = 15;
}
return _maxVideoClippingTime;
}
- (UIColor *)authorizationTipColor {
if (!_authorizationTipColor) {
_authorizationTipColor = [UIColor blackColor];
}
return _authorizationTipColor;
}
- (void)setType:(HXConfigurationType)type {
_type = type;
if (type == HXConfigurationTypeWXChat) {
[self setWxConfiguration];
self.videoMaximumSelectDuration = 60.f * 5.f;
self.selectVideoBeyondTheLimitTimeAutoEdit = NO;
self.photoMaxNum = 0;
self.videoMaxNum = 0;
self.maxNum = 9;
self.selectTogether = YES;
}else if (type == HXConfigurationTypeWXMoment) {
[self setWxConfiguration];
self.videoMaximumDuration = 15;
self.videoMaximumSelectDuration = 15;
self.selectVideoBeyondTheLimitTimeAutoEdit = YES;
self.photoMaxNum = 9;
self.videoMaxNum = 1;
self.maxNum = 9;
self.selectTogether = NO;
}
}
- (void)setWxConfiguration {
self.videoCanEdit = YES;
self.specialModeNeedHideVideoSelectBtn = YES;
self.cameraPhotoJumpEdit = YES;
self.saveSystemAblum = YES;
self.albumShowMode = HXPhotoAlbumShowModePopup;
self.photoListCancelLocation = HXPhotoListCancelButtonLocationTypeLeft;
self.cameraCellShowPreview = NO;
//
self.changeOriginalTinColor = NO;
self.originalNormalImageName = @"hx_original_normal_wx";
self.originalSelectedImageName = @"hx_original_selected_wx";
//
UIColor *wxColor = [UIColor hx_colorWithHexStr:@"#07C160"];
self.statusBarStyle = UIStatusBarStyleLightContent;
self.themeColor = [UIColor whiteColor];
self.photoEditConfigur.themeColor = wxColor;
self.previewBottomSelectColor = wxColor;
self.navBarBackgroudColor = nil;
self.navBarStyle = UIBarStyleBlack;
self.navigationTitleArrowColor = [UIColor hx_colorWithHexStr:@"#B2B2B2"];
self.navigationTitleArrowDarkColor = [UIColor hx_colorWithHexStr:@"#B2B2B2"];
self.cameraFocusBoxColor = wxColor;
self.authorizationTipColor = [UIColor whiteColor];
self.navigationTitleSynchColor = YES;
self.cellSelectedBgColor = wxColor;
self.cellSelectedTitleColor = [UIColor whiteColor];
self.cellDarkSelectBgColor = wxColor;
self.cellDarkSelectTitleColor = [UIColor whiteColor];
self.photoListLimitBlurStyle = UIBlurEffectStyleDark;
self.photoListLimitTextColor = [UIColor hx_colorWithHexStr:@"#999999"];
self.photoListLimitSettingColor = wxColor;
self.photoListLimitCloseColor = [UIColor whiteColor];
self.previewSelectedBtnBgColor = wxColor;
self.selectedTitleColor = [UIColor whiteColor];
self.previewDarkSelectBgColor = wxColor;
self.previewDarkSelectTitleColor = [UIColor whiteColor];
self.bottomDoneBtnBgColor = wxColor;
self.bottomDoneBtnDarkBgColor = wxColor;
self.bottomDoneBtnEnabledBgColor = [[UIColor hx_colorWithHexStr:@"#666666"] colorWithAlphaComponent:0.3];
self.bottomDoneBtnTitleColor = [UIColor whiteColor];
// self.bottomViewBgColor = [UIColor hx_colorWithHexStr:@"#141414"];
self.bottomViewBgColor = nil;
self.bottomViewBarStyle = UIBarStyleBlack;
self.popupTableViewCellBgColor = [UIColor hx_colorWithHexStr:@"#2E2F30"];
self.popupTableViewCellLineColor = [[UIColor hx_colorWithHexStr:@"#434344"] colorWithAlphaComponent:0.6];
self.popupTableViewCellSelectColor = [UIColor clearColor];
self.popupTableViewCellSelectIconColor = wxColor;
self.popupTableViewCellHighlightedColor = [UIColor colorWithRed:0.125 green:0.125 blue:0.125 alpha:1];
self.popupTableViewBgColor = [UIColor colorWithRed:0.125 green:0.125 blue:0.125 alpha:1];
self.popupTableViewCellPhotoCountColor = [UIColor whiteColor];
self.popupTableViewCellAlbumNameColor = [UIColor whiteColor];
self.popupTableViewHeight = HX_ScreenHeight * 0.65;
self.albumListViewBgColor = [UIColor hx_colorWithHexStr:@"#2E2F30"];
self.albumListViewCellBgColor = [UIColor hx_colorWithHexStr:@"#2E2F30"];
self.albumListViewCellSelectBgColor = [UIColor colorWithRed:0.125 green:0.125 blue:0.125 alpha:1];
self.albumListViewCellLineColor = [[UIColor hx_colorWithHexStr:@"#434344"] colorWithAlphaComponent:0.6];
self.albumListViewCellTextColor = [UIColor whiteColor];
self.photoListViewBgColor = [UIColor hx_colorWithHexStr:@"#2E2F30"];
self.photoListBottomPhotoCountTextColor = [UIColor whiteColor];
self.previewPhotoViewBgColor = [UIColor blackColor];
self.photoListLimitCellBackgroundColor = [UIColor hx_colorWithHexStr:@"#383838"];
self.photoListLimitCellLineColor = [UIColor hx_colorWithHexStr:@"#ffffff"];
self.photoListLimitCellTextColor = [UIColor hx_colorWithHexStr:@"#ffffff"];
}
- (HXPhotoEditConfiguration *)photoEditConfigur {
if (!_photoEditConfigur) {
_photoEditConfigur = [[HXPhotoEditConfiguration alloc] init];
_photoEditConfigur.themeColor = self.themeColor;
_photoEditConfigur.supportRotation = self.supportRotation;
}
return _photoEditConfigur;
}
@end

View File

@ -0,0 +1,228 @@
//
// HXPhotoDefine.h
// HXPhotoPickerExample
//
// Created by Silence on 2017/11/24.
// Copyright © 2017年 Silence. All rights reserved.
//
#ifndef HXPhotoDefine_h
#define HXPhotoDefine_h
#import <CommonCrypto/CommonDigest.h>
#import "NSBundle+HXPhotoPicker.h"
/// 当前版本
#define HXVersion @"3.3.2"
// 日志输出
#ifdef DEBUG
#define NSSLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSSLog(...)
#endif
/// 如果想要HXPhotoView的item大小自定义设置请修改为 1
/// 如果为pod导入的话请使用 pod 'HXPhotoPicker/CustomItem'
/// 并且实现HXPhotoView的代理
/// - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath isAddItem:(BOOL)isAddItem photoView:(HXPhotoView *)photoView
/// 如果不实现此代理item的小大将默认 (100, 100)
#define HXPhotoViewCustomItemSize 0
#define HXRound(x) (round(x*100000)/100000)
#define HXRoundHundreds(x) (round(x*100)/100)
#define HXRoundDecade(x) (round(x*10)/10)
#define HXRoundFrame(rect) CGRectMake(HXRound(rect.origin.x), HXRound(rect.origin.y), HXRound(rect.size.width), HXRound(rect.size.height))
#define HXRoundFrameHundreds(rect) CGRectMake(HXRoundHundreds(rect.origin.x), HXRoundHundreds(rect.origin.y), HXRoundHundreds(rect.size.width), HXRoundHundreds(rect.size.height))
#define HXEncodeKey @"HXModelArray"
#define HXCameraImageKey @"HXCameraImageURLKey"
#define HXPhotoPickerLibraryCaches [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]
#define HXPhotoPickerDocuments [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
//#define HXPhotoPickerLocalModelsPath [HXPhotoPickerDocuments stringByAppendingPathComponent:[NSString stringWithFormat:@"%@/localModels", HXPhotoHeaderSearchPath]]
#define HXPhotoPickerLocalModelsPath [HXPhotoPickerLibraryCaches stringByAppendingPathComponent:@"localModels"]
#define HXPhotoHeaderSearchPath @"com.silence.hxphotopicker"
#define HXPhotoPickerAssetCachesPath [HXPhotoPickerLibraryCaches stringByAppendingPathComponent:HXPhotoHeaderSearchPath]
#define HXPhotoPickerCachesDownloadPath [HXPhotoPickerAssetCachesPath stringByAppendingPathComponent:@"download"]
#define HXPhotoPickerDownloadVideosPath [HXPhotoPickerCachesDownloadPath stringByAppendingPathComponent:@"videos"]
#define HXPhotoPickerDownloadPhotosPath [HXPhotoPickerCachesDownloadPath stringByAppendingPathComponent:@"photos"]
#define HXPhotoPickerCachesLivePhotoPath [HXPhotoPickerAssetCachesPath stringByAppendingPathComponent:@"LivePhoto"]
#define HXPhotoPickerLivePhotoVideosPath [HXPhotoPickerCachesLivePhotoPath stringByAppendingPathComponent:@"videos"]
#define HXPhotoPickerLivePhotoImagesPath [HXPhotoPickerCachesLivePhotoPath stringByAppendingPathComponent:@"images"]
#define HXShowLog NO
#define HX_UI_IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define HX_ALLOW_LOCATION ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] || [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"])
#define HX_PREFERS_STATUS_BAR_HIDDEN [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"] boolValue]
#define HasAFNetworking (__has_include(<AFNetworking/AFNetworking.h>) || __has_include("AFNetworking.h"))
#define HasYYWebImage (__has_include(<YYWebImage/YYWebImage.h>) || __has_include("YYWebImage.h"))
#define HasYYKit (__has_include(<YYKit/YYKit.h>) || __has_include("YYKit.h"))
#define HasYYKitOrWebImage (__has_include(<YYWebImage/YYWebImage.h>) || __has_include("YYWebImage.h") || __has_include(<YYKit/YYKit.h>) || __has_include("YYKit.h"))
#define HasSDWebImage (__has_include(<SDWebImage/UIImageView+WebCache.h>) || __has_include("UIImageView+WebCache.h"))
#define HX_ScreenWidth [UIScreen mainScreen].bounds.size.width
#define HX_ScreenHeight [UIScreen mainScreen].bounds.size.height
#define HX_IS_IPHONEX (CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(375, 812)) || CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(812, 375)) || CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(414, 896)) || CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(896, 414)))
// 判断iPhone X
#define HX_Is_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
//判断iPHoneXr
#define HX_Is_iPhoneXR ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(828, 1792), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
//判断iPHoneXs
#define HX_Is_iPhoneXS ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
//判断iPhoneXs Max
#define HX_Is_iPhoneXS_MAX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2688), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
//判断iPHone12 mini
#define HX_Is_iPhoneTwelveMini ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1080, 2340), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
//判断iPHone12 和 iPHone12 Pro
#define HX_Is_iPhoneTwelvePro ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1170, 2532), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
//判断iPHone12 ProMax
#define HX_Is_iPhoneTwelveProMax ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1284, 2778), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
#define HX_Is_iPhone14ProMax ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1290, 2796), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
#define HX_Is_iPhone14Pro ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1179, 2556), [[UIScreen mainScreen] currentMode].size) && !HX_UI_IS_IPAD : NO)
#define HX_IS_IPhoneX_All (HX_Is_iPhoneX || HX_Is_iPhoneXR || HX_Is_iPhoneXS || HX_Is_iPhoneXS_MAX || HX_IS_IPHONEX || HX_Is_iPhoneTwelveMini || HX_Is_iPhoneTwelvePro || HX_Is_iPhoneTwelveProMax || HX_Is_iPhone14Pro || HX_Is_iPhone14ProMax)
// 导航栏 + 状态栏 的高度
#define hxNavigationBarHeight ((HX_UI_IS_IPAD ? 50 : 44) + HXStatusBarHeight)
#define hxTopMargin (HX_IS_IPhoneX_All ? 44 : 0)
#define hxBottomMargin (HX_IS_IPhoneX_All ? 34 : 0)
#define HXStatusBarHeight [HXPhotoTools getStatusBarHeight]
#define HX_IOS14_Later ([UIDevice currentDevice].systemVersion.floatValue >= 14.0f)
#define HX_IOS13_Later ([UIDevice currentDevice].systemVersion.floatValue >= 13.0f)
#define HX_IOS11_Later ([UIDevice currentDevice].systemVersion.floatValue >= 11.0f)
#define HX_IOS11_Earlier ([UIDevice currentDevice].systemVersion.floatValue < 11.0f)
#define HX_IOS10_Later ([UIDevice currentDevice].systemVersion.floatValue >= 10.0f)
#define HX_IOS91Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.1f)
#define HX_IOS9Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.0f)
#define HX_IOS82Later ([UIDevice currentDevice].systemVersion.floatValue >= 8.2f)
#define HX_IOS9Earlier ([UIDevice currentDevice].systemVersion.floatValue < 9.0f)
// 弱引用
#define HXWeakSelf __weak typeof(self) weakSelf = self;
// 强引用
#define HXStrongSelf __strong typeof(weakSelf) strongSelf = weakSelf;
#pragma mark - Hash
#define HX_MAX_FILE_EXTENSION_LENGTH (NAME_MAX - CC_MD5_DIGEST_LENGTH * 2 - 1)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
static inline NSString * _Nonnull HXDiskCacheFileNameForKey(NSString * _Nullable key, BOOL addExt) {
const char *str = key.UTF8String;
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSURL *keyURL = [NSURL URLWithString:key];
NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
// File system has file name length limit, we need to check if ext is too long, we don't add it to the filename
if (ext.length > HX_MAX_FILE_EXTENSION_LENGTH) {
ext = nil;
}
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15]];
if (addExt) {
filename = [filename stringByAppendingFormat:@"%@", ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
}
return filename;
}
#pragma clang diagnostic pop
CG_INLINE UIAlertController * _Nullable hx_showAlert(UIViewController * _Nullable vc,
NSString * _Nullable title,
NSString * _Nullable message,
NSString * _Nullable buttonTitle1,
NSString * _Nullable buttonTitle2,
dispatch_block_t _Nullable buttonTitle1Handler,
dispatch_block_t _Nullable buttonTitle2Handler) {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
UIPopoverPresentationController *pop = [alertController popoverPresentationController];
pop.permittedArrowDirections = UIPopoverArrowDirectionAny;
pop.sourceView = vc.view;
pop.sourceRect = vc.view.bounds;
}
if (buttonTitle1) {
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:buttonTitle1
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
if (buttonTitle1Handler) buttonTitle1Handler();
}];
[alertController addAction:cancelAction];
}
if (buttonTitle2) {
UIAlertAction *okAction = [UIAlertAction actionWithTitle:buttonTitle2
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
if (buttonTitle2Handler) buttonTitle2Handler();
}];
[alertController addAction:okAction];
}
[vc presentViewController:alertController animated:YES completion:nil];
return alertController;
}
#define HXAlbumCameraRoll @"HXAlbumCameraRoll"
#define HXAlbumPanoramas @"HXAlbumPanoramas"
#define HXAlbumVideos @"HXAlbumVideos"
#define HXAlbumFavorites @"HXAlbumFavorites"
#define HXAlbumTimelapses @"HXAlbumTimelapses"
#define HXAlbumRecentlyAdded @"HXAlbumRecentlyAdded"
#define HXAlbumRecents @"HXAlbumRecents"
#define HXAlbumBursts @"HXAlbumBursts"
#define HXAlbumSlomoVideos @"HXAlbumSlomoVideos"
#define HXAlbumSelfPortraits @"HXAlbumSelfPortraits"
#define HXAlbumScreenshots @"HXAlbumScreenshots"
#define HXAlbumDepthEffect @"HXAlbumDepthEffect"
#define HXAlbumLivePhotos @"HXAlbumLivePhotos"
#define HXAlbumAnimated @"HXAlbumAnimated"
#endif /* HXPhotoDefine_h */

View File

@ -0,0 +1,87 @@
//
// HX_PhotoEditViewController.h
// photoEditDemo
//
// Created by Silence on 2020/6/20.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "HXPhotoEdit.h"
#import "HXPhotoEditConfiguration.h"
NS_ASSUME_NONNULL_BEGIN
@class HX_PhotoEditViewController, HXPhotoModel;
typedef void (^ HX_PhotoEditViewControllerDidFinishBlock)(HXPhotoEdit * _Nullable photoEdit, HXPhotoModel *photoModel, HX_PhotoEditViewController *viewController);
typedef void (^ HX_PhotoEditViewControllerDidCancelBlock)(HX_PhotoEditViewController *viewController);
@protocol HX_PhotoEditViewControllerDelegate <NSObject>
@optional
/// 照片编辑完成
/// @param photoEditingVC 编辑控制器
/// @param photoEdit 编辑完之后的数据如果为nil。则未处理
- (void)photoEditingController:(HX_PhotoEditViewController *)photoEditingVC
didFinishPhotoEdit:(HXPhotoEdit * _Nullable)photoEdit
photoModel:(HXPhotoModel *)photoModel;
/// 取消编辑
/// @param photoEditingVC 编辑控制器
- (void)photoEditingControllerDidCancel:(HX_PhotoEditViewController *)photoEditingVC;
@end
@interface HX_PhotoEditViewController : UIViewController<UIViewControllerTransitioningDelegate>
@property (strong, nonatomic) HXPhotoModel *photoModel;
/// 编辑的数据
/// 传入之前的编辑数据可以在原有基础上继续编辑
@property (strong, nonatomic) HXPhotoEdit *photoEdit;
/// 编辑原图
@property (strong, nonatomic) UIImage *editImage;
/// 编辑配置
@property (strong, nonatomic) HXPhotoEditConfiguration *configuration;
/// 只要裁剪
@property (assign, nonatomic) BOOL onlyCliping;
/// 是否保存到系统相册
/// 当保存系统相册时photoEdit会为空
@property (assign, nonatomic) BOOL saveAlbum;
/// 保存到自定义相册的名称
@property (copy, nonatomic) NSString *albumName;
/// 照片定位信息
@property (strong, nonatomic) CLLocation *location;
/// 是否支持旋转,优先级比 configuration.supportRotation 高
@property (assign, nonatomic) BOOL supportRotation;
@property (weak, nonatomic) id<HX_PhotoEditViewControllerDelegate> delegate;
@property (copy, nonatomic) HX_PhotoEditViewControllerDidFinishBlock finishBlock;
@property (copy, nonatomic) HX_PhotoEditViewControllerDidCancelBlock cancelBlock;
- (instancetype)initWithConfiguration:(HXPhotoEditConfiguration *)configuration;
- (instancetype)initWithPhotoEdit:(HXPhotoEdit *)photoEdit
configuration:(HXPhotoEditConfiguration *)configuration;
- (instancetype)initWithEditImage:(UIImage *)editImage
configuration:(HXPhotoEditConfiguration *)configuration;
#pragma mark - < other >
@property (assign, nonatomic) BOOL imageRequestComplete;
@property (assign, nonatomic) BOOL transitionCompletion;
@property (assign, nonatomic) BOOL isCancel;
- (CGRect)getImageFrame;
- (void)showBgViews;
- (void)completeTransition:(UIImage *)image;
- (CGRect)getDismissImageFrame;
- (UIImage *)getCurrentImage;
- (void)hideImageView;
- (void)hiddenTopBottomView;
- (void)showTopBottomView;
@property (assign, nonatomic) BOOL isAutoBack;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
//
// HXPhotoEdit.h
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEdit : NSObject<NSCoding>
/// 编辑封面
@property (nonatomic, readonly) UIImage *editPosterImage;
/// 编辑预览图片
@property (nonatomic, readonly) UIImage *editPreviewImage;
/// 编辑图片数据
@property (nonatomic, readonly) NSData *editPreviewData;
/// 编辑原图片本地临时地址
@property (nonatomic, readonly) NSString *imagePath;
/// 编辑数据
@property (nonatomic, readonly) NSDictionary *editData;
- (instancetype)initWithEditImagePath:(NSString *)imagePath previewImage:(UIImage *)previewImage data:(NSDictionary *)data;
- (void)clearData;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,80 @@
//
// HXPhotoEdit.m
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEdit.h"
#import "UIImage+HXExtension.h"
#import "HXMECancelBlock.h"
@interface HXPhotoEdit ()
///
@property (nonatomic, strong) UIImage *editPosterImage;
///
@property (nonatomic, strong) UIImage *editPreviewImage;
///
@property (nonatomic, strong) NSData *editPreviewData;
///
@property (nonatomic, copy) NSString *imagePath;
///
@property (nonatomic, copy) NSDictionary *editData;
@end
@implementation HXPhotoEdit
- (instancetype)initWithEditImagePath:(NSString *)imagePath previewImage:(UIImage *)previewImage data:(NSDictionary *)data {
self = [super init];
if (self) {
if (!previewImage) {
NSData *data = [NSData dataWithContentsOfFile:imagePath];
previewImage = [UIImage imageWithData:data];
}
[self setEditingImage:previewImage];
_imagePath = imagePath;
_editData = data;
}
return self;
}
#pragma mark - private
- (void)clearData {
self.editPreviewImage = nil;
self.editPosterImage = nil;
self.imagePath = nil;
self.editData = nil;
}
- (void)setEditingImage:(UIImage *)editPreviewImage {
_editPreviewImage = editPreviewImage;
/** */
CGFloat width = MIN(80.f * 2.f, MIN(editPreviewImage.size.width, editPreviewImage.size.height));
CGSize size = [UIImage hx_scaleImageSizeBySize:editPreviewImage.size targetSize:CGSizeMake(width, width) isBoth:YES];
_editPosterImage = [editPreviewImage hx_scaleToFitSize:size];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.editPosterImage = [aDecoder decodeObjectForKey:@"editPosterImage"];
self.editPreviewImage = [aDecoder decodeObjectForKey:@"editPreviewImage"];
self.editPreviewData = [aDecoder decodeObjectForKey:@"editPreviewData"];
self.imagePath = [aDecoder decodeObjectForKey:@"imagePath"];
self.editData = [aDecoder decodeObjectForKey:@"editData"];
}
return self;
}
- (NSData *)editPreviewData {
if (!_editPreviewData) {
_editPreviewData = HX_UIImageJPEGRepresentation(self.editPreviewImage);
}
return _editPreviewData;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.editPosterImage forKey:@"editPosterImage"];
[aCoder encodeObject:self.editPreviewImage forKey:@"editPreviewImage"];
[aCoder encodeObject:self.editPreviewData forKey:@"editPreviewData"];
[aCoder encodeObject:self.imagePath forKey:@"imagePath"];
[aCoder encodeObject:self.editData forKey:@"editData"];
}
@end

View File

@ -0,0 +1,90 @@
//
// HXPhotoEditConfiguration.h
// photoEditDemo
//
// Created by Silence on 2020/7/6.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoEditChartletModel.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, HXPhotoEditAspectRatio) {
HXPhotoEditAspectRatioType_None, // 不设置比例
HXPhotoEditAspectRatioType_Original, // 原图比例
HXPhotoEditAspectRatioType_1x1,
HXPhotoEditAspectRatioType_3x2,
HXPhotoEditAspectRatioType_4x3,
HXPhotoEditAspectRatioType_5x3,
HXPhotoEditAspectRatioType_15x9,
HXPhotoEditAspectRatioType_16x9,
HXPhotoEditAspectRatioType_16x10,
HXPhotoEditAspectRatioType_Custom // 自定义比例
};
@interface HXPhotoEditConfiguration : NSObject
/// 主题色
@property (strong, nonatomic) UIColor *themeColor;
/// 只要裁剪功能
@property (assign, nonatomic) BOOL onlyCliping;
/// 是否支持旋转
/// 旋转之后会重置编辑的内容
@property (assign, nonatomic) BOOL supportRotation;
/// 不可旋转时支持的方向
@property (assign, nonatomic) UIInterfaceOrientationMask supportedInterfaceOrientations;
#pragma mark - < 画笔相关 >
/// 画笔颜色数组
@property (copy, nonatomic) NSArray<UIColor *> *drawColors;
/// 默认选择的画笔颜色下标
/// 与 drawColors 对应,默认 2
@property (assign, nonatomic) NSInteger defaultDarwColorIndex;
/// 画笔默认宽度为最大宽度的50%
/// 画笔最大宽度
/// 默认 12.f
@property (assign, nonatomic) CGFloat brushLineMaxWidth;
/// 画笔最小宽度
/// 默认 2.f
@property (assign, nonatomic) CGFloat brushLineMinWidth;
#pragma mark - < 贴图相关 >
/// 贴图模型数组
@property (copy, nonatomic) NSArray<HXPhotoEditChartletTitleModel *> *chartletModels;
/// 请求获取贴图模型
/// block会在贴图列表弹出之后调用
/// 优先级高于 chartletModels
@property (copy, nonatomic) void (^ requestChartletModels)(void(^ chartletModels)(NSArray<HXPhotoEditChartletTitleModel *> *chartletModels));
#pragma mark - < 文字贴图相关 >
/// 文字颜色数组
@property (copy, nonatomic) NSArray<UIColor *> *textColors;
/// 文字字体
@property (strong, nonatomic) UIFont *textFont;
/// 最大文本长度限制
@property (assign, nonatomic) NSInteger maximumLimitTextLength;
#pragma mark - < 裁剪相关 >
/// 固定裁剪比例
@property (assign, nonatomic) HXPhotoEditAspectRatio aspectRatio;
/// 自定义固定比例
/// 设置自定义比例必须设置 aspectRatio = HXPhotoEditAspectRatioType_Custom否则无效
@property (assign, nonatomic) CGSize customAspectRatio;
/// 圆形裁剪框,只要裁剪功能 并且 固定裁剪比例为 HXPhotoEditAspectRatioType_1x1 时有效
@property (assign, nonatomic) BOOL isRoundCliping;
@property (assign, nonatomic) CGSize clippingMinSize;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,75 @@
//
// HXPhotoEditConfiguration.m
// photoEditDemo
//
// Created by Silence on 2020/7/6.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditConfiguration.h"
#import "UIColor+HXExtension.h"
@implementation HXPhotoEditConfiguration
- (instancetype)init {
self = [super init];
if (self) {
self.maximumLimitTextLength = 0;
self.supportRotation = YES;
self.clippingMinSize = CGSizeMake(80, 80);
self.supportedInterfaceOrientations = UIInterfaceOrientationMaskPortrait;
}
return self;
}
- (CGFloat)brushLineMaxWidth {
if (!_brushLineMaxWidth) {
_brushLineMaxWidth = 12.f;
}
return _brushLineMaxWidth;
}
- (CGFloat)brushLineMinWidth {
if (!_brushLineMinWidth) {
_brushLineMinWidth = 3.f;
}
return _brushLineMinWidth;
}
- (UIColor *)themeColor {
if (!_themeColor) {
_themeColor = [UIColor hx_colorWithHexStr:@"#07C160"];
}
return _themeColor;
}
- (NSArray<UIColor *> *)drawColors {
if (!_drawColors) {
_drawColors = @[[UIColor hx_colorWithHexStr:@"#ffffff"], [UIColor hx_colorWithHexStr:@"#2B2B2B"], [UIColor hx_colorWithHexStr:@"#FA5150"], [UIColor hx_colorWithHexStr:@"#FEC200"], [UIColor hx_colorWithHexStr:@"#07C160"], [UIColor hx_colorWithHexStr:@"#10ADFF"], [UIColor hx_colorWithHexStr:@"#6467EF"]];
self.defaultDarwColorIndex = 2;
}
return _drawColors;
}
- (NSArray<UIColor *> *)textColors {
if (!_textColors) {
_textColors = @[[UIColor hx_colorWithHexStr:@"#ffffff"], [UIColor hx_colorWithHexStr:@"#2B2B2B"], [UIColor hx_colorWithHexStr:@"#FA5150"], [UIColor hx_colorWithHexStr:@"#FEC200"], [UIColor hx_colorWithHexStr:@"#07C160"], [UIColor hx_colorWithHexStr:@"#10ADFF"], [UIColor hx_colorWithHexStr:@"#6467EF"]];
}
return _textColors;
}
- (UIFont *)textFont {
if (!_textFont) {
_textFont = [UIFont boldSystemFontOfSize:25];
}
return _textFont;
}
- (NSArray<HXPhotoEditChartletTitleModel *> *)chartletModels {
if (!_chartletModels) {
HXPhotoEditChartletTitleModel *netModel = [HXPhotoEditChartletTitleModel modelWithImageNamed:@"hx_sticker_cover"];
NSArray *imageNames = @[@"chongya", @"jintianfenkeai", @"keaibiaoq", @"saihong", @"xiaochuzhujiao", @"yuanqimanman", @"yuanqishaonv", @"zaizaijia", @"haoxinqing", @"housailei", @"kehaixing", @"wow", @"woxiangfazipai" ];
NSMutableArray *models = [NSMutableArray array];
for (NSString *imageNamed in imageNames) {
HXPhotoEditChartletModel *subModel = [HXPhotoEditChartletModel modelWithImageNamed:[NSString stringWithFormat:@"hx_sticker_%@", imageNamed]];
[models addObject:subModel];
}
netModel.models = models.copy;
_chartletModels = @[netModel];
}
return _chartletModels;
}
@end

View File

@ -0,0 +1,65 @@
//
// HXMECancelBlock.h
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^hx_me_dispatch_cancelable_block_t)(BOOL cancel);
OBJC_EXTERN hx_me_dispatch_cancelable_block_t hx_dispatch_block_t(NSTimeInterval delay, void(^block)(void));
OBJC_EXTERN void hx_me_dispatch_cancel(hx_me_dispatch_cancelable_block_t block);
OBJC_EXTERN double const HXMediaEditMinRate;
OBJC_EXTERN double const HXMediaEditMaxRate;
OBJC_EXTERN CGRect HXMediaEditProundRect(CGRect rect);
extern __attribute__((overloadable)) NSData * _Nullable HX_UIImagePNGRepresentation(UIImage * image);
extern __attribute__((overloadable)) NSData * _Nullable HX_UIImageJPEGRepresentation(UIImage * image);
extern __attribute__((overloadable)) NSData * _Nullable HX_UIImageRepresentation(UIImage * image, CFStringRef __nonnull type, NSError * _Nullable __autoreleasing * _Nullable error);
/**
@param imageRef
@param size contentMode缩放图片CGSizeZero不处理大小
@param contentMode UIViewContentModeScaleAspectFill与UIViewContentModeScaleAspectFitsize搭配
@param orientation imageRef的方向upup则不更正
@return NULL
*/
CG_EXTERN CGImageRef _Nullable HX_CGImageScaleDecodedFromCopy(CGImageRef imageRef, CGSize size, UIViewContentMode contentMode, UIImageOrientation orientation);
/**
@param imageRef
@return NULL
*/
CG_EXTERN CGImageRef _Nullable HX_CGImageDecodedFromCopy(CGImageRef imageRef);
/**
@param image
@return NULL
*/
CG_EXTERN CGImageRef _Nullable HX_CGImageDecodedCopy(UIImage *image);
/**
@param image
@return
*/
UIKIT_EXTERN UIImage * HX_UIImageDecodedCopy(UIImage *image);
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,274 @@
//
// HXMECancelBlock.m
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXMECancelBlock.h"
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/UTCoreTypes.h>
hx_me_dispatch_cancelable_block_t hx_dispatch_block_t(NSTimeInterval delay, void(^block)(void))
{
__block hx_me_dispatch_cancelable_block_t cancelBlock = nil;
hx_me_dispatch_cancelable_block_t delayBlcok = ^(BOOL cancel){
if (!cancel) {
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
if (cancelBlock) {
cancelBlock = nil;
}
};
cancelBlock = delayBlcok;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (cancelBlock) {
cancelBlock(NO);
}
});
return delayBlcok;
}
void hx_me_dispatch_cancel(hx_me_dispatch_cancelable_block_t block)
{
if (block) {
block(YES);
}
}
double const HXMediaEditMinRate = 0.5f;
double const HXMediaEditMaxRate = 2.f;
CGRect HXMediaEditProundRect(CGRect rect)
{
rect.origin.x = ((int)(rect.origin.x + 0.5) * 1.f);
rect.origin.y = ((int)(rect.origin.y + 0.5) * 1.f);
rect.size.width = ((int)(rect.size.width + 0.5) * 1.f);
rect.size.height = ((int)(rect.size.height + 0.5) * 1.f);
return rect;
}
__attribute__((overloadable)) NSData * HX_UIImagePNGRepresentation(UIImage *image) {
return HX_UIImageRepresentation(image, kUTTypePNG, nil);
}
__attribute__((overloadable)) NSData * HX_UIImageJPEGRepresentation(UIImage *image) {
return HX_UIImageRepresentation(image, kUTTypeJPEG, nil);
}
__attribute__((overloadable)) NSData * HX_UIImageRepresentation(UIImage *image, CFStringRef __nonnull type, NSError * __autoreleasing *error) {
if (!image) {
return nil;
}
NSDictionary *userInfo = nil;
{
NSMutableData *mutableData = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)mutableData, type, 1, NULL);
CGImageDestinationAddImage(destination, [image CGImage], NULL);
BOOL success = CGImageDestinationFinalize(destination);
CFRelease(destination);
if (!success) {
userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Could not finalize image destination", nil)
};
goto _error;
}
return [NSData dataWithData:mutableData];
}
_error: {
if (error) {
*error = [[NSError alloc] initWithDomain:@"com.compuserve.image.error" code:-1 userInfo:userInfo];
}
return nil;
}
}
inline static CGAffineTransform HX_CGAffineTransformExchangeOrientation(UIImageOrientation imageOrientation, CGSize size)
{
CGAffineTransform transform = CGAffineTransformIdentity;
switch (imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, size.width, size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
return transform;
}
CGImageRef HX_CGImageScaleDecodedFromCopy(CGImageRef imageRef, CGSize size, UIViewContentMode contentMode, UIImageOrientation orientation)
{
CGImageRef newImage = NULL;
@autoreleasepool {
if (!imageRef) return NULL;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
if (width == 0 || height == 0) return NULL;
switch (orientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
{
CGFloat tmpWidth = width;
width = height;
height = tmpWidth;
}
break;
default:
break;
}
if (size.width > 0 && size.height > 0) {
float verticalRadio = size.height*1.0/height;
float horizontalRadio = size.width*1.0/width;
float radio = 1;
if (contentMode == UIViewContentModeScaleAspectFill) {
if(verticalRadio > horizontalRadio)
{
radio = verticalRadio;
}
else
{
radio = horizontalRadio;
}
} else if (contentMode == UIViewContentModeScaleAspectFit) {
if(verticalRadio < horizontalRadio)
{
radio = verticalRadio;
}
else
{
radio = horizontalRadio;
}
} else {
if(verticalRadio>1 && horizontalRadio>1)
{
radio = verticalRadio > horizontalRadio ? horizontalRadio : verticalRadio;
}
else
{
radio = verticalRadio < horizontalRadio ? verticalRadio : horizontalRadio;
}
}
width = roundf(width*radio);
height = roundf(height*radio);
}
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaLast ||
alphaInfo == kCGImageAlphaFirst) {
hasAlpha = YES;
}
CGAffineTransform transform = HX_CGAffineTransformExchangeOrientation(orientation, CGSizeMake(width, height));
// BGRA8888 (premultiplied) or BGRX8888
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
if (!context) return NULL;
CGContextConcatCTM(context, transform);
switch (orientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(context, CGRectMake(0, 0, height, width), imageRef); // decode
break;
default:
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode
break;
}
newImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
}
return newImage;
}
CGImageRef HX_CGImageDecodedFromCopy(CGImageRef imageRef)
{
return HX_CGImageScaleDecodedFromCopy(imageRef, CGSizeZero, UIViewContentModeScaleAspectFit, UIImageOrientationUp);
}
CGImageRef HX_CGImageDecodedCopy(UIImage *image)
{
if (!image) return NULL;
if (image.images.count > 1) {
return NULL;
}
CGImageRef imageRef = image.CGImage;
if (!imageRef) return NULL;
CGImageRef newImageRef = HX_CGImageDecodedFromCopy(imageRef);
return newImageRef;
}
UIImage *HX_UIImageDecodedCopy(UIImage *image)
{
CGImageRef imageRef = HX_CGImageDecodedCopy(image);
if (!imageRef) return image;
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation];
CGImageRelease(imageRef);
return newImage;
}

View File

@ -0,0 +1,47 @@
//
// HXPhotoEditChartletModel.h
// photoEditDemo
//
// Created by Silence on 2020/7/2.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, HXPhotoEditChartletModelType) {
HXPhotoEditChartletModelType_Image, // UIImage
HXPhotoEditChartletModelType_ImageNamed, // NSString
HXPhotoEditChartletModelType_NetworkURL // NSURL
};
@interface HXPhotoEditChartletModel : NSObject
/// 当前资源类型
@property (assign, nonatomic) HXPhotoEditChartletModelType type;
/// 图片对象
@property (strong, nonatomic) UIImage *image;
/// 图片名
@property (copy, nonatomic) NSString *imageNamed;
/// 网络图片地址
@property (strong, nonatomic) NSURL *networkURL;
+ (instancetype)modelWithImage:(UIImage *)image;
+ (instancetype)modelWithImageNamed:(NSString *)imageNamed;
+ (instancetype)modelWithNetworkNURL:(NSURL *)networkURL;
// other
@property (assign, nonatomic) BOOL loadCompletion;
@end
@interface HXPhotoEditChartletTitleModel : HXPhotoEditChartletModel
// 贴图数组
@property (copy, nonatomic) NSArray<HXPhotoEditChartletModel *> *models;
// other
@property (assign, nonatomic) BOOL selected;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,35 @@
//
// HXPhotoEditChartletModel.m
// photoEditDemo
//
// Created by Silence on 2020/7/2.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditChartletModel.h"
@implementation HXPhotoEditChartletModel
+ (instancetype)modelWithImage:(UIImage *)image {
HXPhotoEditChartletModel *model = [[self alloc] init];
model.type = HXPhotoEditChartletModelType_Image;
model.image = image;
return model;
}
+ (instancetype)modelWithImageNamed:(NSString *)imageNamed {
HXPhotoEditChartletModel *model = [[self alloc] init];
model.type = HXPhotoEditChartletModelType_ImageNamed;
model.imageNamed = imageNamed;
return model;
}
+ (instancetype)modelWithNetworkNURL:(NSURL *)networkURL {
HXPhotoEditChartletModel *model = [[self alloc] init];
model.type = HXPhotoEditChartletModelType_NetworkURL;
model.networkURL = networkURL;
return model;
}
@end
@implementation HXPhotoEditChartletTitleModel
@end

View File

@ -0,0 +1,18 @@
//
// HXPhotoEditGraffitiColorModel.h
// photoEditDemo
//
// Created by Silence on 2020/6/22.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditGraffitiColorModel : NSObject
@property (strong, nonatomic) UIColor *color;
@property (assign, nonatomic) BOOL selected;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,13 @@
//
// HXPhotoEditGraffitiColorModel.m
// photoEditDemo
//
// Created by Silence on 2020/6/22.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGraffitiColorModel.h"
@implementation HXPhotoEditGraffitiColorModel
@end

View File

@ -0,0 +1,100 @@
//
// HXPhotoClippingView.h
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, HXPhotoClippingViewMirrorType) {
HXPhotoClippingViewMirrorType_None = 0, // 没有镜像翻转
HXPhotoClippingViewMirrorType_Horizontal // 水平翻转
};
@protocol HXPhotoClippingViewDelegate;
@class HXPhotoEditImageView, HXPhotoEditConfiguration;
@interface HXPhotoClippingView : UIScrollView
@property (nonatomic, strong, readonly) HXPhotoEditImageView *imageView;
@property (nonatomic, strong) UIImage *image;
/// 获取除图片以外的编辑图层
/// @param rect 大小
/// @param rotate 旋转角度
- (UIImage *)editOtherImagesInRect:(CGRect)rect rotate:(CGFloat)rotate;
@property (strong, nonatomic) HXPhotoEditConfiguration *configuration;
@property (nonatomic, weak) id<HXPhotoClippingViewDelegate> clippingDelegate;
/** 首次缩放后需要记录最小缩放值 */
@property (nonatomic, readonly) CGFloat first_minimumZoomScale;
/** 与父视图中心偏差坐标 */
@property (nonatomic, assign) CGPoint offsetSuperCenter;
/// 自定义固定比例
@property (assign, nonatomic) CGSize customRatioSize;
/** 是否重置中 */
@property (nonatomic, readonly) BOOL isReseting;
/** 是否旋转中 */
@property (nonatomic, readonly) BOOL isRotating;
/** 是否镜像翻转中 */
@property (nonatomic, assign) BOOL isMirrorFlip;
/** 是否水平翻转 */
@property (assign, nonatomic) HXPhotoClippingViewMirrorType mirrorType;
/** 旋转系数 */
@property (assign, nonatomic, readonly) NSInteger angle;
/** 是否缩放中 */
//@property (nonatomic, readonly) BOOL isZooming;
/** 是否可还原 */
@property (nonatomic, readonly) BOOL canReset;
/// 显示界面的缩放率
@property (nonatomic, assign) CGFloat screenScale;
/** 以某个位置作为可还原的参照物 */
- (BOOL)canResetWithRect:(CGRect)trueFrame;
/** 可编辑范围 */
@property (nonatomic, assign) CGRect editRect;
/** 剪切范围 */
@property (nonatomic, assign) CGRect cropRect;
/** 手势开关,一般编辑模式下开启 默认NO */
@property (nonatomic, assign) BOOL useGesture;
@property (nonatomic, assign) BOOL fixedAspectRatio;
- (void)zoomToRect:(CGRect)rect;
/** 缩小到指定坐标 */
- (void)zoomOutToRect:(CGRect)toRect;
/** 放大到指定坐标(必须大于当前坐标) */
- (void)zoomInToRect:(CGRect)toRect;
/** 旋转 */
- (void)rotateClockwise:(BOOL)clockwise;
/// 镜像翻转
- (void)mirrorFlip;
/** 还原 */
- (void)reset;
/** 还原到某个位置 */
- (void)resetToRect:(CGRect)rect;
/** 取消 */
- (void)cancel;
/** 数据 */
@property (nonatomic, strong, nullable) NSDictionary *photoEditData;
- (void)changeSubviewFrame;
- (void)resetRotateAngle;
- (void)clearCoverage;
@end
@protocol HXPhotoClippingViewDelegate <NSObject>
/** 同步缩放视图调用zoomOutToRect才会触发 */
- (void (^ _Nullable)(CGRect))clippingViewWillBeginZooming:(HXPhotoClippingView *)clippingView;
- (void)clippingViewDidZoom:(HXPhotoClippingView *)clippingView;
- (void)clippingViewDidEndZooming:(HXPhotoClippingView *)clippingView;
/** 移动视图 */
- (void)clippingViewWillBeginDragging:(HXPhotoClippingView *)clippingView;
- (void)clippingViewDidEndDecelerating:(HXPhotoClippingView *)clippingView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,911 @@
//
// HXPhotoClippingView.m
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoClippingView.h"
#import "HXPhotoEditImageView.h"
#import "HXPhotoEditStickerItemView.h"
#import "UIView+HXExtension.h"
#import "HXPhotoDefine.h"
#import <AVFoundation/AVFoundation.h>
#import "HXPhotoEditStickerItemContentView.h"
#import "HXMECancelBlock.h"
#define HXDefaultMaximumZoomScale 5.f
NSString *const kHXClippingViewData = @"HXClippingViewData";
NSString *const kHXStickerViewData_screenScale = @"HXStickerViewData_screenScale";
NSString *const kHXClippingViewData_frame = @"HXClippingViewData_frame";
NSString *const kHXClippingViewData_zoomScale = @"HXClippingViewData_zoomScale";
NSString *const kHXClippingViewData_contentSize = @"HXClippingViewData_contentSize";
NSString *const kHXClippingViewData_contentOffset = @"HXClippingViewData_contentOffset";
NSString *const kHXClippingViewData_minimumZoomScale = @"HXClippingViewData_minimumZoomScale";
NSString *const kHXClippingViewData_maximumZoomScale = @"HXClippingViewData_maximumZoomScale";
NSString *const kHXClippingViewData_clipsToBounds = @"HXClippingViewData_clipsToBounds";
NSString *const kHXClippingViewData_transform = @"HXClippingViewData_transform";
NSString *const kHXClippingViewData_angle = @"HXClippingViewData_angle";
NSString *const kHXClippingViewData_mirror = @"HXClippingViewData_mirror";
NSString *const kHXClippingViewData_first_minimumZoomScale = @"HXClippingViewData_first_minimumZoomScale";
NSString *const kHXClippingViewData_zoomingView = @"HXClippingViewData_zoomingView";
@interface HXPhotoClippingView ()<UIScrollViewDelegate>
@property (nonatomic, strong) HXPhotoEditImageView *imageView;
/** */
@property (nonatomic, assign) CGRect normalRect;
/** frame */
@property (nonatomic, assign) CGRect saveRect;
/** */
@property (nonatomic, assign) CGFloat first_minimumZoomScale;
/** */
@property (nonatomic, assign) NSInteger angle;
/** */
@property (nonatomic, assign) CGFloat defaultMaximumZoomScale;
/** */
@property (nonatomic, assign) CGRect old_frame;
@property (nonatomic, assign) NSInteger old_angle;
@property (nonatomic, assign) BOOL old_mirrorHorizontally;
@property (nonatomic, assign) HXPhotoClippingViewMirrorType old_mirrorType;
@property (nonatomic, assign) CGFloat old_zoomScale;
@property (nonatomic, assign) CGSize old_contentSize;
@property (nonatomic, assign) CGPoint old_contentOffset;
@property (nonatomic, assign) CGFloat old_minimumZoomScale;
@property (nonatomic, assign) CGFloat old_maximumZoomScale;
@property (nonatomic, assign) CGAffineTransform old_transform;
@end
@implementation HXPhotoClippingView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
self.scrollsToTop = NO;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
if (@available(iOS 11.0, *)){
[self setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
}
if (@available(iOS 13.0, *)) {
self.automaticallyAdjustsScrollIndicatorInsets = NO;
}
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = NO;
self.delegate = self;
self.minimumZoomScale = 1.0f;
self.maximumZoomScale = HXDefaultMaximumZoomScale;
self.defaultMaximumZoomScale = HXDefaultMaximumZoomScale;
self.alwaysBounceHorizontal = YES;
self.alwaysBounceVertical = YES;
self.angle = 0;
self.mirrorType = HXPhotoClippingViewMirrorType_None;
self.offsetSuperCenter = CGPointZero;
self.useGesture = NO;
self.imageView = [[HXPhotoEditImageView alloc] initWithFrame:self.bounds];
HXWeakSelf
self.imageView.moveCenter = ^BOOL(CGRect rect) {
/** 线 */
CGRect newRect = [weakSelf.imageView convertRect:rect toView:weakSelf];
CGRect clipTransRect = CGRectApplyAffineTransform(weakSelf.frame, weakSelf.transform);
CGRect screenRect = (CGRect){weakSelf.contentOffset, clipTransRect.size};
screenRect = CGRectInset(screenRect, 22, 22);
return !CGRectIntersectsRect(screenRect, newRect);
};
self.imageView.getMinScale = ^CGFloat(CGSize size) {
return MIN( 35 / size.width, 35 / size.height);
};
self.imageView.getMaxScale = ^CGFloat(CGSize size) {
CGRect clipTransRect = CGRectApplyAffineTransform(weakSelf.frame, weakSelf.transform);
CGRect screenRect = (CGRect){weakSelf.contentOffset, clipTransRect.size};
CGFloat width = screenRect.size.width * weakSelf.screenScale;
CGFloat height = screenRect.size.height * weakSelf.screenScale;
if (width > HX_ScreenWidth) {
width = HX_ScreenWidth;
}
if (height > HX_ScreenHeight) {
height = HX_ScreenHeight;
}
CGFloat maxSize = MIN(size.width, size.height);
return MAX( (width + 35) / maxSize, (height + 35) / maxSize);
};
[self addSubview:self.imageView];
/** */
_editRect = self.bounds;
}
- (void)changeSubviewFrame {
_editRect = self.bounds;
self.imageView.frame = self.bounds;
[self.imageView changeSubviewFrame];
}
- (void)clearCoverage {
[self.imageView clearCoverage];
}
- (void)setConfiguration:(HXPhotoEditConfiguration *)configuration {
_configuration = configuration;
self.imageView.configuration = configuration;
}
- (void)setScreenScale:(CGFloat)screenScale {
_screenScale = screenScale;
self.imageView.screenScale = screenScale;
}
- (void)setImage:(UIImage *)image {
_image = image;
[self setZoomScale:1.f];
if (image) {
if (self.frame.size.width < self.frame.size.height) {
self.defaultMaximumZoomScale = [UIScreen mainScreen].bounds.size.width * HXDefaultMaximumZoomScale / self.frame.size.width;
} else {
self.defaultMaximumZoomScale = [UIScreen mainScreen].bounds.size.height * HXDefaultMaximumZoomScale / self.frame.size.height;
}
self.maximumZoomScale = self.defaultMaximumZoomScale;
}
self.normalRect = self.frame;
self.saveRect = self.frame;
self.contentSize = self.hx_size;
self.imageView.frame = self.bounds;
self.imageView.image = image;
}
/** */
- (UIImage *)editOtherImagesInRect:(CGRect)rect rotate:(CGFloat)rotate {
CGRect inRect = [self.superview convertRect:rect toView:self.imageView];
/** 1 */
// inRect = HXMediaEditProundRect(inRect);
return [self.imageView editOtherImagesInRect:inRect rotate:rotate mirrorHorizontally:self.mirrorType == HXPhotoClippingViewMirrorType_Horizontal];
}
- (void)setCropRect:(CGRect)cropRect {
/** */
self.old_transform = self.transform;
self.old_angle = self.angle;
self.old_mirrorType = self.mirrorType;
self.old_frame = self.frame;
self.old_zoomScale = self.zoomScale;
self.old_contentSize = self.contentSize;
self.old_contentOffset = self.contentOffset;
self.old_minimumZoomScale = self.minimumZoomScale;
self.old_maximumZoomScale = self.maximumZoomScale;
_cropRect = cropRect;
/** UIcontentOffsetcontentSize */
/** contentSize */
CGPoint contentOffset = self.contentOffset;
CGFloat scaleX = MAX(contentOffset.x / (self.contentSize.width - self.hx_w), 0);
CGFloat scaleY = MAX(contentOffset.y / (self.contentSize.height - self.hx_h), 0);
BOOL isHorizontal = NO;
if (self.fixedAspectRatio) {
if (self.angle % 180 != 0) {
isHorizontal = YES;
scaleX = MAX(contentOffset.x / (self.contentSize.width - self.hx_h), 0);
scaleY = MAX(contentOffset.y / (self.contentSize.height - self.hx_w), 0);
}
}
/** contentOffsetcontentSizeframe contentSizecontentOffset */
CGRect oldFrame = self.frame;
self.frame = cropRect;
self.saveRect = self.frame;
CGFloat scale = self.zoomScale;
/** */
CGFloat scaleZX = CGRectGetWidth(cropRect)/(CGRectGetWidth(oldFrame)/scale);
CGFloat scaleZY = CGRectGetHeight(cropRect)/(CGRectGetHeight(oldFrame)/scale);
CGFloat zoomScale = MIN(scaleZX, scaleZY);
[self resetMinimumZoomScale];
self.maximumZoomScale = (zoomScale > self.defaultMaximumZoomScale ? zoomScale : self.defaultMaximumZoomScale);
[self setZoomScale:zoomScale];
/** */
if (self.first_minimumZoomScale == 0) {
self.first_minimumZoomScale = self.minimumZoomScale;
}
/** contentSize */
self.contentSize = self.imageView.hx_size;
if (isHorizontal) {
/** contentOffset */
contentOffset.x = isnan(scaleX) ? contentOffset.x : (scaleX > 0 ? (HXRound(self.contentSize.width) - HXRound(self.hx_h)) * scaleX : contentOffset.x);
contentOffset.y = isnan(scaleY) ? contentOffset.y : (scaleY > 0 ? (HXRound(self.contentSize.height) - HXRound(self.hx_w)) * scaleY : contentOffset.y);
}else {
/** contentOffset */
contentOffset.x = isnan(scaleX) ? contentOffset.x : (scaleX > 0 ? (HXRound(self.contentSize.width) - HXRound(self.hx_w)) * scaleX : contentOffset.x);
contentOffset.y = isnan(scaleY) ? contentOffset.y : (scaleY > 0 ? (HXRound(self.contentSize.height) - HXRound(self.hx_h)) * scaleY : contentOffset.y);
}
/** */
CGRect zoomViewRect = self.imageView.frame;
CGRect selfRect = CGRectApplyAffineTransform(self.frame, self.transform);
CGFloat contentOffsetX = MIN(MAX(contentOffset.x, 0),zoomViewRect.size.width - selfRect.size.width);
CGFloat contentOfssetY = MIN(MAX(contentOffset.y, 0),zoomViewRect.size.height - selfRect.size.height);
self.contentOffset = CGPointMake(contentOffsetX, contentOfssetY);
}
/** */
- (void)cancel {
if (!CGRectEqualToRect(self.old_frame, CGRectZero)) {
self.transform = self.old_transform;
self.angle = self.old_angle;
self.mirrorType = self.old_mirrorType;
self.frame = self.old_frame;
self.saveRect = self.frame;
self.minimumZoomScale = self.old_minimumZoomScale;
self.maximumZoomScale = self.old_maximumZoomScale;
self.zoomScale = self.old_zoomScale;
self.contentSize = self.old_contentSize;
self.contentOffset = self.old_contentOffset;
}
}
- (void)resetRotateAngle {
self.transform = CGAffineTransformIdentity;
self.angle = 0;
self.mirrorType = HXPhotoClippingViewMirrorType_None;
}
- (void)reset {
[self resetToRect:CGRectZero];
}
- (void)resetToRect:(CGRect)rect {
if (!_isReseting) {
_isReseting = YES;
self.mirrorType = HXPhotoClippingViewMirrorType_None;
if (CGRectEqualToRect(rect, CGRectZero)) {
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.transform = CGAffineTransformIdentity;
self.angle = 0;
self.minimumZoomScale = self.first_minimumZoomScale;
[self setZoomScale:self.minimumZoomScale];
self.frame = (CGRect){CGPointZero, self.imageView.hx_size};
self.center = CGPointMake(self.superview.center.x-self.offsetSuperCenter.x/2, self.superview.center.y-self.offsetSuperCenter.y/2);
self.saveRect = self.frame;
/** contentSize */
self.contentSize = self.imageView.hx_size;
/** contentOffset */
self.contentOffset = CGPointZero;
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginZooming:)]) {
void (^block)(CGRect) = [self.clippingDelegate clippingViewWillBeginZooming:self];
if (block) block(self.frame);
}
} completion:^(BOOL finished) {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndZooming:)]) {
[self.clippingDelegate clippingViewDidEndZooming:self];
}
self->_isReseting = NO;
}];
} else {
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.transform = CGAffineTransformIdentity;
self.angle = 0;
self.minimumZoomScale = self.first_minimumZoomScale;
[self setZoomScale:self.minimumZoomScale];
self.frame = (CGRect){CGPointZero, self.imageView.hx_size};
self.center = CGPointMake(self.superview.center.x-self.offsetSuperCenter.x/2, self.superview.center.y-self.offsetSuperCenter.y/2);
/** contentSize */
self.contentSize = self.imageView.hx_size;
/** contentOffset */
self.contentOffset = CGPointZero;
[self zoomInToRect:rect];
[self zoomOutToRect:rect completion:^{
self->_isReseting = NO;
}];
} completion:nil];
}
}
}
- (BOOL)canReset {
CGRect superViewRect = self.superview.bounds;
CGFloat x = (HXRoundDecade(CGRectGetWidth(superViewRect)) - HXRoundDecade(CGRectGetWidth(self.imageView.frame))) / 2 - HXRoundDecade(self.offsetSuperCenter.x) / 2;
CGFloat y = (HXRoundDecade(CGRectGetHeight(superViewRect)) - HXRoundDecade(CGRectGetHeight(self.imageView.frame))) / 2 - HXRoundDecade(self.offsetSuperCenter.y) / 2;
CGRect trueFrame = CGRectMake(x ,y , HXRoundDecade(CGRectGetWidth(self.imageView.frame))
, HXRoundDecade(CGRectGetHeight(self.imageView.frame)));
return [self canResetWithRect:trueFrame];
}
- (BOOL)canResetWithRect:(CGRect)trueFrame {
BOOL canReset = !(CGAffineTransformIsIdentity(self.transform)
&& HXRound(self.zoomScale) == HXRound(self.minimumZoomScale)
&& [self verifyRect:trueFrame]);
if (self.fixedAspectRatio && !canReset) {
if (fabs((fabs(HXRoundHundreds(self.contentSize.width) - HXRoundHundreds(self.hx_w)) / 2 - HXRoundHundreds(self.contentOffset.x))) >= 0.999) {
canReset = YES;
}else if (fabs((fabs(HXRoundHundreds(self.contentSize.height) - HXRoundHundreds(self.hx_h)) / 2 - HXRoundHundreds(self.contentOffset.y))) >= 0.999) {
canReset = YES;
}
}
return canReset;
}
- (CGRect)cappedCropRectInImageRectWithCropRect:(CGRect)cropRect {
CGRect rect = [self convertRect:self.imageView.frame toView:self.superview];
if (CGRectGetMinX(cropRect) < CGRectGetMinX(rect)) {
cropRect.origin.x = CGRectGetMinX(rect);
}
if (CGRectGetMinY(cropRect) < CGRectGetMinY(rect)) {
cropRect.origin.y = CGRectGetMinY(rect);
}
if (CGRectGetMaxX(cropRect) > CGRectGetMaxX(rect)) {
cropRect.size.width = CGRectGetMaxX(rect) - CGRectGetMinX(cropRect);
}
if (CGRectGetMaxY(cropRect) > CGRectGetMaxY(rect)) {
cropRect.size.height = CGRectGetMaxY(rect) - CGRectGetMinY(cropRect);
}
return cropRect;
}
#pragma mark
- (void)zoomOutToRect:(CGRect)toRect {
[self zoomOutToRect:toRect completion:nil];
}
- (void)zoomOutToRect:(CGRect)toRect completion:(void (^)(void))completion {
/** */
if (self.dragging || self.decelerating) {
return;
}
CGRect rect = [self cappedCropRectInImageRectWithCropRect:toRect];
CGFloat width = CGRectGetWidth(rect);
CGFloat height = CGRectGetHeight(rect);
/** */
[self resetMinimumZoomScale];
[self setZoomScale:self.zoomScale];
CGFloat scale = MIN(CGRectGetWidth(self.editRect) / width, CGRectGetHeight(self.editRect) / height);
/** = */
if (CGRectEqualToRect(HXRoundFrame(self.frame), HXRoundFrame(rect)) || (HXRound(self.zoomScale) == HXRound(self.maximumZoomScale) && HXRound(scale) > 1.f)) {
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
/** */
self.center = CGPointMake(self.superview.center.x-self.offsetSuperCenter.x/2, self.superview.center.y-self.offsetSuperCenter.y/2);
self.saveRect = self.frame;
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginZooming:)]) {
void (^block)(CGRect) = [self.clippingDelegate clippingViewWillBeginZooming:self];
if (block) block(self.frame);
}
} completion:^(BOOL finished) {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndZooming:)]) {
[self.clippingDelegate clippingViewDidEndZooming:self];
}
if (completion) {
completion();
}
}];
return;
}
CGFloat scaledWidth = width * scale;
CGFloat scaledHeight = height * scale;
/** */
CGFloat zoomScale = MIN(self.zoomScale * scale, self.maximumZoomScale);
/** 100:1 1:100 */
CGRect zoomViewRect = CGRectApplyAffineTransform(self.imageView.frame, self.transform);
scaledWidth = MIN(scaledWidth, CGRectGetWidth(zoomViewRect) * (zoomScale / self.minimumZoomScale));
scaledHeight = MIN(scaledHeight, CGRectGetHeight(zoomViewRect) * (zoomScale / self.minimumZoomScale));
/** */
CGRect cropRect = CGRectMake((CGRectGetWidth(self.superview.bounds) - scaledWidth) / 2 - self.offsetSuperCenter.x/2,
(CGRectGetHeight(self.superview.bounds) - scaledHeight) / 2 - self.offsetSuperCenter.y/2,
scaledWidth,
scaledHeight);
/** */
__block CGPoint contentOffset = self.contentOffset;
if (!([self verifyRect:cropRect] && zoomScale == self.zoomScale)) { /** && */
/** */
CGRect zoomRect = [self.superview convertRect:rect toView:self];
contentOffset.x = zoomRect.origin.x * zoomScale / self.zoomScale;
contentOffset.y = zoomRect.origin.y * zoomScale / self.zoomScale;
}
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.frame = cropRect;
self.saveRect = self.frame;
[self setZoomScale:zoomScale];
/** contentSize */
self.contentSize = self.imageView.hx_size;
[self setContentOffset:contentOffset];
/** */
[self resetMinimumZoomScale];
[self setZoomScale:self.zoomScale];
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginZooming:)]) {
void (^block)(CGRect) = [self.clippingDelegate clippingViewWillBeginZooming:self];
if (block) block(self.frame);
}
} completion:^(BOOL finished) {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndZooming:)]) {
[self.clippingDelegate clippingViewDidEndZooming:self];
}
if (completion) {
completion();
}
}];
}
- (void)zoomToRect:(CGRect)rect {
self.frame = rect;
[self resetMinimumZoomScale];
[self setZoomScale:self.zoomScale];
}
#pragma mark ()
- (void)zoomInToRect:(CGRect)toRect {
/** */
if (self.dragging || self.decelerating) {
return;
}
CGRect zoomingRect = [self convertRect:self.imageView.frame toView:self.superview];
/** */
if ((CGRectGetMinX(toRect) + FLT_EPSILON) < CGRectGetMinX(zoomingRect)
|| (CGRectGetMinY(toRect) + FLT_EPSILON) < CGRectGetMinY(zoomingRect)
|| (CGRectGetMaxX(toRect) - FLT_EPSILON) > (CGRectGetMaxX(zoomingRect)+0.5) /** 0. */
|| (CGRectGetMaxY(toRect) - FLT_EPSILON) > (CGRectGetMaxY(zoomingRect)+0.5)
) {
/** */
CGRect myFrame = self.frame;
myFrame.origin.x = MIN(myFrame.origin.x, toRect.origin.x);
myFrame.origin.y = MIN(myFrame.origin.y, toRect.origin.y);
myFrame.size.width = MAX(myFrame.size.width, toRect.size.width);
myFrame.size.height = MAX(myFrame.size.height, toRect.size.height);
self.frame = myFrame;
[self resetMinimumZoomScale];
[self setZoomScale:self.zoomScale];
}
}
#pragma mark
- (void)rotateClockwise:(BOOL)clockwise {
/** */
if (self.dragging || self.decelerating) {
return;
}
if (!_isRotating) {
_isRotating = YES;
if (self.mirrorType == HXPhotoClippingViewMirrorType_Horizontal) {
clockwise = YES;
}
NSInteger newAngle = self.angle;
newAngle = clockwise ? newAngle + 90 : newAngle - 90;
if (newAngle <= -360 || newAngle >= 360)
newAngle = 0;
_angle = newAngle;
[UIView animateWithDuration:0.35f delay:0.0f usingSpringWithDamping:1.0f initialSpringVelocity:0.8f options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[self transformRotate:self.angle];
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginZooming:)]) {
void (^block)(CGRect) = [self.clippingDelegate clippingViewWillBeginZooming:self];
if (block) block(self.frame);
}
} completion:^(BOOL complete) {
self->_isRotating = NO;
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndZooming:)]) {
[self.clippingDelegate clippingViewDidEndZooming:self];
}
}];
}
}
///
- (void)mirrorFlip {
if (self.dragging || self.decelerating) {
return;
}
if (!self.isMirrorFlip) {
self.isMirrorFlip = YES;
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginZooming:)]) {
[self.clippingDelegate clippingViewWillBeginZooming:self];
}
CGFloat angleInRadians = 0.0f;
switch (self.angle) {
case 90: angleInRadians = M_PI_2; break;
case -90: angleInRadians = -M_PI_2; break;
case 180: angleInRadians = M_PI; break;
case -180: angleInRadians = -M_PI; break;
case 270: angleInRadians = (M_PI + M_PI_2); break;
case -270: angleInRadians = -(M_PI + M_PI_2); break;
default: break;
}
CGAffineTransform rotateTransform = CGAffineTransformRotate(CGAffineTransformIdentity ,angleInRadians);
[UIView animateWithDuration:0.25 animations:^{
if (self.mirrorType == HXPhotoClippingViewMirrorType_None) {
//
if (self.angle == 0) {
//
self.transform = CGAffineTransformScale(rotateTransform ,-1.0, 1.0);
self.mirrorType = HXPhotoClippingViewMirrorType_Horizontal;
}else if (self.angle == -90 || self.angle == 90) {
//
self.transform = CGAffineTransformScale(rotateTransform ,1.0, -1.0);
self.mirrorType = HXPhotoClippingViewMirrorType_Horizontal;
self.angle = 270;
}else if (self.angle == -180 || self.angle == 180) {
//
self.transform = CGAffineTransformScale(rotateTransform ,-1.0, 1.0);
self.mirrorType = HXPhotoClippingViewMirrorType_Horizontal;
}else if (self.angle == -270 || self.angle == 270) {
//
self.transform = CGAffineTransformScale(CGAffineTransformRotate(CGAffineTransformIdentity, -M_PI_2), -1, 1);
self.angle = 90;
self.mirrorType = HXPhotoClippingViewMirrorType_Horizontal;
}
}else if (self.mirrorType == HXPhotoClippingViewMirrorType_Horizontal){
//
if (self.angle == 0) {
//
self.transform = CGAffineTransformScale(rotateTransform ,1.0, 1.0);
self.mirrorType = HXPhotoClippingViewMirrorType_None;
}else if (self.angle == -90 || self.angle == 90) {
//
CGAffineTransform transfrom = CGAffineTransformRotate(CGAffineTransformIdentity, -(M_PI + M_PI_2));
self.transform = transfrom;
self.angle = -270;
self.mirrorType = HXPhotoClippingViewMirrorType_None;
}else if (self.angle == -180 || self.angle == 180) {
//
self.transform = CGAffineTransformScale(rotateTransform ,1.0, 1.0);
self.mirrorType = HXPhotoClippingViewMirrorType_None;
}else if (self.angle == -270 || self.angle == 270) {
//
self.transform = CGAffineTransformScale(CGAffineTransformRotate(CGAffineTransformIdentity, -M_PI_2) , 1, 1);
self.angle = -90;
self.mirrorType = HXPhotoClippingViewMirrorType_None;
}
}
} completion:^(BOOL finished) {
self.isMirrorFlip = NO;
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndZooming:)]) {
[self.clippingDelegate clippingViewDidEndZooming:self];
}
}];
}
}
- (CGAffineTransform)getCurrentMirrorFlip {
if (self.mirrorType == HXPhotoClippingViewMirrorType_Horizontal){
//
return CGAffineTransformScale(CGAffineTransformIdentity ,-1.0, 1.0);
}
return CGAffineTransformIdentity;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginDragging:)]) {
[self.clippingDelegate clippingViewWillBeginDragging:self];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndDecelerating:)]) {
[self.clippingDelegate clippingViewDidEndDecelerating:self];
}
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndDecelerating:)]) {
[self.clippingDelegate clippingViewDidEndDecelerating:self];
}
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewWillBeginZooming:)]) {
void (^block)(CGRect) = [self.clippingDelegate clippingViewWillBeginZooming:self];
block(self.frame);
}
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
self.contentInset = UIEdgeInsetsZero;
self.scrollIndicatorInsets = UIEdgeInsetsZero;
if (scrollView.isZooming || scrollView.isZoomBouncing) {
/** zoom2UI */
[self refreshImageZoomViewCenter];
}
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidZoom:)]) {
[self.clippingDelegate clippingViewDidZoom:self];
}
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale {
if ([self.clippingDelegate respondsToSelector:@selector(clippingViewDidEndZooming:)]) {
[self.clippingDelegate clippingViewDidEndZooming:self];
}
}
#pragma mark - Private
- (void)refreshImageZoomViewCenter {
CGRect rect = CGRectApplyAffineTransform(self.frame, self.transform);
CGFloat offsetX = (rect.size.width > self.contentSize.width) ? ((rect.size.width - self.contentSize.width) * 0.5) : 0.0;
CGFloat offsetY = (rect.size.height > self.contentSize.height) ? ((rect.size.height - self.contentSize.height) * 0.5) : 0.0;
self.imageView.center = CGPointMake(self.contentSize.width * 0.5 + offsetX, self.contentSize.height * 0.5 + offsetY);
}
#pragma mark -
- (BOOL)verifyRect:(CGRect)r_rect {
/** */
CGRect rect = CGRectApplyAffineTransform(r_rect, self.transform);
/** */
BOOL isEqual = CGRectEqualToRect(rect, self.frame);
if (isEqual == NO) {
/** */
// 0.5f
if (fabs(self.hx_x - rect.origin.x) <= 0.5f) {
rect = CGRectMake(self.frame.origin.x, rect.origin.y, rect.size.width, rect.size.height);
}
if (fabs(self.hx_y - rect.origin.y) <= 0.5f) {
rect = CGRectMake(rect.origin.x, self.frame.origin.y, rect.size.width, rect.size.height);
}
if (fabs(self.hx_w - rect.size.width) <= 0.5f) {
rect = CGRectMake(rect.origin.x, rect.origin.y, self.frame.size.width, rect.size.height);
}
if (fabs(self.hx_h - rect.size.height) <= 0.5f) {
rect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, self.frame.size.height);
}
BOOL x = HXRoundDecade(CGRectGetMinX(rect)) == HXRoundDecade(CGRectGetMinX(self.frame));
BOOL y = HXRoundDecade(CGRectGetMinY(rect)) == HXRoundDecade(CGRectGetMinY(self.frame));
BOOL w = HXRoundDecade(CGRectGetWidth(rect)) == HXRoundDecade(CGRectGetWidth(self.frame));
BOOL h = HXRoundDecade(CGRectGetHeight(rect)) == HXRoundDecade(CGRectGetHeight(self.frame));
isEqual = x && y && w && h;
}
return isEqual;
}
#pragma mark -
- (void)transformRotate:(NSInteger)angle {
//Convert the new angle to radians
CGFloat angleInRadians = 0.0f;
switch (angle) {
case 90: angleInRadians = M_PI_2; break;
case -90: angleInRadians = -M_PI_2; break;
case 180: angleInRadians = M_PI; break;
case -180: angleInRadians = -M_PI; break;
case 270: angleInRadians = (M_PI + M_PI_2); break;
case -270: angleInRadians = -(M_PI + M_PI_2); break;
default: break;
}
/** 使centerboundsframe */
CGPoint center = self.center;
CGRect bounds = self.bounds;
CGRect oldRect = CGRectMake(center.x-0.5*bounds.size.width, center.y-0.5*bounds.size.height, bounds.size.width, bounds.size.height);
CGFloat width = CGRectGetWidth(oldRect);
CGFloat height = CGRectGetHeight(oldRect);
if (self.fixedAspectRatio) {
if (angle%180 == 0) { /** */
CGFloat tempWidth = width;
width = height;
height = tempWidth;
}
}else {
if (angle%180 != 0) { /** */
CGFloat tempWidth = width;
width = height;
height = tempWidth;
}
}
/** */
CGPoint contentOffset = self.contentOffset;
CGFloat marginXScale = 0.5;
if (self.fixedAspectRatio) {
CGFloat w = ((angle%180 == 0) ? self.hx_h : self.hx_w);
if (self.contentSize.width - w > 0) {
marginXScale = contentOffset.x / (self.contentSize.width - w);
if (marginXScale == 0) {
marginXScale = 0.5;
}
marginXScale = HXRound(marginXScale);
}
}
CGFloat marginYScale = 0.5f;
if (self.fixedAspectRatio) {
CGFloat h = ((angle%180 == 0) ? self.hx_w : self.hx_h);
if (self.contentSize.height - h > 0) {
marginYScale = contentOffset.y / (self.contentSize.height - h);
if (marginYScale == 0) {
marginYScale = 0.5;
}
marginYScale = HXRound(marginYScale);
}
}
/** */
CGAffineTransform transform = CGAffineTransformRotate([self getCurrentMirrorFlip], angleInRadians);
self.transform = transform;
/** */
CGRect frame = HXRoundFrameHundreds(AVMakeRectWithAspectRatioInsideRect(CGSizeMake(width, height), self.editRect));
self.frame = frame;
/** */
[self resetMinimumZoomScale];
/** */
CGFloat scale = MIN(CGRectGetWidth(self.frame) / width, CGRectGetHeight(self.frame) / height);
/** */
self.zoomScale *= scale;
/** */
if (self.fixedAspectRatio) {
if (angle%180 != 0) { /** */
contentOffset.x = fabs(self.contentSize.width - self.hx_h) * marginXScale;
contentOffset.y = fabs(self.contentSize.height - self.hx_w) * marginYScale;
}else {
contentOffset.x = fabs(self.contentSize.width - self.hx_w) * marginXScale;
contentOffset.y = fabs(self.contentSize.height - self.hx_h) * marginYScale;
}
}else {
contentOffset.x *= scale;
contentOffset.y *= scale;
}
self.contentOffset = contentOffset;
}
#pragma mark -
- (void)resetMinimumZoomScale {
/** */
CGRect rotateNormalRect = CGRectApplyAffineTransform(self.normalRect, self.transform);
if (CGSizeEqualToSize(rotateNormalRect.size, CGSizeZero)) {
/** size0minimumZoomScale=+Inf */
return;
}
CGFloat minimumZoomScale = MAX(CGRectGetWidth(self.frame) / CGRectGetWidth(rotateNormalRect), CGRectGetHeight(self.frame) / CGRectGetHeight(rotateNormalRect));
self.minimumZoomScale = minimumZoomScale;
}
#pragma mark -
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
// if ([[self.imageView subviews] containsObject:view]) {
// if (event.allTouches.count == 1) { /** 1 */
// return YES;
// } else if (event.allTouches.count == 2) { /** 2 */
// return NO;
// }
// }
return [super touchesShouldBegin:touches withEvent:event inContentView:view];
}
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
// if ([[self.imageView subviews] containsObject:view]) {
// return NO;
// } else if (![[self subviews] containsObject:view]) { /** */
// return NO;
// }
return [super touchesShouldCancelInContentView:view];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if (!view) {
CGPoint sPoint = [self convertPoint:point toView:self.imageView.stickerView];
if (CGRectContainsPoint(self.imageView.stickerView.selectItemView.frame, sPoint)) {
self.imageView.stickerView.hitTestSubView = YES;
return self.imageView.stickerView.selectItemView.contentView;
}else {
[self.imageView.stickerView removeSelectItem];
}
}
if (view == self.imageView) { /** UI */
return self;
}
return view;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
/** */
return self.useGesture;
}
#pragma mark -
- (NSDictionary *)photoEditData
{
NSMutableDictionary *data = [@{} mutableCopy];
if ([self canReset]) { /** */
// CGRect trueFrame = CGRectApplyAffineTransform(self.frame, CGAffineTransformInvert(self.transform));
NSDictionary *myData = @{kHXClippingViewData_frame:[NSValue valueWithCGRect:self.saveRect]
, kHXClippingViewData_zoomScale:@(self.zoomScale)
, kHXClippingViewData_contentSize:[NSValue valueWithCGSize:self.contentSize]
, kHXClippingViewData_contentOffset:[NSValue valueWithCGPoint:self.contentOffset]
, kHXClippingViewData_minimumZoomScale:@(self.minimumZoomScale)
, kHXClippingViewData_maximumZoomScale:@(self.maximumZoomScale)
, kHXClippingViewData_clipsToBounds:@(self.clipsToBounds)
, kHXClippingViewData_first_minimumZoomScale:@(self.first_minimumZoomScale)
, kHXClippingViewData_transform:[NSValue valueWithCGAffineTransform:self.transform]
, kHXClippingViewData_angle:@(self.angle)
, kHXClippingViewData_mirror : @(self.mirrorType)
, kHXStickerViewData_screenScale:@(self.screenScale)
};
[data setObject:myData forKey:kHXClippingViewData];
}
NSDictionary *imageViewData = self.imageView.photoEditData;
if (imageViewData) [data setObject:imageViewData forKey:kHXClippingViewData_zoomingView];
if (data.count) {
return data;
}
return nil;
}
- (void)setPhotoEditData:(NSDictionary *)photoEditData
{
NSDictionary *myData = photoEditData[kHXClippingViewData];
if (myData) {
self.transform = [myData[kHXClippingViewData_transform] CGAffineTransformValue];
self.angle = [myData[kHXClippingViewData_angle] integerValue];
self.mirrorType = [myData[kHXClippingViewData_mirror] integerValue];
self.saveRect = [myData[kHXClippingViewData_frame] CGRectValue];
self.frame = self.saveRect;
self.minimumZoomScale = [myData[kHXClippingViewData_minimumZoomScale] floatValue];
self.maximumZoomScale = [myData[kHXClippingViewData_maximumZoomScale] floatValue];
self.zoomScale = [myData[kHXClippingViewData_zoomScale] floatValue];
self.contentSize = [myData[kHXClippingViewData_contentSize] CGSizeValue];
self.contentOffset = [myData[kHXClippingViewData_contentOffset] CGPointValue];
self.clipsToBounds = [myData[kHXClippingViewData_clipsToBounds] boolValue];
self.first_minimumZoomScale = [myData[kHXClippingViewData_first_minimumZoomScale] floatValue];
self.screenScale = [myData[kHXStickerViewData_screenScale] floatValue];
}
self.imageView.photoEditData = photoEditData[kHXClippingViewData_zoomingView];
}
@end

View File

@ -0,0 +1,19 @@
//
// HXPhotoEditChartletContentViewCell.h
// photoEditDemo
//
// Created by Silence on 2020/7/2.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class HXPhotoEditChartletModel;
@interface HXPhotoEditChartletContentViewCell : UICollectionViewCell
@property (weak, nonatomic, readonly) IBOutlet UICollectionView *collectionView;
@property (copy, nonatomic) NSArray<HXPhotoEditChartletModel *> *models;
@property (copy, nonatomic) void (^ selectCellBlock)(UIImage *image);
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,66 @@
//
// HXPhotoEditChartletContentViewCell.m
// photoEditDemo
//
// Created by Silence on 2020/7/2.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditChartletContentViewCell.h"
#import "HXPhotoEditChartletModel.h"
#import "HXPhotoEditChartletListView.h"
#import "HXPhotoDefine.h"
@interface HXPhotoEditChartletContentViewCell ()<UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet UICollectionViewFlowLayout *flowLayout;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *collectionLeftConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *collectionRightConstraint;
@end
@implementation HXPhotoEditChartletContentViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
if (@available(iOS 11.0, *)){
[self.collectionView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
}
CGFloat width;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown || HX_UI_IS_IPAD) {
width = (HX_ScreenWidth - 5 * 20) / 4;
self.flowLayout.sectionInset = UIEdgeInsetsMake(75, 20, 20, 20);
}else {
self.flowLayout.sectionInset = UIEdgeInsetsMake(75, 20 + hxTopMargin, 20, 20 + hxTopMargin);
width = ((HX_ScreenWidth - hxTopMargin * 2) - 9 * 20) / 8;
}
self.flowLayout.itemSize = CGSizeMake(width, width);
self.flowLayout.minimumLineSpacing = 20;
self.flowLayout.minimumInteritemSpacing = 20;
[self.collectionView registerClass:[HXPhotoEditChartletListViewCell class] forCellWithReuseIdentifier:@"HXPhotoEditChartletListViewCell_Id"];
}
- (void)setModels:(NSArray<HXPhotoEditChartletModel *> *)models {
_models = models;
[self.collectionView reloadData];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.models.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
HXPhotoEditChartletListViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"HXPhotoEditChartletListViewCell_Id" forIndexPath:indexPath];
cell.model = self.models[indexPath.item];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (self.selectCellBlock) {
HXPhotoEditChartletListViewCell *cell = (HXPhotoEditChartletListViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
if (!cell.imageView.image) {
return;
}
self.selectCellBlock(cell.imageView.image);
}
}
@end

View File

@ -0,0 +1,28 @@
//
// HXPhotoEditChartletListView.h
// photoEditDemo
//
// Created by Silence on 2020/6/23.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class HXPhotoEditChartletModel, HXPhotoEditChartletTitleModel, HXPhotoEditConfiguration;
@interface HXPhotoEditChartletListView : UIView
+ (void)showEmojiViewWithConfiguration:(HXPhotoEditConfiguration *)configuration
completion:(void (^ _Nullable)(UIImage *image))completion;
+ (void)showEmojiViewWithModels:(NSArray<HXPhotoEditChartletTitleModel *> *)models
completion:(void (^ _Nullable)(UIImage *image))completion;
@end
@interface HXPhotoEditChartletListViewCell : UICollectionViewCell
@property (strong, nonatomic) UIImageView *imageView;
@property (strong, nonatomic) HXPhotoEditChartletTitleModel *titleModel;
@property (strong, nonatomic) HXPhotoEditChartletModel *model;
@property (assign, nonatomic) BOOL showMask;
- (void)setShowMask:(BOOL)showMask isAnimate:(BOOL)isAnimate;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,462 @@
//
// HXPhotoEditChartletListView.m
// photoEditDemo
//
// Created by Silence on 2020/6/23.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditChartletListView.h"
#import "UIView+HXExtension.h"
#import "HXPhotoDefine.h"
#import "UIImage+HXExtension.h"
#import "HXPhotoEditChartletPreviewView.h"
#import "HXPhotoEditChartletModel.h"
#import "HXPhotoEditChartletContentViewCell.h"
#import "UIImageView+HXExtension.h"
#import "HXPhotoEditConfiguration.h"
#import "NSBundle+HXPhotoPicker.h"
#define HXclViewHeight HX_UI_IS_IPAD ? 500 : (([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown) ? (HX_IS_IPhoneX_All ? 400 : 350) : 200)
@interface HXPhotoEditChartletListView ()<UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UIView *bgView;
@property (weak, nonatomic) IBOutlet UIView *contentView;
@property (weak, nonatomic) IBOutlet UIButton *arrowBtn;
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet UICollectionViewFlowLayout *flowLayout;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *collectionViewHeightConstraint;
@property (copy, nonatomic) void (^ selectImageCompletion)(UIImage *image);
@property (copy, nonatomic) NSArray *models;
@property (strong, nonatomic) HXPhotoEditChartletPreviewView *currentPreview;
@property (weak, nonatomic) IBOutlet UICollectionView *titleCollectionView;
@property (weak, nonatomic) IBOutlet UICollectionViewFlowLayout *titleFlowLayout;
@property (weak, nonatomic) IBOutlet UIVisualEffectView *titleView;
@property (strong, nonatomic) NSIndexPath *currentSelectTitleIndexPath;
@property (assign, nonatomic) CGFloat contentViewBottom;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *arrowRightConstraint;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *loadingView;
@property (strong, nonatomic) HXPhotoEditConfiguration *configuration;
@end
@implementation HXPhotoEditChartletListView
+ (void)showEmojiViewWithConfiguration:(HXPhotoEditConfiguration *)configuration
completion:(void (^ _Nullable)(UIImage *image))completion {
HXPhotoEditChartletListView *view = [HXPhotoEditChartletListView initView];
view.selectImageCompletion = completion;
view.configuration = configuration;
view.frame = [UIScreen mainScreen].bounds;
[[UIApplication sharedApplication].keyWindow addSubview:view];
[view show];
}
+ (void)showEmojiViewWithModels:(NSArray<HXPhotoEditChartletTitleModel *> *)models
completion:(void (^ _Nullable)(UIImage *image))completion {
HXPhotoEditChartletListView *view = [HXPhotoEditChartletListView initView];
view.selectImageCompletion = completion;
view.models = models;
view.frame = [UIScreen mainScreen].bounds;
[[UIApplication sharedApplication].keyWindow addSubview:view];
[view show];
}
+ (instancetype)initView {
return [[[NSBundle hx_photoPickerBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)awakeFromNib {
[super awakeFromNib];
self.loadingView.hidesWhenStopped = YES;
if (@available(iOS 11.0, *)){
[self.collectionView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
[self.titleCollectionView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
}
[self.arrowBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_pull_down"] forState:UIControlStateNormal];
self.contentViewBottomConstraint.constant = -(HXclViewHeight + hxBottomMargin);
self.collectionViewHeightConstraint.constant = HXclViewHeight + hxBottomMargin;
[self.bgView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hide)]];
self.bgView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];
self.contentView.layer.masksToBounds = YES;
if (HX_IOS11_Later) {
[self.contentView hx_radiusWithRadius:8 corner:UIRectCornerTopLeft | UIRectCornerTopRight];
}
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft) {
self.arrowRightConstraint.constant = hxTopMargin;
self.titleFlowLayout.sectionInset = UIEdgeInsetsMake(12.5, 20 + hxTopMargin, 12.5, 20);
}else {
self.titleFlowLayout.sectionInset = UIEdgeInsetsMake(12.5, 20, 12.5, 20);
}
self.titleFlowLayout.itemSize = CGSizeMake(35, 35);
self.titleFlowLayout.minimumInteritemSpacing = 20;
self.titleFlowLayout.minimumLineSpacing = 20;
self.titleCollectionView.dataSource = self;
self.titleCollectionView.delegate = self;
[self.titleCollectionView registerClass:[HXPhotoEditChartletListViewCell class] forCellWithReuseIdentifier:@"HXPhotoEditChartletListViewCellId"];
self.flowLayout.itemSize = CGSizeMake(HX_ScreenWidth, HXclViewHeight + hxBottomMargin);
self.flowLayout.minimumLineSpacing = 0;
self.flowLayout.minimumInteritemSpacing = 0;
self.collectionView.pagingEnabled = YES;
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
[self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([HXPhotoEditChartletContentViewCell class]) bundle:[NSBundle hx_photoPickerBundle]] forCellWithReuseIdentifier:@"HXPhotoEditChartletContentViewCellId"];
UILongPressGestureRecognizer *longPgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureRecongizerClick:)];
[self addGestureRecognizer:longPgr];
UIPanGestureRecognizer *panPgr = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panPressGestureRecongizerClick:)];
[self.titleView addGestureRecognizer:panPgr];
self.contentViewBottom = 0;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationWillChanged) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)deviceOrientationWillChanged {
[self removeFromSuperview];
}
- (void)setConfiguration:(HXPhotoEditConfiguration *)configuration {
_configuration = configuration;
if (configuration.requestChartletModels) {
[self.loadingView startAnimating];
HXWeakSelf
configuration.requestChartletModels(^(NSArray<HXPhotoEditChartletTitleModel *> * _Nonnull chartletModels) {
if (!weakSelf) {
return;
}
[weakSelf.loadingView stopAnimating];
weakSelf.models = chartletModels;
[weakSelf.collectionView reloadData];
[weakSelf.titleCollectionView reloadData];
});
}else {
self.models = configuration.chartletModels;
}
}
- (void)setModels:(NSArray *)models {
_models = models;
for (HXPhotoEditChartletTitleModel *model in models) {
model.selected = NO;
}
HXPhotoEditChartletTitleModel *model = models.firstObject;
model.selected = YES;
self.currentSelectTitleIndexPath = [NSIndexPath indexPathForItem:0 inSection:0];
}
- (void)panPressGestureRecongizerClick:(UIPanGestureRecognizer *)panPgr {
CGPoint point = [panPgr translationInView:self.titleView];
if (panPgr.state == UIGestureRecognizerStateBegan) {
CGFloat contentViewBottom = self.contentViewBottom - point.y;
if (contentViewBottom > 0) {
contentViewBottom = 0;
}
self.contentViewBottomConstraint.constant = contentViewBottom;
}else if (panPgr.state == UIGestureRecognizerStateChanged) {
CGFloat contentViewBottom = self.contentViewBottom - point.y;
if (contentViewBottom > 0) {
contentViewBottom = 0;
}
self.contentViewBottomConstraint.constant = contentViewBottom;
}else if (panPgr.state == UIGestureRecognizerStateEnded ||
panPgr.state == UIGestureRecognizerStateCancelled) {
if (self.contentViewBottomConstraint.constant < -100.f) {
[self hide];
}else {
[UIView animateWithDuration:0.15 animations:^{
self.contentViewBottomConstraint.constant = 0;
[self layoutIfNeeded];
}];
}
self.contentViewBottom = 0;
}
}
- (void)longPressGestureRecongizerClick:(UILongPressGestureRecognizer *)longPgr {
CGPoint point = [longPgr locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point];
if (indexPath) {
HXPhotoEditChartletContentViewCell *cell = (HXPhotoEditChartletContentViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
point = [longPgr locationInView:cell];
point = [cell convertPoint:point toView:cell.collectionView];
NSIndexPath *contentIndexPath = [cell.collectionView indexPathForItemAtPoint:point];
if (contentIndexPath) {
HXPhotoEditChartletListViewCell *contentCell = (HXPhotoEditChartletListViewCell *)[cell.collectionView cellForItemAtIndexPath:contentIndexPath];
CGRect cellFrame = [cell.collectionView convertRect:contentCell.frame toView:self];
CGFloat cellY = cellFrame.origin.y;
if (cellY < self.contentView.hx_y + 60.f) {
cellY = self.contentView.hx_y + 60.f;
}
CGPoint showPoint = CGPointMake(cellFrame.origin.x + cellFrame.size.width / 2, cellY);
if (!self.currentPreview) {
contentCell.showMask = YES;
self.currentPreview = [self createdPreviewViewWithImage:contentCell.imageView.image point:showPoint cell:contentCell];
[self addSubview:self.currentPreview];
[UIView animateWithDuration:0.2 animations:^{
self.currentPreview.alpha = 1;
}];
}else {
if (contentCell != self.currentPreview.cell) {
[self.currentPreview removeFromSuperview];
self.currentPreview = [self createdPreviewViewWithImage:contentCell.imageView.image point:showPoint cell:contentCell];
self.currentPreview.alpha = 1;
[self addSubview:self.currentPreview];
}
}
}else {
[self removePreviewView];
}
}else {
[self removePreviewView];
}
if (longPgr.state == UIGestureRecognizerStateEnded ||
longPgr.state == UIGestureRecognizerStateCancelled) {
[self removePreviewView];
}
}
- (void)removePreviewView {
HXPhotoEditChartletPreviewView *previewView = self.currentPreview;
previewView.cell.showMask = NO;
[UIView animateWithDuration:0.2 animations:^{
previewView.alpha = 0;
} completion:^(BOOL finished) {
[previewView removeFromSuperview];
}];
self.currentPreview = nil;
}
- (HXPhotoEditChartletPreviewView *)createdPreviewViewWithImage:(UIImage *)image point:(CGPoint)point cell:(HXPhotoEditChartletListViewCell *)cell {
HXPhotoEditChartletPreviewView *preview = [HXPhotoEditChartletPreviewView showPreviewWithModel:cell.model atPoint:point];
preview.alpha = 0;
preview.cell = cell;
return preview;
}
- (IBAction)pullDownBtnClick:(UIButton *)sender {
[self hide];
}
- (void)show {
[self layoutIfNeeded];
[UIView animateWithDuration:0.25 animations:^{
self.bgView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3];
self.contentViewBottomConstraint.constant = 0;
[self layoutIfNeeded];
}];
}
- (void)hide {
[UIView animateWithDuration:0.25 animations:^{
self.bgView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];
self.contentViewBottomConstraint.constant = -(HXclViewHeight + hxBottomMargin);
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
if (collectionView == self.titleCollectionView) {
return 1;
}
return self.models.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (collectionView == self.titleCollectionView) {
return self.models.count;
}
return 1;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.collectionView) {
HXPhotoEditChartletContentViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"HXPhotoEditChartletContentViewCellId" forIndexPath:indexPath];
HXPhotoEditChartletTitleModel *titleModel = self.models[indexPath.section];
cell.models = titleModel.models;
HXWeakSelf
cell.selectCellBlock = ^(UIImage * _Nonnull image) {
if (weakSelf.selectImageCompletion) {
weakSelf.selectImageCompletion(image);
}
[weakSelf hide];
};
return cell;
}
HXPhotoEditChartletListViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"HXPhotoEditChartletListViewCellId" forIndexPath:indexPath];
cell.titleModel = self.models[indexPath.item];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.titleCollectionView) {
HXPhotoEditChartletTitleModel *titleModel = self.models[indexPath.item];
[(HXPhotoEditChartletListViewCell *)cell setShowMask:titleModel.selected isAnimate:NO];
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.titleCollectionView) {
if (self.currentSelectTitleIndexPath.item == indexPath.item) {
return;
}
[self.collectionView setContentOffset:CGPointMake(HX_ScreenWidth * indexPath.item, 0) animated:NO];
return;
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.collectionView) {
CGFloat width = HX_ScreenWidth;
CGFloat offsetx = self.collectionView.contentOffset.x;
if (self.models.count) {
NSInteger currentIndex = (offsetx + width * 0.5) / width;
if (currentIndex > self.models.count - 1) {
currentIndex = self.models.count - 1;
}
if (currentIndex < 0) {
currentIndex = 0;
}
if (self.currentSelectTitleIndexPath.item == currentIndex) {
return;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:currentIndex inSection:0];
HXPhotoEditChartletListViewCell *cell = (HXPhotoEditChartletListViewCell *)[self.titleCollectionView cellForItemAtIndexPath:indexPath];
cell.showMask = YES;
cell.titleModel.selected = YES;
HXPhotoEditChartletListViewCell *selectCell = (HXPhotoEditChartletListViewCell *)[self.titleCollectionView cellForItemAtIndexPath:self.currentSelectTitleIndexPath];
if (selectCell) {
selectCell.showMask = NO;
selectCell.titleModel.selected = NO;
}else {
HXPhotoEditChartletTitleModel *titleModel = self.models[self.currentSelectTitleIndexPath.item];
titleModel.selected = NO;
}
self.currentSelectTitleIndexPath = indexPath;
[self.titleCollectionView scrollToItemAtIndexPath:self.currentSelectTitleIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
}
}
@end
@interface HXPhotoEditChartletListViewCell ()
@property (strong, nonatomic) UIView *bgMaskView;
@property (strong, nonatomic) UIActivityIndicatorView *loadingView;
@end
@implementation HXPhotoEditChartletListViewCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self.contentView addSubview:self.bgMaskView];
[self.contentView addSubview:self.imageView];
[self.contentView addSubview:self.loadingView];
}
return self;
}
- (void)setTitleModel:(HXPhotoEditChartletTitleModel *)titleModel {
_titleModel = titleModel;
if (titleModel.type == HXPhotoEditChartletModelType_Image) {
[self.loadingView stopAnimating];
self.imageView.image = titleModel.image;
}else if (titleModel.type == HXPhotoEditChartletModelType_ImageNamed) {
[self.loadingView stopAnimating];
UIImage *image = [UIImage hx_imageContentsOfFile:titleModel.imageNamed];
self.imageView.image = image;
}else if (titleModel.type == HXPhotoEditChartletModelType_NetworkURL) {
HXWeakSelf
if (!titleModel.loadCompletion) {
[self.loadingView startAnimating];
}
[self.imageView hx_setImageWithURL:titleModel.networkURL progress:^(CGFloat progress) {
if (progress < 1) {
[weakSelf.loadingView startAnimating];
}
} completed:^(UIImage *image, NSError *error) {
weakSelf.titleModel.loadCompletion = YES;
[weakSelf.loadingView stopAnimating];
}];
}
[self setShowMask:titleModel.selected isAnimate:NO];
self.showMask = titleModel.selected;
}
- (void)setModel:(HXPhotoEditChartletModel *)model {
_model = model;
if (model.type == HXPhotoEditChartletModelType_Image) {
[self.loadingView stopAnimating];
self.imageView.image = model.image;
}else if (model.type == HXPhotoEditChartletModelType_ImageNamed) {
[self.loadingView stopAnimating];
UIImage *image = [UIImage hx_imageContentsOfFile:model.imageNamed];
self.imageView.image = image;
}else if (model.type == HXPhotoEditChartletModelType_NetworkURL) {
if (!model.loadCompletion) {
[self.loadingView startAnimating];
}
HXWeakSelf
[self.imageView hx_setImageWithURL:model.networkURL progress:^(CGFloat progress) {
if (progress < 1) {
[weakSelf.loadingView startAnimating];
}
} completed:^(UIImage *image, NSError *error) {
weakSelf.model.loadCompletion = YES;
[weakSelf.loadingView stopAnimating];
}];
}
}
- (void)setShowMask:(BOOL)showMask {
[self setShowMask:showMask isAnimate:YES];
}
- (void)setShowMask:(BOOL)showMask isAnimate:(BOOL)isAnimate {
_showMask = showMask;
if (isAnimate) {
[UIView animateWithDuration:0.2 animations:^{
self.bgMaskView.alpha = showMask ? 1 :0;
}];
}else {
self.bgMaskView.alpha = showMask ? 1 :0;
}
}
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = CGRectMake(2.5, 2.5, self.hx_w - 5, self.hx_h - 5);
self.bgMaskView.frame = CGRectMake(-5, -5, self.hx_w + 10, self.hx_h + 10);
self.loadingView.center = CGPointMake(self.hx_w / 2, self.hx_h / 2);
if (HX_IOS11_Earlier) {
[self.bgMaskView hx_radiusWithRadius:5.f corner:UIRectCornerAllCorners];
}
}
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [[UIImageView alloc] init];
_imageView.clipsToBounds = YES;
_imageView.contentMode = UIViewContentModeScaleAspectFit;
}
return _imageView;
}
- (UIView *)bgMaskView {
if (!_bgMaskView) {
_bgMaskView = [[UIView alloc] init];
_bgMaskView.alpha = 0;
_bgMaskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3f];
if (HX_IOS11_Later) {
[_bgMaskView hx_radiusWithRadius:5.f corner:UIRectCornerAllCorners];
}
}
return _bgMaskView;
}
- (UIActivityIndicatorView *)loadingView {
if (!_loadingView) {
_loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
_loadingView.hidesWhenStopped = YES;
}
return _loadingView;
}
@end

View File

@ -0,0 +1,18 @@
//
// HXPhotoEditChartletPreviewView.h
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class HXPhotoEditChartletListViewCell, HXPhotoEditChartletModel;
@interface HXPhotoEditChartletPreviewView : UIView
@property (weak, nonatomic) HXPhotoEditChartletListViewCell *cell;
+ (instancetype)showPreviewWithModel:(HXPhotoEditChartletModel *)model atPoint:(CGPoint)point;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,170 @@
//
// HXPhotoEditChartletPreviewView.m
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditChartletPreviewView.h"
#import "UIView+HXExtension.h"
#import "HXPhotoDefine.h"
#import "HXPhotoEditChartletModel.h"
#import "UIImage+HXExtension.h"
#import "UIImageView+HXExtension.h"
#import "NSBundle+HXPhotoPicker.h"
@interface HXPhotoEditChartletPreviewView ()
@property (strong, nonatomic) UIActivityIndicatorView *loadingView;
@property (weak, nonatomic) IBOutlet UIView *contentView;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (assign, nonatomic) CGSize imageSize;
@property (assign, nonatomic) CGRect viewFrame;
@property (assign, nonatomic) CGPoint point;
@property (assign, nonatomic) CGFloat triangleX;
@property (strong, nonatomic) HXPhotoEditChartletModel *model;
@end
@implementation HXPhotoEditChartletPreviewView
+ (instancetype)showPreviewWithModel:(HXPhotoEditChartletModel *)model atPoint:(CGPoint)point {
HXPhotoEditChartletPreviewView *view = [self initView];
view.point = point;
view.model = model;
return view;
}
+ (instancetype)initView {
return [[[NSBundle hx_photoPickerBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
CGContextMoveToPoint (context, self.triangleX - 15, self.hx_h - 20);
CGContextAddLineToPoint(context, self.triangleX + 15, self.hx_h - 20);
CGContextAddLineToPoint(context, self.triangleX, self.hx_h);
CGContextClosePath(context);
CGContextSetRGBFillColor(context, 1, 1, 1, 1);
CGContextFillPath(context);
}
- (void)setModel:(HXPhotoEditChartletModel *)model {
_model = model;
if (model.type == HXPhotoEditChartletModelType_Image) {
self.imageView.image = model.image;
self.imageSize = self.imageView.image.size;
[self updateFrame];
}else if (model.type == HXPhotoEditChartletModelType_ImageNamed) {
UIImage *image = [UIImage hx_imageContentsOfFile:model.imageNamed];
self.imageView.image = image;
self.imageSize = self.imageView.image.size;
[self updateFrame];
}else if (model.type == HXPhotoEditChartletModelType_NetworkURL) {
self.loadingView.hidden = NO;
[self.loadingView startAnimating];
self.imageSize = CGSizeMake(HX_ScreenWidth / 3, HX_ScreenWidth / 3);
[self updateFrame];
[self layoutIfNeeded];
HXWeakSelf
[self.imageView hx_setImageWithURL:model.networkURL progress:^(CGFloat progress) {
if (progress < 1) {
weakSelf.loadingView.hidden = NO;
[weakSelf.loadingView startAnimating];
}
} completed:^(UIImage *image, NSError *error) {
weakSelf.loadingView.hidden = YES;
[weakSelf.loadingView stopAnimating];
weakSelf.viewFrame = CGRectZero;
weakSelf.imageSize = weakSelf.imageView.image.size;
[UIView animateWithDuration:0.25 animations:^{
[weakSelf updateFrame];
[weakSelf layoutIfNeeded];
[weakSelf setNeedsDisplay];
}];
}];
}
}
- (void)updateFrame {
self.frame = self.viewFrame;
self.loadingView.center = CGPointMake(self.hx_w / 2, self.hx_h / 2 - 5);
}
- (CGRect)viewFrame {
if (CGRectIsEmpty(_viewFrame)) {
CGFloat width = HX_ScreenWidth / 2;
CGFloat height = width;
CGFloat imgWidth = self.imageSize.width;
CGFloat imgHeight = self.imageSize.height;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown || HX_UI_IS_IPAD) {
if (imgWidth > imgHeight) {
width = HX_ScreenWidth - 40;
}else if (imgHeight > imgWidth) {
height = HX_ScreenWidth - 40;
}
}else {
width = HX_ScreenHeight / 2;
height = width;
if (imgWidth > imgHeight) {
width = HX_ScreenHeight - 40;
}else if (imgHeight > imgWidth) {
height = HX_ScreenHeight - 40;
}
}
CGFloat w;
CGFloat h;
if (imgWidth > width) {
imgHeight = width / imgWidth * imgHeight;
}
if (imgHeight > height) {
w = height / self.imageSize.height * imgWidth;
h = height;
}else {
if (imgWidth > width) {
w = width;
}else {
w = imgWidth;
}
h = imgHeight;
}
w += 10;
h += 20;
CGFloat x = self.point.x - w / 2;
CGFloat y = self.point.y - 10 - h;
if (x + w > HX_ScreenWidth - 15) {
x = HX_ScreenWidth - 15 - w;
}
if (x < 15) {
x = 15;
}
self.triangleX = self.point.x - x;
_viewFrame = CGRectMake(x, y, w, h);
}
return _viewFrame;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self addSubview:self.loadingView];
if (HX_IOS11_Later) {
[self.contentView hx_radiusWithRadius:5 corner:UIRectCornerAllCorners];
}
self.layer.shadowOffset = CGSizeMake(0, 0);
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 5.f;
self.layer.shadowOpacity = 0.3f;
_viewFrame = CGRectZero;
}
- (void)layoutSubviews {
[super layoutSubviews];
if (HX_IOS11_Earlier) {
[self.contentView hx_radiusWithRadius:5 corner:UIRectCornerAllCorners];
}
}
- (UIActivityIndicatorView *)loadingView {
if (!_loadingView) {
_loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
_loadingView.hidden = YES;
}
return _loadingView;
}
@end

View File

@ -0,0 +1,56 @@
//
// HXPhotoEditClippingToolBar.h
// photoEditDemo
//
// Created by Silence on 2020/6/30.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class HXPhotoEditClippingToolBarRotaioModel;
@interface HXPhotoEditClippingToolBar : UIView
@property (assign, nonatomic) BOOL enableRotaio;
@property (strong, nonatomic) UIColor *themeColor;
@property (assign, nonatomic) BOOL enableReset;
@property (copy, nonatomic) void (^ didBtnBlock)(NSInteger tag);
@property (copy, nonatomic) void (^ didRotateBlock)(void);
@property (copy, nonatomic) void (^ didMirrorHorizontallyBlock)(void);
@property (copy, nonatomic) void (^ selectedRotaioBlock)(HXPhotoEditClippingToolBarRotaioModel *model);
+ (instancetype)initView;
- (void)resetRotate;
- (void)setRotateAlpha:(CGFloat)alpha;
@end
@interface HXPhotoEditClippingToolBarHeader : UICollectionReusableView
@property (assign, nonatomic) BOOL enableRotaio;
@property (copy, nonatomic) void (^ didRotateBlock)(void);
@property (copy, nonatomic) void (^ didMirrorHorizontallyBlock)(void);
@end
@interface HXPhotoEditClippingToolBarHeaderViewCell : UICollectionViewCell
@property (strong, nonatomic) UIColor *themeColor;
@property (strong, nonatomic) HXPhotoEditClippingToolBarRotaioModel *model;
@end
@interface HXPhotoEditClippingToolBarRotaioModel : NSObject
@property (assign, nonatomic) CGSize size;
@property (assign, nonatomic) CGSize scaleSize;
@property (assign, nonatomic) CGFloat widthRatio;
@property (assign, nonatomic) CGFloat heightRatio;
@property (copy, nonatomic) NSString *scaleText;
@property (assign, nonatomic) BOOL isSelected;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,337 @@
//
// HXPhotoEditClippingToolBar.m
// photoEditDemo
//
// Created by Silence on 2020/6/30.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditClippingToolBar.h"
#import "UIImage+HXExtension.h"
#import "NSBundle+HXPhotoPicker.h"
#import "UIButton+HXExtension.h"
#import "UIView+HXExtension.h"
#import "UIFont+HXExtension.h"
#import "UILabel+HXExtension.h"
#import "HXPhotoDefine.h"
#define HXScaleViewSize 28.f
@interface HXPhotoEditClippingToolBar ()<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@property (weak, nonatomic) IBOutlet UIButton *confirmBtn;
@property (weak, nonatomic) IBOutlet UIButton *cancelBtn;
@property (weak, nonatomic) IBOutlet UIButton *resetBtn;
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (copy, nonatomic) NSArray<HXPhotoEditClippingToolBarRotaioModel *> *models;
@property (strong, nonatomic) HXPhotoEditClippingToolBarRotaioModel *currentSelectedModel;
@end
@implementation HXPhotoEditClippingToolBar
+ (instancetype)initView {
return [[[NSBundle hx_photoPickerBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
- (void)awakeFromNib {
[super awakeFromNib];
[self.confirmBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_clip_confirm"] forState:UIControlStateNormal];
[self.cancelBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_clip_cancel"] forState:UIControlStateNormal];
self.resetBtn.enabled = NO;
[self.resetBtn setTitle:[NSBundle hx_localizedStringForKey:@"还原"] forState:UIControlStateNormal];
UICollectionViewFlowLayout *flowlayout = (id)self.collectionView.collectionViewLayout;
flowlayout.minimumInteritemSpacing = 20;
flowlayout.sectionInset = UIEdgeInsetsMake(0, 20, 0, 10);
flowlayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
[self.collectionView registerClass:[HXPhotoEditClippingToolBarHeader class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HXPhotoEditClippingToolBarHeaderId"];
[self.collectionView registerClass:[HXPhotoEditClippingToolBarHeaderViewCell class] forCellWithReuseIdentifier:@"HXPhotoEditClippingToolBarHeaderViewCellId"];
}
- (void)resetRotate {
self.currentSelectedModel.isSelected = NO;
self.currentSelectedModel = self.models.firstObject;
self.currentSelectedModel.isSelected = YES;
[self.collectionView reloadData];
}
- (void)setRotateAlpha:(CGFloat)alpha {
if (alpha == 1) {
self.collectionView.userInteractionEnabled = YES;
}else {
self.collectionView.userInteractionEnabled = NO;
}
self.collectionView.alpha = alpha;
}
- (void)setEnableReset:(BOOL)enableReset {
_enableReset = enableReset;
self.resetBtn.enabled = enableReset;
}
- (IBAction)didBtnClick:(UIButton *)sender {
if (self.enableRotaio) {
[self resetRotate];
[self.collectionView setContentOffset:CGPointMake(0, 0) animated:YES];
}
if (self.didBtnBlock) {
self.didBtnBlock(sender.tag);
}
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (self.enableRotaio) {
return self.models.count;
}else {
return 0;
}
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
HXPhotoEditClippingToolBarHeaderViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"HXPhotoEditClippingToolBarHeaderViewCellId" forIndexPath:indexPath];
cell.themeColor = self.themeColor;
cell.model = self.models[indexPath.item];
return cell;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
HXPhotoEditClippingToolBarHeader *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"HXPhotoEditClippingToolBarHeaderId" forIndexPath:indexPath];
header.enableRotaio = self.enableRotaio;
header.didRotateBlock = self.didRotateBlock;
header.didMirrorHorizontallyBlock = self.didMirrorHorizontallyBlock;
return header;
}
return [UICollectionReusableView new];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
HXPhotoEditClippingToolBarRotaioModel *model = self.models[indexPath.item];
if (!CGSizeEqualToSize(model.scaleSize, CGSizeZero)) {
return model.scaleSize;
}
CGFloat scaleWidth = HXScaleViewSize + 10;
if (!model.widthRatio || model.widthRatio == 1) {
model.size = CGSizeMake(HXScaleViewSize, HXScaleViewSize);
model.scaleSize = CGSizeMake(HXScaleViewSize, scaleWidth);
}else {
CGFloat scale = scaleWidth / model.widthRatio;
CGFloat width = model.widthRatio * scale;
CGFloat height = model.heightRatio * scale;
if (height > scaleWidth) {
height = scaleWidth;
width = scaleWidth / model.heightRatio * model.widthRatio;
}
model.size = CGSizeMake(width, height);
if (height < scaleWidth) {
height = scaleWidth;
}
CGFloat textWidth = [UILabel hx_getTextWidthWithText:model.scaleText height:height - 3 font:[UIFont hx_mediumHelveticaNeueOfSize:12]] + 5;
if (width < textWidth) {
height = textWidth / width * height;
width = textWidth;
if (height > 45.f) {
height = 45.f;
}
model.size = CGSizeMake(width, height);
}
model.scaleSize = CGSizeMake(width, height);
}
return model.scaleSize;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
return CGSizeMake(100, 50);
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *selectedIndexPath;
if (self.currentSelectedModel) {
selectedIndexPath = [NSIndexPath indexPathForItem:[self.models indexOfObject:self.currentSelectedModel] inSection:0];
}
if (selectedIndexPath && selectedIndexPath.item == indexPath.item) {
return;
}
self.currentSelectedModel.isSelected = NO;
HXPhotoEditClippingToolBarRotaioModel *model = self.models[indexPath.item];
model.isSelected = YES;
if (self.selectedRotaioBlock) {
self.selectedRotaioBlock(model);
}
self.currentSelectedModel = model;
if (selectedIndexPath) {
[collectionView reloadItemsAtIndexPaths:@[indexPath, selectedIndexPath]];
}else {
[collectionView reloadItemsAtIndexPaths:@[indexPath]];
}
}
- (NSArray<HXPhotoEditClippingToolBarRotaioModel *> *)models {
if (!_models) {
NSArray *scaleArray = @[@[@0, @0], @[@1, @1], @[@3, @2], @[@2, @3], @[@4, @3], @[@3, @4], @[@16, @9], @[@9, @16]];
NSMutableArray *modelArray = [NSMutableArray array];
for (NSArray *array in scaleArray) {
HXPhotoEditClippingToolBarRotaioModel *model = [[HXPhotoEditClippingToolBarRotaioModel alloc] init];
model.widthRatio = [array.firstObject floatValue];
model.heightRatio = [array.lastObject floatValue];
if (modelArray.count == 0) {
model.isSelected = YES;
self.currentSelectedModel = model;
}
[modelArray addObject:model];
}
_models = modelArray.copy;
}
return _models;
}
@end
@interface HXPhotoEditClippingToolBarHeader ()
@property (strong, nonatomic) UIButton *rotateBtn;
@property (strong, nonatomic) UIButton *mirrorHorizontallyBtn;
@property (strong, nonatomic) UIView *lineView;
@end
@implementation HXPhotoEditClippingToolBarHeader
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.rotateBtn];
[self addSubview:self.mirrorHorizontallyBtn];
[self addSubview:self.lineView];
}
return self;
}
- (void)setEnableRotaio:(BOOL)enableRotaio {
_enableRotaio = enableRotaio;
self.lineView.hidden = !enableRotaio;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.rotateBtn.hx_centerY = self.hx_h / 2;
self.rotateBtn.hx_x = 20;
self.mirrorHorizontallyBtn.hx_x = CGRectGetMaxX(self.rotateBtn.frame) + 10;
self.mirrorHorizontallyBtn.hx_centerY = self.rotateBtn.hx_centerY;
self.lineView.hx_x = self.hx_w - 2;
self.lineView.hx_centerY = self.hx_h / 2;
}
- (UIButton *)rotateBtn {
if (!_rotateBtn) {
_rotateBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_rotateBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_clip_rotate"] forState:UIControlStateNormal];
_rotateBtn.hx_size = _rotateBtn.currentImage.size;
_rotateBtn.tintColor = [UIColor whiteColor];
[_rotateBtn addTarget:self action:@selector(didRotateBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[_rotateBtn hx_setEnlargeEdgeWithTop:5 right:5 bottom:5 left:5];
}
return _rotateBtn;
}
- (void)didRotateBtnClick:(UIButton *)button {
if (self.didRotateBlock) {
self.didRotateBlock();
}
}
- (UIButton *)mirrorHorizontallyBtn {
if (!_mirrorHorizontallyBtn) {
_mirrorHorizontallyBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_mirrorHorizontallyBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_clip_mirror_horizontally"] forState:UIControlStateNormal];
_mirrorHorizontallyBtn.hx_size = _rotateBtn.currentImage.size;
_mirrorHorizontallyBtn.tintColor = [UIColor whiteColor];
[_mirrorHorizontallyBtn addTarget:self action:@selector(didMirrorHorizontallyBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[_mirrorHorizontallyBtn hx_setEnlargeEdgeWithTop:5 right:5 bottom:5 left:5];
}
return _mirrorHorizontallyBtn;
}
- (void)didMirrorHorizontallyBtnClick:(UIButton *)button {
if (self.didMirrorHorizontallyBlock) {
self.didMirrorHorizontallyBlock();
}
}
- (UIView *)lineView {
if (!_lineView) {
_lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2, 20)];
_lineView.backgroundColor = [UIColor whiteColor];
}
return _lineView;
}
@end
@interface HXPhotoEditClippingToolBarHeaderViewCell ()
@property (strong, nonatomic) UIImageView *scaleImageView;
@property (strong, nonatomic) UIView *scaleView;
@property (strong, nonatomic) UILabel *scaleLb;
@end
@implementation HXPhotoEditClippingToolBarHeaderViewCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self.contentView addSubview:self.scaleView];
}
return self;
}
- (void)setModel:(HXPhotoEditClippingToolBarRotaioModel *)model {
_model = model;
[self setSubviewFrame];
self.scaleLb.text = model.scaleText;
if (!model.widthRatio) {
self.scaleView.layer.borderWidth = 0.f;
self.scaleImageView.hidden = NO;
}else {
self.scaleView.layer.borderWidth = 1.25f;
self.scaleImageView.hidden = YES;
}
if (model.isSelected) {
self.scaleImageView.tintColor = self.themeColor;
self.scaleView.layer.borderColor = self.themeColor.CGColor;
self.scaleLb.textColor = self.themeColor;
}else {
self.scaleImageView.tintColor = UIColor.whiteColor;
self.scaleView.layer.borderColor = UIColor.whiteColor.CGColor;
self.scaleLb.textColor = [UIColor whiteColor];
}
}
- (void)setSubviewFrame {
self.scaleView.hx_size = self.model.size;
self.scaleView.center = CGPointMake(self.model.scaleSize.width / 2, self.model.scaleSize.height / 2);
self.scaleLb.frame = CGRectMake(1.5, 1.5, self.scaleView.hx_w - 3, self.scaleView.hx_h - 3);
self.scaleImageView.frame = self.scaleView.bounds;
if (HX_IOS11_Earlier) {
[self.scaleView hx_radiusWithRadius:2 corner:UIRectCornerAllCorners];
}
}
- (UIView *)scaleView {
if (!_scaleView) {
_scaleView = [[UIView alloc] init];
if (HX_IOS11_Later) {
[_scaleView hx_radiusWithRadius:2 corner:UIRectCornerAllCorners];
}
[_scaleView addSubview:self.scaleImageView];
[_scaleView addSubview:self.scaleLb];
}
return _scaleView;
}
- (UIImageView *)scaleImageView {
if (!_scaleImageView) {
_scaleImageView = [[UIImageView alloc] initWithImage:[[UIImage hx_imageContentsOfFile:@"hx_photo_edit_clip_free"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
}
return _scaleImageView;
}
- (UILabel *)scaleLb {
if (!_scaleLb) {
_scaleLb = [[UILabel alloc] init];
_scaleLb.textColor = [UIColor whiteColor];
_scaleLb.textAlignment = NSTextAlignmentCenter;
_scaleLb.font = [UIFont hx_mediumHelveticaNeueOfSize:12];
}
return _scaleLb;
}
@end
@implementation HXPhotoEditClippingToolBarRotaioModel
- (NSString *)scaleText {
if (!self.widthRatio) {
return [NSBundle hx_localizedStringForKey:@"自由"];
}else {
return [NSString stringWithFormat:@"%ld:%ld", (NSInteger)self.widthRatio, (NSInteger)self.heightRatio];
}
}
@end

View File

@ -0,0 +1,40 @@
//
// HXPhotoEditDrawView.h
// photoEditDemo
//
// Created by Silence on 2020/6/23.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditDrawView : UIView
@property (assign, nonatomic) CGFloat lineWidth;
@property (strong, nonatomic) UIColor *lineColor;
@property (copy, nonatomic) void (^ beganDraw)(void);
@property (copy, nonatomic) void (^ endDraw)(void);
/** 正在绘画 */
@property (nonatomic, readonly) BOOL isDrawing;
/** 图层数量 */
@property (nonatomic, readonly) NSUInteger count;
/// 显示界面的缩放率
@property (nonatomic, assign) CGFloat screenScale;
@property (assign, nonatomic) BOOL enabled;
/** 数据 */
@property (nonatomic, strong, nullable) NSDictionary *data;
/// 是否可以撤销
- (BOOL)canUndo;
/// 撤销
- (void)undo;
- (void)clearCoverage;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,176 @@
//
// HXPhotoEditDrawView.m
// photoEditDemo
//
// Created by Silence on 2020/6/23.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditDrawView.h"
NSString *const kHXDrawViewData = @"HXDrawViewData";
@interface HXDrawBezierPath : UIBezierPath
@property (strong, nonatomic) UIColor *color;
@end
@implementation HXDrawBezierPath
@end
@interface HXPhotoEditDrawView ()
///
@property (strong, nonatomic) NSMutableArray <HXDrawBezierPath *>*lineArray;
///
@property (strong, nonatomic) NSMutableArray <CAShapeLayer *>*slayerArray;
@property (assign, nonatomic) BOOL isWork;
@property (assign, nonatomic) BOOL isBegan;
@end
@implementation HXPhotoEditDrawView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.lineWidth = 5.f;
self.lineColor = [UIColor whiteColor];
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
self.exclusiveTouch = YES;
self.lineArray = @[].mutableCopy;
self.slayerArray = @[].mutableCopy;
self.enabled = YES;
self.screenScale = 1;
}
return self;
}
- (CAShapeLayer *)createShapeLayer:(HXDrawBezierPath *)path {
CAShapeLayer *slayer = [CAShapeLayer layer];
slayer.path = path.CGPath;
slayer.backgroundColor = [UIColor clearColor].CGColor;
slayer.fillColor = [UIColor clearColor].CGColor;
slayer.lineCap = kCALineCapRound;
slayer.lineJoin = kCALineJoinRound;
slayer.strokeColor = path.color.CGColor;
slayer.lineWidth = path.lineWidth;
return slayer;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if ([event allTouches].count == 1 && self.enabled) {
self.isWork = NO;
self.isBegan = YES;
HXDrawBezierPath *path = [[HXDrawBezierPath alloc] init];
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
path.lineWidth = self.lineWidth;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path moveToPoint:point];
path.color = self.lineColor;
[self.lineArray addObject:path];
CAShapeLayer *slayer = [self createShapeLayer:path];
[self.layer addSublayer:slayer];
[self.slayerArray addObject:slayer];
} else {
[super touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if ([event allTouches].count == 1 && self.enabled){
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
HXDrawBezierPath *path = self.lineArray.lastObject;
if (!CGPointEqualToPoint(path.currentPoint, point)) {
if (self.beganDraw) {
self.beganDraw();
}
self.isBegan = NO;
self.isWork = YES;
[path addLineToPoint:point];
CAShapeLayer *slayer = self.slayerArray.lastObject;
slayer.path = path.CGPath;
}
} else {
[super touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
if ([event allTouches].count == 1 && self.enabled){
if (self.isWork) {
if (self.endDraw) {
self.endDraw();
}
} else {
[self undo];
}
} else {
[super touchesEnded:touches withEvent:event];
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if ([event allTouches].count == 1 && self.enabled){
if (self.isWork) {
if (self.endDraw) {
self.endDraw();
}
} else {
[self undo];
}
} else {
[super touchesCancelled:touches withEvent:event];
}
}
- (void)setEnabled:(BOOL)enabled {
_enabled = enabled;
}
- (BOOL)canUndo {
return self.lineArray.count;
}
- (void)undo {
[self.slayerArray.lastObject removeFromSuperlayer];
[self.slayerArray removeLastObject];
[self.lineArray removeLastObject];
}
- (BOOL)isDrawing {
if (!self.userInteractionEnabled || !self.enabled) {
return NO;
}
return _isWork;
}
/** */
- (NSUInteger)count {
return self.lineArray.count;
}
- (void)clearCoverage {
[self.lineArray removeAllObjects];
[self.slayerArray performSelector:@selector(removeFromSuperlayer)];
[self.slayerArray removeAllObjects];
}
#pragma mark -
- (NSDictionary *)data {
if (self.lineArray.count) {
return @{kHXDrawViewData:[self.lineArray copy]};
}
return nil;
}
- (void)setData:(NSDictionary *)data {
NSArray *lineArray = data[kHXDrawViewData];
if (lineArray.count) {
for (HXDrawBezierPath *path in lineArray) {
CAShapeLayer *slayer = [self createShapeLayer:path];
[self.layer addSublayer:slayer];
[self.slayerArray addObject:slayer];
}
[self.lineArray addObjectsFromArray:lineArray];
}
}
@end

View File

@ -0,0 +1,22 @@
//
// HXPhotoEditGraffitiColorSizeView.h
// HXPhotoPickerExample
//
// Created by Silence on 2020/8/14.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditGraffitiColorSizeView : UIView
@property (assign, nonatomic) CGFloat scale;
@property (copy, nonatomic) void (^ changeColorSize)(CGFloat scale);
+ (instancetype)initView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,70 @@
//
// HXPhotoEditGraffitiColorSizeView.m
// HXPhotoPickerExample
//
// Created by Silence on 2020/8/14.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGraffitiColorSizeView.h"
#import "UIView+HXExtension.h"
#import "HXPreviewVideoView.h"
#import "NSBundle+HXPhotoPicker.h"
#import "UIImage+HXExtension.h"
@interface HXPhotoEditGraffitiColorSizeView ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIView *indicatorView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *indicatorViewCenterYConstraint;
@property (assign, nonatomic) CGFloat indicatorCenterY;
@end
@implementation HXPhotoEditGraffitiColorSizeView
+ (instancetype)initView {
return [[[NSBundle hx_photoPickerBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(CGRectMake(self.indicatorView.hx_x - 10, self.indicatorView.hx_y - 10, self.indicatorView.hx_w + 20, self.indicatorView.hx_h + 20), point)) {
return self.indicatorView;
}
return [super hitTest:point withEvent:event];
}
- (void)awakeFromNib {
[super awakeFromNib];
self.scale = 0.5f;
self.imageView.image = [UIImage hx_imageContentsOfFile:@"hx_photo_edit_graffiti_size_backgroud"];
[self.indicatorView hx_radiusWithRadius:10.f corner:UIRectCornerAllCorners];
self.indicatorView.layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.6f].CGColor;
self.indicatorView.layer.shadowOpacity = 0.56f;
self.indicatorView.layer.shadowRadius = 10.f;
HXPanGestureRecognizer *panGesture = [[HXPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognizerClick:)];
[self.indicatorView addGestureRecognizer:panGesture];
}
- (void)panGestureRecognizerClick:(UIPanGestureRecognizer *)panGesture {
CGPoint point = [panGesture translationInView:self];
if (panGesture.state == UIGestureRecognizerStateBegan) {
self.indicatorCenterY = self.indicatorViewCenterYConstraint.constant;
}
CGFloat centerY = self.indicatorCenterY + point.y;
if (centerY < -(self.hx_h / 2)) {
centerY = -(self.hx_h / 2);
}else if (centerY > self.hx_h / 2) {
centerY = self.hx_h / 2;
}
self.indicatorViewCenterYConstraint.constant = centerY;
CGFloat scale = 1 - self.indicatorView.hx_centerY / self.hx_h;
self.scale = scale;
if (self.changeColorSize) {
self.changeColorSize(scale);
}
}
@end

View File

@ -0,0 +1,22 @@
//
// HXPhotoEditGraffitiColorView.h
// photoEditDemo
//
// Created by Silence on 2020/6/20.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditGraffitiColorView : UIView
@property (copy, nonatomic) NSArray<UIColor *> *drawColors;
@property (assign, nonatomic) NSInteger defaultDarwColorIndex;
@property (copy, nonatomic) void (^ selectColorBlock)(UIColor *color);
@property (copy, nonatomic) void (^ undoBlock)(void);
@property (assign, nonatomic) BOOL undo;
+ (instancetype)initView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,92 @@
//
// HXPhotoEditGraffitiColorView.m
// photoEditDemo
//
// Created by Silence on 2020/6/20.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGraffitiColorView.h"
#import "HXPhotoEditGraffitiColorViewCell.h"
#import "UIImage+HXExtension.h"
#import "NSBundle+HXPhotoPicker.h"
@interface HXPhotoEditGraffitiColorView ()<UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet UICollectionViewFlowLayout *flowLayout;
@property (weak, nonatomic) IBOutlet UIButton *repealBtn;
@property (strong, nonatomic) NSMutableArray *colorModels;
@property (strong, nonatomic) HXPhotoEditGraffitiColorModel *currentSelectModel;
@end
@implementation HXPhotoEditGraffitiColorView
+ (instancetype)initView {
return [[[NSBundle hx_photoPickerBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
- (void)awakeFromNib {
[super awakeFromNib];
self.repealBtn.enabled = NO;
self.flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.flowLayout.sectionInset = UIEdgeInsetsMake(0, 15, 0, 0);
self.flowLayout.minimumInteritemSpacing = 5;
self.flowLayout.itemSize = CGSizeMake(37.f, 37.f);
[self.repealBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_repeal"] forState:UIControlStateNormal];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
[self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([HXPhotoEditGraffitiColorViewCell class]) bundle:[NSBundle hx_photoPickerBundle]] forCellWithReuseIdentifier:NSStringFromClass([HXPhotoEditGraffitiColorViewCell class])];
}
- (void)setUndo:(BOOL)undo {
_undo = undo;
self.repealBtn.enabled = undo;
}
- (void)setDrawColors:(NSArray<UIColor *> *)drawColors {
_drawColors = drawColors;
self.colorModels = @[].mutableCopy;
for (UIColor *color in drawColors) {
HXPhotoEditGraffitiColorModel *model = [[HXPhotoEditGraffitiColorModel alloc] init];
model.color = color;
[self.colorModels addObject:model];
if (self.colorModels.count == self.defaultDarwColorIndex + 1) {
model.selected = YES;
self.currentSelectModel = model;
}
}
[self.collectionView reloadData];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.colorModels.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
HXPhotoEditGraffitiColorViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([HXPhotoEditGraffitiColorViewCell class]) forIndexPath:indexPath];
cell.model = self.colorModels[indexPath.item];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
HXPhotoEditGraffitiColorModel *model = self.colorModels[indexPath.item];
if (self.currentSelectModel == model) {
return;
}
if (self.currentSelectModel.selected) {
self.currentSelectModel.selected = NO;
HXPhotoEditGraffitiColorViewCell *beforeCell = (HXPhotoEditGraffitiColorViewCell *)[collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:[self.colorModels indexOfObject:self.currentSelectModel] inSection:0]];
[beforeCell setSelected:NO];
}
model.selected = YES;
self.currentSelectModel = model;
if (self.selectColorBlock) {
self.selectColorBlock(model.color);
}
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
- (IBAction)didRepealBtnClick:(id)sender {
if (self.undoBlock) {
self.undoBlock();
}
}
@end

View File

@ -0,0 +1,18 @@
//
// HXPhotoEditGraffitiColorViewCell.h
// photoEditDemo
//
// Created by Silence on 2020/6/22.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoEditGraffitiColorModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditGraffitiColorViewCell : UICollectionViewCell
@property (strong, nonatomic) HXPhotoEditGraffitiColorModel *model;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,55 @@
//
// HXPhotoEditGraffitiColorViewCell.m
// photoEditDemo
//
// Created by Silence on 2020/6/22.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGraffitiColorViewCell.h"
#import "UIView+HXExtension.h"
#import "UIColor+HXExtension.h"
@interface HXPhotoEditGraffitiColorViewCell ()
@property (weak, nonatomic) IBOutlet UIView *colorView;
@property (weak, nonatomic) IBOutlet UIView *colorCenterView;
@end
@implementation HXPhotoEditGraffitiColorViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
self.colorView.backgroundColor = [UIColor whiteColor];
[self.colorView hx_radiusWithRadius:11.f corner:UIRectCornerAllCorners];
[self.colorCenterView hx_radiusWithRadius:16.f / 2.f corner:UIRectCornerAllCorners];
}
- (void)setModel:(HXPhotoEditGraffitiColorModel *)model {
_model = model;
if ([model.color hx_colorIsWhite]) {
self.colorView.backgroundColor = [UIColor hx_colorWithHexStr:@"#dadada"];
}else {
self.colorView.backgroundColor = [UIColor whiteColor];
}
self.colorCenterView.backgroundColor = model.color;
[self setupColor];
}
- (void)setupColor {
if (self.model.selected) {
self.colorView.transform = CGAffineTransformMakeScale(1.2, 1.2);
}else {
self.colorView.transform = CGAffineTransformIdentity;
}
}
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
self.model.selected = selected;
[UIView animateWithDuration:0.2 animations:^{
[self setupColor];
}];
}
@end

View File

@ -0,0 +1,23 @@
//
// HXPhotoEditGridLayer.h
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditGridLayer : CAShapeLayer
@property (nonatomic, assign) CGRect gridRect;
- (void)setGridRect:(CGRect)gridRect animated:(BOOL)animated;
- (void)setGridRect:(CGRect)gridRect animated:(BOOL)animated completion:(void (^ _Nullable)(BOOL finished))completion;
@property (nonatomic, assign) BOOL isRound;
@property (nonatomic, strong) UIColor *bgColor;
@property (nonatomic, strong) UIColor *gridColor;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,239 @@
//
// HXPhotoEditGridLayer.m
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGridLayer.h"
NSString *const hxGridLayerAnimtionKey = @"hxGridLayerAnimtionKey";
@interface HXPhotoEditGridLayer ()<CAAnimationDelegate>
@property (weak, nonatomic) CAShapeLayer *topLeftCornerLayer;
@property (weak, nonatomic) CAShapeLayer *topRightCornerLayer;
@property (weak, nonatomic) CAShapeLayer *bottomLeftCornerLayer;
@property (weak, nonatomic) CAShapeLayer *bottomRightCornerLayer;
@property (weak, nonatomic) CAShapeLayer *middleLineLayer;
@property (nonatomic, copy) void (^callback)(BOOL finished);
@end
@implementation HXPhotoEditGridLayer
- (instancetype)init {
self = [super init];
if (self) {
CAShapeLayer *topLeftCornerLayer = [self creatLayer:NO];
CAShapeLayer *topRightCornerLayer = [self creatLayer:NO];
CAShapeLayer *bottomLeftCornerLayer = [self creatLayer:NO];
CAShapeLayer *bottomRightCornerLayer = [self creatLayer:NO];
CAShapeLayer *middleLineLayer = [self creatLayer:YES];
[self addSublayer:topLeftCornerLayer];
[self addSublayer:topRightCornerLayer];
[self addSublayer:bottomLeftCornerLayer];
[self addSublayer:bottomRightCornerLayer];
[self addSublayer:middleLineLayer];
self.topLeftCornerLayer = topLeftCornerLayer;
self.topRightCornerLayer = topRightCornerLayer;
self.bottomLeftCornerLayer = bottomLeftCornerLayer;
self.bottomRightCornerLayer = bottomRightCornerLayer;
self.middleLineLayer = middleLineLayer;
self.contentsScale = [[UIScreen mainScreen] scale];
_bgColor = [UIColor clearColor];
_gridColor = [UIColor blackColor];
self.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.6].CGColor;
self.shadowRadius = 2.f;
self.shadowOffset = CGSizeZero;
self.shadowOpacity = .5f;
}
return self;
}
- (CAShapeLayer *)creatLayer:(BOOL)shadow {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.contentsScale = [[UIScreen mainScreen] scale];
if (shadow) {
layer.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4].CGColor;
layer.shadowRadius = 1.f;
layer.shadowOffset = CGSizeZero;
layer.shadowOpacity = .4f;
}
return layer;
}
- (void)setIsRound:(BOOL)isRound {
_isRound = isRound;
if (isRound) {
[self.topLeftCornerLayer removeFromSuperlayer];
[self.topRightCornerLayer removeFromSuperlayer];
[self.bottomLeftCornerLayer removeFromSuperlayer];
[self.bottomRightCornerLayer removeFromSuperlayer];
[self.middleLineLayer removeFromSuperlayer];
}
}
- (void)setGridRect:(CGRect)gridRect {
[self setGridRect:gridRect animated:NO];
}
- (void)setGridRect:(CGRect)gridRect animated:(BOOL)animated {
[self setGridRect:gridRect animated:animated completion:nil];
}
- (void)setGridRect:(CGRect)gridRect animated:(BOOL)animated completion:(void (^)(BOOL finished))completion {
_gridRect = gridRect;
if (completion) {
self.callback = completion;
}
CGPathRef path = [self drawGrid];
if (animated) {
if (!self.isRound) {
[self setMiddleLine];
[self setAllCorner:UIRectCornerTopLeft];
[self setAllCorner:UIRectCornerTopRight];
[self setAllCorner:UIRectCornerBottomLeft];
[self setAllCorner:UIRectCornerBottomRight];
}
CABasicAnimation *animate = [CABasicAnimation animationWithKeyPath:@"path"];
animate.duration = 0.25f;
animate.fromValue = (__bridge id _Nullable)(self.path);
animate.toValue = (__bridge id _Nullable)(path);
animate.removedOnCompletion = NO;
animate.delegate = self;
self.path = path;
[self addAnimation:animate forKey:hxGridLayerAnimtionKey];
} else {
if (!self.isRound) {
self.topLeftCornerLayer.path = [self setAllCornerPath:UIRectCornerTopLeft].CGPath;
self.topRightCornerLayer.path = [self setAllCornerPath:UIRectCornerTopRight].CGPath;
self.bottomLeftCornerLayer.path = [self setAllCornerPath:UIRectCornerBottomLeft].CGPath;
self.bottomRightCornerLayer.path = [self setAllCornerPath:UIRectCornerBottomRight].CGPath;
self.middleLineLayer.path = [self setMiddleLinePath].CGPath;
}
self.path = path;
if (self.callback) {
self.callback(YES);
self.callback = nil;
}
}
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if ([self animationForKey:hxGridLayerAnimtionKey] == anim) {
if (self.callback) {
self.callback(flag);
self.callback = nil;
}
[self removeAnimationForKey:hxGridLayerAnimtionKey];
}
}
- (UIBezierPath *)setAllCornerPath:(UIRectCorner)rectConrner {
CGFloat lineWidth = 3.f;
CGFloat length = 20.f;
CGFloat margin = lineWidth / 2.f - 0.2f;
CGRect rct = self.gridRect;
UIBezierPath *path = [UIBezierPath bezierPath];
if (rectConrner == UIRectCornerTopLeft) {
[path moveToPoint:CGPointMake(rct.origin.x + length, rct.origin.y - lineWidth / 2)];
[path addLineToPoint:CGPointMake(rct.origin.x - margin, rct.origin.y - margin)];
[path addLineToPoint:CGPointMake(rct.origin.x - margin, rct.origin.y + length)];
}else if (rectConrner == UIRectCornerTopRight) {
[path moveToPoint:CGPointMake(CGRectGetMaxX(rct) - length, rct.origin.y - margin)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(rct) + margin, rct.origin.y - margin)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(rct) + margin, rct.origin.y + length)];
}else if (rectConrner == UIRectCornerBottomLeft) {
[path moveToPoint:CGPointMake(rct.origin.x - margin, CGRectGetMaxY(rct) - length)];
[path addLineToPoint:CGPointMake(rct.origin.x - margin, CGRectGetMaxY(rct) + margin)];
[path addLineToPoint:CGPointMake(rct.origin.x + length, CGRectGetMaxY(rct) + margin)];
}else if (rectConrner == UIRectCornerBottomRight) {
[path moveToPoint:CGPointMake(CGRectGetMaxX(rct) - length, CGRectGetMaxY(rct) + margin)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(rct) + margin, CGRectGetMaxY(rct) + margin)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(rct) + margin, CGRectGetMaxY(rct) - length)];
}
return path;
}
- (CAAnimation *)setAllCorner:(UIRectCorner)rectConrner {
CGFloat lineWidth = 2;
UIBezierPath *path = [self setAllCornerPath:rectConrner];
CAShapeLayer *layer;
if (rectConrner == UIRectCornerTopLeft) {
layer = self.topLeftCornerLayer;
}else if (rectConrner == UIRectCornerTopRight) {
layer = self.topRightCornerLayer;
}else if (rectConrner == UIRectCornerBottomLeft) {
layer = self.bottomLeftCornerLayer;
}else if (rectConrner == UIRectCornerBottomRight) {
layer = self.bottomRightCornerLayer;
}
layer.fillColor = self.bgColor.CGColor;
layer.strokeColor = self.gridColor.CGColor;
layer.lineWidth = lineWidth;
CABasicAnimation *animate = [CABasicAnimation animationWithKeyPath:@"path"];
animate.duration = 0.25f;
animate.fromValue = (__bridge id _Nullable)(layer.path);
animate.toValue = (__bridge id _Nullable)(path.CGPath);
animate.removedOnCompletion = NO;
layer.path = path.CGPath;
[layer addAnimation:animate forKey:nil];
return animate;
}
- (UIBezierPath *)setMiddleLinePath {
CGRect rct = self.gridRect;
CGFloat widthMargin = rct.size.width / 3;
CGFloat heightMargin = rct.size.height / 3;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(rct.origin.x, rct.origin.y + heightMargin)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(rct), rct.origin.y + heightMargin)];
[path moveToPoint:CGPointMake(rct.origin.x, rct.origin.y + heightMargin * 2)];
[path addLineToPoint:CGPointMake(CGRectGetMaxX(rct), rct.origin.y + heightMargin * 2)];
[path moveToPoint:CGPointMake(rct.origin.x + widthMargin, rct.origin.y)];
[path addLineToPoint:CGPointMake(rct.origin.x + widthMargin, CGRectGetMaxY(rct))];
[path moveToPoint:CGPointMake(rct.origin.x + widthMargin * 2, rct.origin.y)];
[path addLineToPoint:CGPointMake(rct.origin.x + widthMargin * 2, CGRectGetMaxY(rct))];
return path;
}
- (CAAnimation *)setMiddleLine {
self.middleLineLayer.fillColor = self.bgColor.CGColor;
self.middleLineLayer.strokeColor = self.gridColor.CGColor;
self.middleLineLayer.lineWidth = 0.5f;
UIBezierPath *path = [self setMiddleLinePath];
CABasicAnimation *animate = [CABasicAnimation animationWithKeyPath:@"path"];
animate.duration = 0.25f;
animate.fromValue = (__bridge id _Nullable)(self.middleLineLayer.path);
animate.toValue = (__bridge id _Nullable)(path.CGPath);
animate.removedOnCompletion = NO;
self.middleLineLayer.path = path.CGPath;
[self.middleLineLayer addAnimation:animate forKey:nil];
return animate;
}
- (CGPathRef)drawGrid {
self.fillColor = self.bgColor.CGColor;
self.strokeColor = self.gridColor.CGColor;
self.lineWidth = 1;
// self.borderWidth = 1.5f;
CGRect rct = self.gridRect;
UIBezierPath *path;
if (self.isRound) {
path = [UIBezierPath bezierPathWithRoundedRect:rct cornerRadius:rct.size.width / 2.f];
}else {
path = [UIBezierPath bezierPathWithRect:rct];
}
return path.CGPath;
}
- (void)layoutSublayers {
[super layoutSublayers];
self.topLeftCornerLayer.frame = self.frame;
self.topRightCornerLayer.frame = self.frame;
self.bottomLeftCornerLayer.frame = self.frame;
self.bottomRightCornerLayer.frame = self.frame;
self.middleLineLayer.frame = self.frame;
}
@end

View File

@ -0,0 +1,26 @@
//
// HXPhotoEditGridMaskLayer.h
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditGridMaskLayer : CAShapeLayer
/** 遮罩颜色 */
@property (nonatomic, assign) CGColorRef maskColor;
@property (nonatomic, assign) BOOL isRound;
/** 遮罩范围 */
@property (nonatomic, assign, setter=setMaskRect:) CGRect maskRect;
- (void)setMaskRect:(CGRect)maskRect animated:(BOOL)animated;
/** 取消遮罩 */
- (void)clearMask;
- (void)clearMaskWithAnimated:(BOOL)animated;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,88 @@
//
// HXPhotoEditGridMaskLayer.m
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGridMaskLayer.h"
@implementation HXPhotoEditGridMaskLayer
@synthesize maskColor = _maskColor;
- (instancetype)init {
self = [super init];
if (self) {
[self customInit];
}
return self;
}
- (void)customInit {
self.contentsScale = [[UIScreen mainScreen] scale];
}
- (void)setMaskColor:(CGColorRef)maskColor {
self.fillColor = maskColor;
self.fillRule = kCAFillRuleEvenOdd;
}
- (CGColorRef)maskColor {
return self.fillColor;
}
- (void)setMaskRect:(CGRect)maskRect {
[self setMaskRect:maskRect animated:NO];
}
- (void)setMaskRect:(CGRect)maskRect animated:(BOOL)animated {
_maskRect = maskRect;
CGPathRef path = nil;
if (CGRectEqualToRect(CGRectZero, maskRect)) {
path = [self newDrawClearGrid];
} else {
path = [self newDrawGrid];
}
[self removeAnimationForKey:@"hx_maskLayer_opacityAnimate"];
if (animated) {
CABasicAnimation *animate = [CABasicAnimation animationWithKeyPath:@"opacity"];
animate.duration = 0.25f;
animate.fromValue = @(0.0);
animate.toValue = @(1.0);
self.path = path;
[self addAnimation:animate forKey:@"hx_maskLayer_opacityAnimate"];
} else {
self.path = path;
}
CGPathRelease(path);
}
- (void)clearMask {
[self clearMaskWithAnimated:NO];
}
- (void)clearMaskWithAnimated:(BOOL)animated {
[self setMaskRect:CGRectZero animated:animated];
}
- (CGPathRef)newDrawGrid {
CGRect maskRect = self.maskRect;
CGMutablePathRef mPath = CGPathCreateMutable();
CGPathAddRect(mPath, NULL, self.bounds);
if (self.isRound) {
CGPathAddRoundedRect(mPath, NULL, maskRect, maskRect.size.width / 2.f, maskRect.size.height / 2.f);
}else {
CGPathAddRect(mPath, NULL, maskRect);
}
return mPath;
}
- (CGPathRef)newDrawClearGrid {
CGMutablePathRef mPath = CGPathCreateMutable();
CGPathAddRect(mPath, NULL, self.bounds);
CGPathAddRect(mPath, NULL, self.bounds);
return mPath;
}
@end

View File

@ -0,0 +1,81 @@
//
// HXPhotoEditGridView.h
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoEditGridLayer.h"
#import "HXPhotoEditGridMaskLayer.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, HXPhotoEditGridViewAspectRatioType) {
HXPhotoEditGridViewAspectRatioType_None, // 不设置比例
HXPhotoEditGridViewAspectRatioType_Original, // 原图比例
HXPhotoEditGridViewAspectRatioType_1x1,
HXPhotoEditGridViewAspectRatioType_3x2,
HXPhotoEditGridViewAspectRatioType_4x3,
HXPhotoEditGridViewAspectRatioType_5x3,
HXPhotoEditGridViewAspectRatioType_15x9,
HXPhotoEditGridViewAspectRatioType_16x9,
HXPhotoEditGridViewAspectRatioType_16x10,
HXPhotoEditGridViewAspectRatioType_Custom
};
@protocol HXPhotoEditGridViewDelegate;
@interface HXPhotoEditGridView : UIView
@property (nonatomic, assign) CGRect gridRect;
@property (nonatomic, weak, readonly) HXPhotoEditGridMaskLayer *gridMaskLayer;
- (void)setGridRect:(CGRect)gridRect animated:(BOOL)animated;
- (void)setGridRect:(CGRect)gridRect maskLayer:(BOOL)isMaskLayer animated:(BOOL)animated;
- (void)setGridRect:(CGRect)gridRect maskLayer:(BOOL)isMaskLayer animated:(BOOL)animated completion:(void (^ _Nullable)(BOOL finished))completion;
/** 最小尺寸 CGSizeMake(80, 80); */
@property (nonatomic, assign) CGSize controlMinSize;
/** 最大尺寸 CGRectInset(self.bounds, 20, 20) */
@property (nonatomic, assign) CGRect controlMaxRect;
/** 原图尺寸 */
@property (nonatomic, assign) CGSize controlSize;
/** 显示遮罩层触发拖动条件必须设置为YESdefault is YES */
@property (nonatomic, assign) BOOL showMaskLayer;
/** 是否正在拖动 */
@property(nonatomic,readonly,getter=isDragging) BOOL dragging;
/** 比例是否水平翻转 */
@property (nonatomic, assign) BOOL aspectRatioHorizontally;
/** 设置固定比例 */
@property (nonatomic, assign) HXPhotoEditGridViewAspectRatioType aspectRatio;
/// 自定义固定比例
@property (assign, nonatomic) CGSize customRatioSize;
- (void)setAspectRatio:(HXPhotoEditGridViewAspectRatioType)aspectRatio animated:(BOOL)animated;
- (void)setupAspectRatio:(HXPhotoEditGridViewAspectRatioType)aspectRatio;
@property (nonatomic, weak) id<HXPhotoEditGridViewDelegate> delegate;
/** 遮罩颜色 */
@property (nonatomic, assign) CGColorRef maskColor;
@property (nonatomic, weak, readonly) HXPhotoEditGridLayer *gridLayer;
@property (nonatomic, assign) BOOL isRound;
/** 长宽比例描述 */
- (NSArray <NSString *>*)aspectRatioDescs;
- (void)setAspectRatioWithoutDelegate:(HXPhotoEditGridViewAspectRatioType)aspectRatio;;
- (void)changeSubviewFrame:(CGRect)frame;
@end
@protocol HXPhotoEditGridViewDelegate <NSObject>
- (void)gridViewDidBeginResizing:(HXPhotoEditGridView *)gridView;
- (void)gridViewDidResizing:(HXPhotoEditGridView *)gridView;
- (void)gridViewDidEndResizing:(HXPhotoEditGridView *)gridView;
/** 调整长宽比例 */
- (void)gridViewDidAspectRatio:(HXPhotoEditGridView *)gridView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,533 @@
//
// HXPhotoEditGridView.m
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditGridView.h"
#import "HXPhotoEditResizeControl.h"
/** */
const CGFloat HXControlWidth = 30.f;
@interface HXPhotoEditGridView ()<HXPhotoEditResizeControlDelegate>
@property (nonatomic, weak) HXPhotoEditResizeControl *topLeftCornerView;
@property (nonatomic, weak) HXPhotoEditResizeControl *topRightCornerView;
@property (nonatomic, weak) HXPhotoEditResizeControl *bottomLeftCornerView;
@property (nonatomic, weak) HXPhotoEditResizeControl *bottomRightCornerView;
@property (nonatomic, weak) HXPhotoEditResizeControl *topEdgeView;
@property (nonatomic, weak) HXPhotoEditResizeControl *leftEdgeView;
@property (nonatomic, weak) HXPhotoEditResizeControl *bottomEdgeView;
@property (nonatomic, weak) HXPhotoEditResizeControl *rightEdgeView;
@property (nonatomic, weak) HXPhotoEditGridMaskLayer *gridMaskLayer;
@property (nonatomic, assign) CGRect initialRect;
@property (nonatomic, weak) HXPhotoEditGridLayer *gridLayer;
@property (nonatomic, readonly) CGSize aspectRatioSize;
@end
@implementation HXPhotoEditGridView
@synthesize dragging = _dragging;
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self customInit];
}
return self;
}
- (void)customInit {
/** */
HXPhotoEditGridMaskLayer *gridMaskLayer = [[HXPhotoEditGridMaskLayer alloc] init];
gridMaskLayer.frame = self.bounds;
gridMaskLayer.maskColor = [UIColor blackColor].CGColor;
[self.layer addSublayer:gridMaskLayer];
self.gridMaskLayer = gridMaskLayer;
/** */
HXPhotoEditGridLayer *gridLayer = [[HXPhotoEditGridLayer alloc] init];
gridLayer.frame = self.bounds;
// gridLayer.lineWidth = 2.f;
gridLayer.bgColor = [UIColor clearColor];
gridLayer.gridColor = [UIColor clearColor];
[self.layer addSublayer:gridLayer];
self.gridLayer = gridLayer;
self.gridRect = CGRectInset(self.bounds, 20, 20);
self.controlMinSize = CGSizeMake(80, 80);
self.controlMaxRect = CGRectInset(self.bounds, 20, 20);
self.controlSize = CGSizeZero;
/** */
self.showMaskLayer = YES;
self.topLeftCornerView = [self createResizeControl];
self.topRightCornerView = [self createResizeControl];
self.bottomLeftCornerView = [self createResizeControl];
self.bottomRightCornerView = [self createResizeControl];
self.topEdgeView = [self createResizeControl];
self.leftEdgeView = [self createResizeControl];
self.bottomEdgeView = [self createResizeControl];
self.rightEdgeView = [self createResizeControl];
}
- (void)changeSubviewFrame:(CGRect)frame {
self.gridRect = CGRectInset(CGRectMake(0, 0, frame.size.width, frame.size.height), 20, 20);
self.controlMaxRect = CGRectInset(CGRectMake(0, 0, frame.size.width, frame.size.height), 20, 20);
}
- (void)setMaskColor:(CGColorRef)maskColor {
_maskColor = maskColor;
self.gridMaskLayer.maskColor = maskColor;
}
- (void)setIsRound:(BOOL)isRound {
_isRound = isRound;
self.gridLayer.isRound = isRound;
self.gridMaskLayer.isRound = isRound;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.gridLayer.frame = self.bounds;
self.gridMaskLayer.frame = self.bounds;
CGRect rect = self.gridRect;
self.topLeftCornerView.frame = (CGRect){CGRectGetMinX(rect) - CGRectGetWidth(self.topLeftCornerView.bounds) / 2, CGRectGetMinY(rect) - CGRectGetHeight(self.topLeftCornerView.bounds) / 2, self.topLeftCornerView.bounds.size};
self.topRightCornerView.frame = (CGRect){CGRectGetMaxX(rect) - CGRectGetWidth(self.topRightCornerView.bounds) / 2, CGRectGetMinY(rect) - CGRectGetHeight(self.topRightCornerView.bounds) / 2, self.topRightCornerView.bounds.size};
self.bottomLeftCornerView.frame = (CGRect){CGRectGetMinX(rect) - CGRectGetWidth(self.bottomLeftCornerView.bounds) / 2, CGRectGetMaxY(rect) - CGRectGetHeight(self.bottomLeftCornerView.bounds) / 2, self.bottomLeftCornerView.bounds.size};
self.bottomRightCornerView.frame = (CGRect){CGRectGetMaxX(rect) - CGRectGetWidth(self.bottomRightCornerView.bounds) / 2, CGRectGetMaxY(rect) - CGRectGetHeight(self.bottomRightCornerView.bounds) / 2, self.bottomRightCornerView.bounds.size};
self.topEdgeView.frame = (CGRect){CGRectGetMaxX(self.topLeftCornerView.frame), CGRectGetMinY(rect) - CGRectGetHeight(self.topEdgeView.frame) / 2, CGRectGetMinX(self.topRightCornerView.frame) - CGRectGetMaxX(self.topLeftCornerView.frame), CGRectGetHeight(self.topEdgeView.bounds)};
self.leftEdgeView.frame = (CGRect){CGRectGetMinX(rect) - CGRectGetWidth(self.leftEdgeView.frame) / 2, CGRectGetMaxY(self.topLeftCornerView.frame), CGRectGetWidth(self.leftEdgeView.bounds), CGRectGetMinY(self.bottomLeftCornerView.frame) - CGRectGetMaxY(self.topLeftCornerView.frame)};
self.bottomEdgeView.frame = (CGRect){CGRectGetMaxX(self.bottomLeftCornerView.frame), CGRectGetMinY(self.bottomLeftCornerView.frame), CGRectGetMinX(self.bottomRightCornerView.frame) - CGRectGetMaxX(self.bottomLeftCornerView.frame), CGRectGetHeight(self.bottomEdgeView.bounds)};
self.rightEdgeView.frame = (CGRect){CGRectGetMaxX(rect) - CGRectGetWidth(self.rightEdgeView.bounds) / 2, CGRectGetMaxY(self.topRightCornerView.frame), CGRectGetWidth(self.rightEdgeView.bounds), CGRectGetMinY(self.bottomRightCornerView.frame) - CGRectGetMaxY(self.topRightCornerView.frame)};
}
- (void)setShowMaskLayer:(BOOL)showMaskLayer {
if (_showMaskLayer != showMaskLayer) {
_showMaskLayer = showMaskLayer;
if (showMaskLayer) {
/** */
[self.gridMaskLayer setMaskRect:self.gridRect animated:YES];
} else {
/** */
[self.gridMaskLayer clearMaskWithAnimated:YES];
}
}
// self.userInteractionEnabled = showMaskLayer;
}
- (BOOL)isDragging {
return _dragging;
}
#pragma mark - lf_resizeConrolDelegate
- (void)resizeConrolDidBeginResizing:(HXPhotoEditResizeControl *)resizeConrol {
self.initialRect = self.gridRect;
_dragging = YES;
// self.showMaskLayer = NO;
if ([self.delegate respondsToSelector:@selector(gridViewDidBeginResizing:)]) {
[self.delegate gridViewDidBeginResizing:self];
}
}
- (void)resizeConrolDidResizing:(HXPhotoEditResizeControl *)resizeConrol {
CGRect gridRect = [self cropRectMakeWithResizeControlView:resizeConrol];
[self setGridRect:gridRect maskLayer:NO];
if ([self.delegate respondsToSelector:@selector(gridViewDidResizing:)]) {
[self.delegate gridViewDidResizing:self];
}
}
- (void)resizeConrolDidEndResizing:(HXPhotoEditResizeControl *)resizeConrol {
[self enableCornerViewUserInteraction:nil];
if ([self.delegate respondsToSelector:@selector(gridViewDidEndResizing:)]) {
[self.delegate gridViewDidEndResizing:self];
}
_dragging = NO;
}
- (void)setGridRect:(CGRect)gridRect {
[self setGridRect:gridRect maskLayer:YES];
}
- (void)setGridRect:(CGRect)gridRect maskLayer:(BOOL)isMaskLayer {
[self setGridRect:gridRect maskLayer:isMaskLayer animated:NO];
}
- (void)setGridRect:(CGRect)gridRect animated:(BOOL)animated {
[self setGridRect:gridRect maskLayer:NO animated:animated];
}
- (void)setGridRect:(CGRect)gridRect maskLayer:(BOOL)isMaskLayer animated:(BOOL)animated {
[self setGridRect:gridRect maskLayer:isMaskLayer animated:animated completion:nil];
}
- (void)setGridRect:(CGRect)gridRect maskLayer:(BOOL)isMaskLayer animated:(BOOL)animated completion:(void (^ _Nullable)(BOOL finished))completion {
CGSize size = self.aspectRatioSize;
if (!CGSizeEqualToSize(size, CGSizeZero)) {
/** */
CGFloat newHeight = gridRect.size.width * (size.height/size.width);
/** */
if (newHeight > _controlMaxRect.size.height) {
CGFloat newWidth = gridRect.size.width * (_controlMaxRect.size.height/newHeight);
CGFloat diffWidth = gridRect.size.width - newWidth;
gridRect.size.width = newWidth;
gridRect.origin.x = gridRect.origin.x + diffWidth/2;
newHeight = _controlMaxRect.size.height;
}
CGFloat diffHeight = gridRect.size.height - newHeight;
gridRect.size.height = newHeight;
gridRect.origin.y = gridRect.origin.y + diffHeight/2;
}
_gridRect = gridRect;
[self setNeedsLayout];
[self.gridLayer setGridRect:gridRect animated:animated completion:completion];
if (isMaskLayer && _showMaskLayer) {
[self.gridMaskLayer setMaskRect:gridRect animated:YES];
}
}
- (void)setAspectRatioWithoutDelegate:(HXPhotoEditGridViewAspectRatioType)aspectRatio {
if (_aspectRatio != aspectRatio) {
_aspectRatio = aspectRatio;
}
}
- (void)setupAspectRatio:(HXPhotoEditGridViewAspectRatioType)aspectRatio {
_aspectRatio = aspectRatio;
CGSize size = self.aspectRatioSize;
CGRect gridRect = self.gridRect;
if (!CGSizeEqualToSize(size, CGSizeZero)) {
/** */
CGFloat newHeight = gridRect.size.width * (size.height/size.width);
/** */
if (newHeight > _controlMaxRect.size.height) {
CGFloat newWidth = gridRect.size.width * (_controlMaxRect.size.height/newHeight);
CGFloat diffWidth = gridRect.size.width - newWidth;
gridRect.size.width = newWidth;
gridRect.origin.x = gridRect.origin.x + diffWidth/2;
newHeight = _controlMaxRect.size.height;
}
CGFloat diffHeight = gridRect.size.height - newHeight;
gridRect.size.height = newHeight;
gridRect.origin.y = gridRect.origin.y + diffHeight/2;
}
_gridRect = gridRect;
}
- (void)setAspectRatio:(HXPhotoEditGridViewAspectRatioType)aspectRatio {
[self setAspectRatio:aspectRatio animated:NO];
}
- (void)setAspectRatio:(HXPhotoEditGridViewAspectRatioType)aspectRatio animated:(BOOL)animated {
if (_aspectRatio != aspectRatio) {
_aspectRatio = aspectRatio;
CGSize size = self.aspectRatioSize;
if (!CGSizeEqualToSize(size, CGSizeZero)) {
__weak typeof(self) weakSelf = self;
[self setGridRect:self.gridRect maskLayer:NO animated:animated completion:^(BOOL finished) {
if ([weakSelf.delegate respondsToSelector:@selector(gridViewDidAspectRatio:)]) {
[weakSelf.delegate gridViewDidAspectRatio:weakSelf];
}
}];
}
}
}
- (NSArray <NSString *>*)aspectRatioDescs {
if (_aspectRatioHorizontally) {
static NSArray <NSString *> *aspectRatioHorizontally = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *custom = [NSString stringWithFormat:@"%dx%d", (int)self.customRatioSize.width, (int)self.customRatioSize.height];
aspectRatioHorizontally = @[@"Original", @"1x1", @"3x2", @"4x3", @"5x3", @"15x9", @"16x9", @"16x10", custom];
});
return aspectRatioHorizontally;
} else {
static NSArray <NSString *> *aspectRatioVertical = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *custom = [NSString stringWithFormat:@"%dx%d", (int)self.customRatioSize.height, (int)self.customRatioSize.width];
aspectRatioVertical = @[@"Original", @"1x1", @"2x3", @"3x4", @"3x5", @"9x15", @"9x16", @"10x16", custom];
});
return aspectRatioVertical;
}
}
#pragma mark - private
- (CGSize)aspectRatioSize {
if (self.aspectRatioHorizontally) {
switch (self.aspectRatio) {
case HXPhotoEditGridViewAspectRatioType_None:
return CGSizeZero;
case HXPhotoEditGridViewAspectRatioType_Original:
if (self.controlSize.width == 0 || self.controlSize.height == 0) {
return CGSizeZero;
}
return CGSizeMake(1, self.controlSize.height/self.controlSize.width);
case HXPhotoEditGridViewAspectRatioType_1x1:
return CGSizeMake(1, 1);
case HXPhotoEditGridViewAspectRatioType_3x2:
return CGSizeMake(3, 2);
case HXPhotoEditGridViewAspectRatioType_4x3:
return CGSizeMake(4, 3);
case HXPhotoEditGridViewAspectRatioType_5x3:
return CGSizeMake(5, 3);
case HXPhotoEditGridViewAspectRatioType_15x9:
return CGSizeMake(15, 9);
case HXPhotoEditGridViewAspectRatioType_16x9:
return CGSizeMake(16, 9);
case HXPhotoEditGridViewAspectRatioType_16x10:
return CGSizeMake(16, 10);
case HXPhotoEditGridViewAspectRatioType_Custom:
return self.customRatioSize;
break;
}
} else {
switch (self.aspectRatio) {
case HXPhotoEditGridViewAspectRatioType_None:
return CGSizeZero;
case HXPhotoEditGridViewAspectRatioType_Original:
if (self.controlSize.width == 0 || self.controlSize.height == 0) {
return CGSizeZero;
}
return CGSizeMake(self.controlSize.width/self.controlSize.height, 1);
case HXPhotoEditGridViewAspectRatioType_1x1:
return CGSizeMake(1, 1);
case HXPhotoEditGridViewAspectRatioType_3x2:
return CGSizeMake(2, 3);
case HXPhotoEditGridViewAspectRatioType_4x3:
return CGSizeMake(3, 4);
case HXPhotoEditGridViewAspectRatioType_5x3:
return CGSizeMake(3, 5);
case HXPhotoEditGridViewAspectRatioType_15x9:
return CGSizeMake(9, 15);
case HXPhotoEditGridViewAspectRatioType_16x9:
return CGSizeMake(9, 16);
case HXPhotoEditGridViewAspectRatioType_16x10:
return CGSizeMake(10, 16);
case HXPhotoEditGridViewAspectRatioType_Custom:
return self.customRatioSize;
break;
}
}
return CGSizeZero;
}
- (HXPhotoEditResizeControl *)createResizeControl {
HXPhotoEditResizeControl *control = [[HXPhotoEditResizeControl alloc] initWithFrame:(CGRect){CGPointZero, CGSizeMake(HXControlWidth, HXControlWidth)}];
control.delegate = self;
[self addSubview:control];
return control;
}
- (CGRect)cropRectMakeWithResizeControlView:(HXPhotoEditResizeControl *)resizeControlView {
CGRect rect = self.gridRect;
CGSize aspectRatioSize = self.aspectRatioSize;
CGFloat widthRatio = aspectRatioSize.height == 0 ? 0 : aspectRatioSize.width/aspectRatioSize.height;
CGFloat heightRatio = aspectRatioSize.width == 0 ? 0 : aspectRatioSize.height/aspectRatioSize.width;
if (resizeControlView == self.topEdgeView) {
rect = CGRectMake(CGRectGetMinX(self.initialRect) + resizeControlView.translation.y * widthRatio / 2,
CGRectGetMinY(self.initialRect) + resizeControlView.translation.y,
CGRectGetWidth(self.initialRect) - resizeControlView.translation.y * widthRatio,
CGRectGetHeight(self.initialRect) - resizeControlView.translation.y);
} else if (resizeControlView == self.leftEdgeView) {
rect = CGRectMake(CGRectGetMinX(self.initialRect) + resizeControlView.translation.x,
CGRectGetMinY(self.initialRect) + resizeControlView.translation.x * heightRatio / 2,
CGRectGetWidth(self.initialRect) - resizeControlView.translation.x,
CGRectGetHeight(self.initialRect) - resizeControlView.translation.x * heightRatio);
} else if (resizeControlView == self.bottomEdgeView) {
rect = CGRectMake(CGRectGetMinX(self.initialRect) - resizeControlView.translation.y * widthRatio / 2,
CGRectGetMinY(self.initialRect),
CGRectGetWidth(self.initialRect) + resizeControlView.translation.y * widthRatio,
CGRectGetHeight(self.initialRect) + resizeControlView.translation.y);
} else if (resizeControlView == self.rightEdgeView) {
rect = CGRectMake(CGRectGetMinX(self.initialRect),
CGRectGetMinY(self.initialRect) - resizeControlView.translation.x * heightRatio / 2,
CGRectGetWidth(self.initialRect) + resizeControlView.translation.x,
CGRectGetHeight(self.initialRect) + resizeControlView.translation.x * heightRatio);
} else if (resizeControlView == self.topLeftCornerView) {
/** */
if (heightRatio && widthRatio) {
CGFloat trans = self.aspectRatioHorizontally ? MAX(resizeControlView.translation.x, resizeControlView.translation.y) : MIN(resizeControlView.translation.x, resizeControlView.translation.y);
rect = CGRectMake(CGRectGetMinX(self.initialRect) + trans,
CGRectGetMinY(self.initialRect) + trans * heightRatio,
CGRectGetWidth(self.initialRect) - trans,
CGRectGetHeight(self.initialRect) - trans * heightRatio);
} else {
rect = CGRectMake(CGRectGetMinX(self.initialRect) + resizeControlView.translation.x,
CGRectGetMinY(self.initialRect) + resizeControlView.translation.y,
CGRectGetWidth(self.initialRect) - resizeControlView.translation.x,
CGRectGetHeight(self.initialRect) - resizeControlView.translation.y);
}
} else if (resizeControlView == self.topRightCornerView) {
/** */
if (heightRatio && widthRatio) {
CGFloat trans = self.aspectRatioHorizontally ? MAX(resizeControlView.translation.x * -1, resizeControlView.translation.y) : MIN(resizeControlView.translation.x * -1, resizeControlView.translation.y);
rect = CGRectMake(CGRectGetMinX(self.initialRect),
CGRectGetMinY(self.initialRect) + trans * heightRatio,
CGRectGetWidth(self.initialRect) - trans,
CGRectGetHeight(self.initialRect) - trans * heightRatio);
} else {
rect = CGRectMake(CGRectGetMinX(self.initialRect),
CGRectGetMinY(self.initialRect) + resizeControlView.translation.y,
CGRectGetWidth(self.initialRect) + resizeControlView.translation.x,
CGRectGetHeight(self.initialRect) - resizeControlView.translation.y);
}
} else if (resizeControlView == self.bottomLeftCornerView) {
/** */
if (heightRatio && widthRatio) {
CGFloat trans = self.aspectRatioHorizontally ? MAX(resizeControlView.translation.x, resizeControlView.translation.y * -1) : MIN(resizeControlView.translation.x, resizeControlView.translation.y * -1);
rect = CGRectMake(CGRectGetMinX(self.initialRect) + trans,
CGRectGetMinY(self.initialRect),
CGRectGetWidth(self.initialRect) - trans,
CGRectGetHeight(self.initialRect) - trans * heightRatio);
} else {
rect = CGRectMake(CGRectGetMinX(self.initialRect) + resizeControlView.translation.x,
CGRectGetMinY(self.initialRect),
CGRectGetWidth(self.initialRect) - resizeControlView.translation.x,
CGRectGetHeight(self.initialRect) + resizeControlView.translation.y);
}
} else if (resizeControlView == self.bottomRightCornerView) {
/** */
if (heightRatio && widthRatio) {
CGFloat trans = self.aspectRatioHorizontally ? MAX(resizeControlView.translation.x * -1, resizeControlView.translation.y * -1) : MIN(resizeControlView.translation.x * -1, resizeControlView.translation.y * -1);
rect = CGRectMake(CGRectGetMinX(self.initialRect),
CGRectGetMinY(self.initialRect),
CGRectGetWidth(self.initialRect) - trans,
CGRectGetHeight(self.initialRect) - trans * heightRatio);
} else {
rect = CGRectMake(CGRectGetMinX(self.initialRect),
CGRectGetMinY(self.initialRect),
CGRectGetWidth(self.initialRect) + resizeControlView.translation.x,
CGRectGetHeight(self.initialRect) + resizeControlView.translation.y);
}
}
if (heightRatio && widthRatio) {
/** */
if (ceil(rect.size.width) < ceil(_controlMinSize.width) && ceil(rect.size.height) < ceil(_controlMinSize.height)) {
return self.gridRect;
}
}
/** ps
使CGRectGet
rect = (origin = (x = 50, y = 618), size = (width = 61, height = -488))
CGRectGetMaxY(rect) = 618CGRectGetHeight(rect) = 488
*/
/** x/y */
if (ceil(rect.origin.x) < ceil(CGRectGetMinX(_controlMaxRect))) {
rect.origin.x = _controlMaxRect.origin.x;
rect.size.width = CGRectGetMaxX(self.initialRect)-rect.origin.x;
}
if (ceil(rect.origin.y) < ceil(CGRectGetMinY(_controlMaxRect))) {
rect.origin.y = _controlMaxRect.origin.y;
rect.size.height = CGRectGetMaxY(self.initialRect)-rect.origin.y;
}
/** */
if (ceil(rect.origin.x+rect.size.width) > ceil(CGRectGetMaxX(_controlMaxRect))) {
rect.size.width = CGRectGetMaxX(_controlMaxRect) - CGRectGetMinX(rect);
}
if (ceil(rect.origin.y+rect.size.height) > ceil(CGRectGetMaxY(_controlMaxRect))) {
rect.size.height = CGRectGetMaxY(_controlMaxRect) - CGRectGetMinY(rect);
}
/** */
if (ceil(rect.size.width) <= ceil(_controlMinSize.width)) {
/** x */
if (resizeControlView == self.topLeftCornerView || resizeControlView == self.leftEdgeView || resizeControlView == self.bottomLeftCornerView) {
rect.origin.x = CGRectGetMaxX(self.initialRect) - _controlMinSize.width;
}
rect.size.width = _controlMinSize.width;
if (heightRatio && widthRatio) {
rect.size.height = rect.size.width * heightRatio;
/** x */
if (resizeControlView == self.leftEdgeView || resizeControlView == self.rightEdgeView) {
rect.origin.y = CGRectGetMaxY(self.initialRect) - CGRectGetHeight(self.initialRect)/2 - rect.size.height/2;
}
/** y */
if (resizeControlView == self.topEdgeView || resizeControlView == self.bottomEdgeView) {
rect.origin.x = CGRectGetMaxX(self.initialRect) - CGRectGetWidth(self.initialRect)/2 - _controlMinSize.width/2;
}
/** y */
if (resizeControlView == self.topLeftCornerView || resizeControlView == self.topEdgeView || resizeControlView == self.topRightCornerView) {
rect.origin.y = CGRectGetMaxY(self.initialRect) - rect.size.height;
}
}
}
if (ceil(rect.size.height) <= ceil(_controlMinSize.height)) {
/** y */
if (resizeControlView == self.topLeftCornerView || resizeControlView == self.topEdgeView || resizeControlView == self.topRightCornerView) {
rect.origin.y = CGRectGetMaxY(self.initialRect) - _controlMinSize.height;
}
rect.size.height = _controlMinSize.height;
if (heightRatio && widthRatio) {
rect.size.width = rect.size.height * widthRatio;
/** y */
if (resizeControlView == self.leftEdgeView || resizeControlView == self.rightEdgeView) {
rect.origin.y = CGRectGetMaxY(self.initialRect) - CGRectGetHeight(self.initialRect)/2 - _controlMinSize.height/2;
}
/** x */
if (resizeControlView == self.topEdgeView || resizeControlView == self.bottomEdgeView) {
rect.origin.x = CGRectGetMaxX(self.initialRect) - CGRectGetWidth(self.initialRect)/2 - rect.size.width/2;
}
/** x */
if (resizeControlView == self.topLeftCornerView || resizeControlView == self.leftEdgeView || resizeControlView == self.bottomLeftCornerView) {
rect.origin.x = CGRectGetMaxX(self.initialRect) - rect.size.width;
}
}
}
/** */
if (heightRatio && widthRatio) {
if (rect.size.width == _controlMaxRect.size.width) {
rect.origin.y = self.initialRect.origin.y;
rect.size.height = rect.size.width * heightRatio;
} else if (rect.size.height == _controlMaxRect.size.height) {
rect.origin.x = self.initialRect.origin.x;
rect.size.width = rect.size.height * widthRatio;
}
}
return rect;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *view = [super hitTest:point withEvent:event];
if (self == view) {
return nil;
}
[self enableCornerViewUserInteraction:view];
return view;
}
- (void)enableCornerViewUserInteraction:(UIView *)view {
for (UIView *control in self.subviews) {
if ([control isKindOfClass:[HXPhotoEditResizeControl class]]) {
if (view) {
if (control == view) {
control.userInteractionEnabled = YES;
} else {
control.userInteractionEnabled = NO;
}
} else {
control.userInteractionEnabled = YES;
}
}
}
}
@end

View File

@ -0,0 +1,47 @@
//
// HXPhotoEditImageView.h
// photoEditDemo
//
// Created by Silence on 2020/6/23.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HXPhotoEditDrawView.h"
#import "HXPhotoEditStickerView.h"
#import "HXPhotoEditSplashView.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, HXPhotoEditImageViewType) {
HXPhotoEditImageViewTypeNormal = 0, //!< 正常情况
HXPhotoEditImageViewTypeDraw = 1, //!< 绘图状态
HXPhotoEditImageViewTypeSplash = 2 //!< 绘图状态
};
@class HXPhotoEditConfiguration;
@interface HXPhotoEditImageView : UIView
@property (strong, nonatomic, readonly) UIImageView *imageView;
@property (strong, nonatomic) UIImage *image;
@property (assign, nonatomic) HXPhotoEditImageViewType type;
@property (strong, nonatomic, readonly) HXPhotoEditDrawView *drawView;
@property (strong, nonatomic, readonly) HXPhotoEditStickerView *stickerView;
@property (strong, nonatomic, readonly) HXPhotoEditSplashView *splashView;
@property (assign, nonatomic) BOOL splashViewEnable;
/// 显示界面的缩放率
@property (nonatomic, assign) CGFloat screenScale;
/** 贴图是否需要移到屏幕中心 */
@property (nonatomic, copy) BOOL(^moveCenter)(CGRect rect);
@property (nonatomic, copy, nullable) CGFloat (^ getMinScale)(CGSize size);
@property (nonatomic, copy, nullable) CGFloat (^ getMaxScale)(CGSize size);
@property (strong, nonatomic) HXPhotoEditConfiguration *configuration;
/** 数据 */
@property (nonatomic, strong, nullable) NSDictionary *photoEditData;
- (UIImage * _Nullable)editOtherImagesInRect:(CGRect)rect rotate:(CGFloat)rotate mirrorHorizontally:(BOOL)mirrorHorizontally;
- (void)changeSubviewFrame;
- (void)clearCoverage;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,221 @@
//
// HXPhotoEditImageView.m
// photoEditDemo
//
// Created by Silence on 2020/6/23.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditImageView.h"
#import "UIView+HXExtension.h"
#import "HXPhotoEditStickerItemView.h"
#import "HXPhotoEditStickerItemContentView.h"
#import "UIImage+HXExtension.h"
#import "UIView+HXExtension.h"
#import "HXPhotoDefine.h"
NSString *const kHXImageViewData_draw = @"HXImageViewData_draw";
NSString *const kHXImageViewData_sticker = @"HXImageViewData_sticker";
NSString *const kHXImageViewData_splash = @"HXImageViewData_splash";
NSString *const kHXImageViewData_filter = @"HXImageViewData_filter";
@interface HXPhotoEditImageView ()
@property (strong, nonatomic) UIImageView *imageView;
@property (strong, nonatomic) HXPhotoEditDrawView *drawView;
@property (strong, nonatomic) HXPhotoEditStickerView *stickerView;
@property (strong, nonatomic) HXPhotoEditSplashView *splashView;
@end
@implementation HXPhotoEditImageView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.imageView];
[self addSubview:self.splashView];
[self addSubview:self.drawView];
[self addSubview:self.stickerView];
}
return self;
}
- (void)changeSubviewFrame {
self.imageView.frame = self.bounds;
self.drawView.frame = self.imageView.frame;
self.stickerView.frame = self.bounds;
self.splashView.frame = self.bounds;
}
- (void)clearCoverage {
// [self.drawView clearCoverage];
// [self.stickerView clearCoverage];
// [self.splashView clearCoverage];
}
- (void)setType:(HXPhotoEditImageViewType)type {
self.drawView.userInteractionEnabled = NO;
self.splashView.userInteractionEnabled = NO;
if (type == HXPhotoEditImageViewTypeDraw) {
self.drawView.userInteractionEnabled = YES;
}else if (type == HXPhotoEditImageViewTypeSplash) {
self.splashView.userInteractionEnabled = YES;
}
}
- (void)setImage:(UIImage *)image {
_image = image;
self.imageView.image = image;
}
- (void)setMoveCenter:(BOOL (^)(CGRect))moveCenter {
_moveCenter = moveCenter;
if (moveCenter) {
self.stickerView.moveCenter = moveCenter;
} else {
self.stickerView.moveCenter = nil;
}
}
- (void)setGetMaxScale:(CGFloat (^)(CGSize))getMaxScale {
_getMaxScale = getMaxScale;
if (getMaxScale) {
self.stickerView.getMaxScale = getMaxScale;
}else {
self.stickerView.getMaxScale = nil;
}
}
- (void)setGetMinScale:(CGFloat (^)(CGSize))getMinScale {
_getMinScale = getMinScale;
if (getMinScale) {
self.stickerView.getMinScale = getMinScale;
}else {
self.stickerView.getMinScale = nil;
}
}
- (void)setScreenScale:(CGFloat)screenScale {
_screenScale = screenScale;
self.drawView.screenScale = screenScale;
self.stickerView.screenScale = screenScale;
self.splashView.screenScale = screenScale;
}
- (UIImage * _Nullable)editOtherImagesInRect:(CGRect)rect rotate:(CGFloat)rotate mirrorHorizontally:(BOOL)mirrorHorizontally {
UIImage *image = nil;
NSMutableArray *array = nil;
for (UIView *subView in self.subviews) {
if (subView == self.imageView) {
continue;
} else if ([subView isKindOfClass:[HXPhotoEditDrawView class]]) {
if (((HXPhotoEditDrawView *)subView).count == 0) {
continue;
}
} else if ([subView isKindOfClass:[HXPhotoEditStickerView class]]) {
if (((HXPhotoEditStickerView *)subView).count == 0) {
continue;
}
} else if ([subView isKindOfClass:[HXPhotoEditSplashView class]]) {
if (!((HXPhotoEditSplashView *)subView).canUndo) {
continue;
}
}
if (array == nil) {
array = [NSMutableArray arrayWithCapacity:3];
}
UIImage *subImage = [subView hx_captureImageAtFrame:rect];
subView.layer.contents = (id)nil;
if (subImage) {
[array addObject:subImage];
}
}
if (array.count) {
image = [UIImage hx_mergeimages:array];
if (rotate || mirrorHorizontally) {
NSInteger angle = fabs(rotate * 180 / M_PI - 360);
if (angle == 0 || angle == 360) {
if (mirrorHorizontally) {
image = [image hx_rotationImage:UIImageOrientationUpMirrored];
}
}else if (angle == 90) {
if (!mirrorHorizontally) {
image = [image hx_rotationImage:UIImageOrientationLeft];
}else {
image = [image hx_rotationImage:UIImageOrientationRightMirrored];
}
}else if (angle == 180) {
if (!mirrorHorizontally) {
image = [image hx_rotationImage:UIImageOrientationDown];
}else {
image = [image hx_rotationImage:UIImageOrientationDownMirrored];
}
}else if (angle == 270) {
if (!mirrorHorizontally) {
image = [image hx_rotationImage:UIImageOrientationRight];
}else {
image = [image hx_rotationImage:UIImageOrientationLeftMirrored];
}
}
}
}
return image;
}
- (void)setConfiguration:(HXPhotoEditConfiguration *)configuration {
_configuration = configuration;
self.stickerView.configuration = configuration;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = self.bounds;
self.drawView.frame = self.imageView.frame;
self.stickerView.frame = self.bounds;
self.splashView.frame = self.bounds;
}
- (UIImageView *)imageView {
if (!_imageView) {
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.clipsToBounds = YES;
}
return _imageView;
}
- (HXPhotoEditDrawView *)drawView {
if (!_drawView) {
_drawView = [[HXPhotoEditDrawView alloc] initWithFrame:self.bounds];
_drawView.userInteractionEnabled = NO;
}
return _drawView;
}
- (HXPhotoEditStickerView *)stickerView {
if (!_stickerView) {
_stickerView = [[HXPhotoEditStickerView alloc] initWithFrame:self.bounds];
}
return _stickerView;
}
- (HXPhotoEditSplashView *)splashView {
if (!_splashView) {
_splashView = [[HXPhotoEditSplashView alloc] initWithFrame:self.bounds];
_splashView.userInteractionEnabled = NO;
HXWeakSelf
_splashView.splashColor = ^UIColor *(CGPoint point) {
return [weakSelf.imageView hx_colorOfPoint:point];
};
}
return _splashView;
}
#pragma mark -
- (NSDictionary *)photoEditData {
NSDictionary *drawData = self.drawView.data;
NSDictionary *stickerData = self.stickerView.data;
NSDictionary *splashData = self.splashView.data;
NSMutableDictionary *data = [@{} mutableCopy];
if (drawData) [data setObject:drawData forKey:kHXImageViewData_draw];
if (stickerData) [data setObject:stickerData forKey:kHXImageViewData_sticker];
if (splashData) [data setObject:splashData forKey:kHXImageViewData_splash];
if (data.count) {
return data;
}
return nil;
}
- (void)setPhotoEditData:(NSDictionary *)photoEditData {
self.drawView.data = photoEditData[kHXImageViewData_draw];
self.stickerView.data = photoEditData[kHXImageViewData_sticker];
self.splashView.data = photoEditData[kHXImageViewData_splash];
}
@end

View File

@ -0,0 +1,23 @@
//
// HXPhotoEditMosaicView.h
// photoEditDemo
//
// Created by Silence on 2020/6/22.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditMosaicView : UIView
/// 主题色
/// 默认微信主题色
@property (strong, nonatomic) UIColor *themeColor;
@property (copy, nonatomic) void (^ didBtnBlock)(NSInteger tag);
@property (copy, nonatomic) void (^ undoBlock)(void);
@property (assign, nonatomic) BOOL undo;
+ (instancetype)initView;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,71 @@
//
// HXPhotoEditMosaicView.m
// photoEditDemo
//
// Created by Silence on 2020/6/22.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditMosaicView.h"
#import "UIImage+HXExtension.h"
#import "NSBundle+HXPhotoPicker.h"
@interface HXPhotoEditMosaicView ()
@property (weak, nonatomic) IBOutlet UIButton *normalBtn;
@property (weak, nonatomic) IBOutlet UIButton *colorBtn;
@property (weak, nonatomic) IBOutlet UIButton *undoBtn;
@end
@implementation HXPhotoEditMosaicView
+ (instancetype)initView {
return [[[NSBundle hx_photoPickerBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
- (void)awakeFromNib {
[super awakeFromNib];
self.undoBtn.enabled = NO;
UIImage *normalImage = [[UIImage hx_imageContentsOfFile:@"hx_photo_edit_mosaic_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self.normalBtn setImage:normalImage forState:UIControlStateNormal];
[self.normalBtn setImage:normalImage forState:UIControlStateSelected];
UIImage *colorImage = [[UIImage hx_imageContentsOfFile:@"hx_photo_edit_mosaic_color"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self.colorBtn setImage:colorImage forState:UIControlStateNormal];
[self.colorBtn setImage:colorImage forState:UIControlStateSelected];
[self.undoBtn setImage:[UIImage hx_imageContentsOfFile:@"hx_photo_edit_repeal"] forState:UIControlStateNormal];
}
- (void)setThemeColor:(UIColor *)themeColor {
_themeColor = themeColor;
self.normalBtn.selected = YES;
self.normalBtn.imageView.tintColor = themeColor;
}
- (void)setUndo:(BOOL)undo {
_undo = undo;
self.undoBtn.enabled = undo;
}
- (IBAction)didBtnClick:(UIButton *)button {
if (button.tag == 0) {
self.normalBtn.selected = YES;
self.normalBtn.imageView.tintColor = self.themeColor;
self.colorBtn.selected = NO;
self.colorBtn.imageView.tintColor = nil;
}else {
self.colorBtn.selected = YES;
self.colorBtn.imageView.tintColor = self.themeColor;
self.normalBtn.selected = NO;
self.normalBtn.imageView.tintColor = nil;
}
if (self.didBtnBlock) {
self.didBtnBlock(button.tag);
}
}
- (IBAction)didUndoBtn:(UIButton *)button {
if (self.undoBlock) {
self.undoBlock();
}
}
@end

View File

@ -0,0 +1,28 @@
//
// HXPhotoEditResizeControl.h
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol HXPhotoEditResizeControlDelegate;
@interface HXPhotoEditResizeControl : UIView
@property (weak, nonatomic) id<HXPhotoEditResizeControlDelegate> delegate;
@property (nonatomic, readonly) CGPoint translation;
@property (nonatomic, getter=isEnabled) BOOL enabled;
@end
@protocol HXPhotoEditResizeControlDelegate <NSObject>
- (void)resizeConrolDidBeginResizing:(HXPhotoEditResizeControl *)resizeConrol;
- (void)resizeConrolDidResizing:(HXPhotoEditResizeControl *)resizeConrol;
- (void)resizeConrolDidEndResizing:(HXPhotoEditResizeControl *)resizeConrol;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,61 @@
//
// HXPhotoEditResizeControl.m
// photoEditDemo
//
// Created by Silence on 2020/6/29.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditResizeControl.h"
@interface HXPhotoEditResizeControl ()
@property (nonatomic, readwrite) CGPoint translation;
@property (nonatomic) CGPoint startPoint;
@property (nonatomic, strong) UIPanGestureRecognizer *gestureRecognizer;
@end
@implementation HXPhotoEditResizeControl
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self addGestureRecognizer:gestureRecognizer];
_gestureRecognizer = gestureRecognizer;
}
return self;
}
- (BOOL)isEnabled {
return self.gestureRecognizer.isEnabled;
}
- (void)setEnabled:(BOOL)enabled {
self.gestureRecognizer.enabled = enabled;
}
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
CGPoint translationInView = [gestureRecognizer translationInView:self.superview];
self.startPoint = CGPointMake(roundf(translationInView.x), translationInView.y);
if ([self.delegate respondsToSelector:@selector(resizeConrolDidBeginResizing:)]) {
[self.delegate resizeConrolDidBeginResizing:self];
}
} else if (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [gestureRecognizer translationInView:self.superview];
self.translation = CGPointMake(roundf(self.startPoint.x + translation.x),
roundf(self.startPoint.y + translation.y));
if ([self.delegate respondsToSelector:@selector(resizeConrolDidResizing:)]) {
[self.delegate resizeConrolDidResizing:self];
}
} else if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
if ([self.delegate respondsToSelector:@selector(resizeConrolDidEndResizing:)]) {
[self.delegate resizeConrolDidEndResizing:self];
}
}
}
@end

View File

@ -0,0 +1,31 @@
//
// HXPhotoEditSplashMaskLayer.h
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HXPhotoEditSplashBlur : NSObject
@property (nonatomic, assign) CGRect rect;
@property (nonatomic, strong, nullable) UIColor *color;
@end
@interface HXPhotoEditSplashImageBlur : HXPhotoEditSplashBlur
@property (nonatomic, copy) NSString *imageName;
@end
@interface HXPhotoEditSplashMaskLayer : CALayer
@property (nonatomic, strong) NSMutableArray <HXPhotoEditSplashBlur *>*lineArray;;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,123 @@
//
// HXPhotoEditSplashMaskLayer.m
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import "HXPhotoEditSplashMaskLayer.h"
#import "UIImage+HXExtension.h"
#define HXRadiansToDegrees(x) (180.0 * x / M_PI)
CGFloat angleBetweenPoints(CGPoint startPoint, CGPoint endPoint) {
CGPoint Xpoint = CGPointMake(startPoint.x + 100, startPoint.y);
CGFloat a = endPoint.x - startPoint.x;
CGFloat b = endPoint.y - startPoint.y;
CGFloat c = Xpoint.x - startPoint.x;
CGFloat d = Xpoint.y - startPoint.y;
CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
if (startPoint.y>endPoint.y) {
rads = -rads;
}
return rads;
}
CGFloat angleBetweenLines(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;
CGFloat rads = acos(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
return HXRadiansToDegrees(rads);
}
@implementation HXPhotoEditSplashBlur
@end
@implementation HXPhotoEditSplashImageBlur
@end
@interface HXPhotoEditSplashMaskLayer ()
@end
@implementation HXPhotoEditSplashMaskLayer
- (instancetype)init {
self = [super init];
if (self) {
// self.contentsScale = [[UIScreen mainScreen] scale];
self.backgroundColor = [UIColor clearColor].CGColor;
_lineArray = [@[] mutableCopy];
}
return self;
}
- (void)drawInContext:(CGContextRef)context {
UIGraphicsPushContext( context );
[[UIColor clearColor] setFill];
UIRectFill(self.bounds);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);
for (NSInteger i = 0; i < self.lineArray.count; i++) {
HXPhotoEditSplashBlur *blur = self.lineArray[i];
CGRect rect = blur.rect;
CGContextSetStrokeColorWithColor(context, (blur.color ? blur.color.CGColor : [UIColor clearColor].CGColor));
//
CGContextSetFillColorWithColor(context, (blur.color ? blur.color.CGColor : [UIColor clearColor].CGColor));
if ([blur isMemberOfClass:[HXPhotoEditSplashImageBlur class]]) {
UIImage *image = [UIImage hx_imageNamed:((HXPhotoEditSplashImageBlur *)blur).imageName];
if (image) {
// CGPoint firstPoint = CGPointZero;
// if (i > 0) {
// HXPhotoEditSplashBlur *prevBlur = self.lineArray[i-1];
// firstPoint = prevBlur.rect.origin;
// }
/** */
CGColorSpaceRef colorRef = CGColorSpaceCreateDeviceRGB();
CGContextRef contextRef = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, image.size.width*4, colorRef, kCGImageAlphaPremultipliedFirst);
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextClipToMask(contextRef, imageRect, image.CGImage);
CGContextSetFillColorWithColor(contextRef, (blur.color ? blur.color.CGColor : [UIColor clearColor].CGColor));
CGContextFillRect(contextRef,imageRect);
/** */
CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
CGContextDrawImage(context, rect, imageRef);
CGImageRelease(imageRef);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorRef);
}
} else {
//
CGContextFillRect(context, rect);
}
}
UIGraphicsPopContext();
}
@end

View File

@ -0,0 +1,46 @@
//
// HXPhotoEditSplashView.h
// photoEditDemo
//
// Created by Silence on 2020/7/1.
// Copyright © 2020 Silence. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, HXPhotoEditSplashStateType) {
/** 马赛克 */
HXPhotoEditSplashStateType_Mosaic,
/** 高斯模糊 */
HXPhotoEditSplashStateType_Blurry,
/** 画笔涂抹 */
HXPhotoEditSplashStateType_Paintbrush,
};
@interface HXPhotoEditSplashView : UIView
/// 显示界面的缩放率
@property (nonatomic, assign) CGFloat screenScale;
/** 数据 */
@property (nonatomic, strong, nullable) NSDictionary *data;
@property (nonatomic, copy) void(^splashBegan)(void);
@property (nonatomic, copy) void(^splashEnded)(void);
/** 绘画颜色 */
@property (nonatomic, copy) UIColor *(^splashColor)(CGPoint point);
/** 改变模糊状态 */
@property (nonatomic, assign) HXPhotoEditSplashStateType state;
/** 是否可撤销 */
- (BOOL)canUndo;
//撤销
- (void)undo;
- (void)clearCoverage;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More