提交
This commit is contained in:
18
TUIKit/TUIMultimediaPlugin/Pick/NSBundle+TUIImagePicker.h
Executable file
18
TUIKit/TUIMultimediaPlugin/Pick/NSBundle+TUIImagePicker.h
Executable file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// NSBundle+TUIImagePicker.h
|
||||
// NSBundle+TUIImagePicker
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface NSBundle (TUIImagePicker)
|
||||
|
||||
+ (NSBundle *)tz_imagePickerBundle;
|
||||
|
||||
+ (NSString *)tui_localizedStringForKey:(NSString *)key value:(NSString *)value;
|
||||
+ (NSString *)tui_localizedStringForKey:(NSString *)key;
|
||||
|
||||
@end
|
||||
|
||||
34
TUIKit/TUIMultimediaPlugin/Pick/NSBundle+TUIImagePicker.m
Executable file
34
TUIKit/TUIMultimediaPlugin/Pick/NSBundle+TUIImagePicker.m
Executable file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// NSBundle+TUIImagePicker.m
|
||||
// NSBundle+TUIImagePicker
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import "NSBundle+TUIImagePicker.h"
|
||||
#import "TUIMultimediaNavController.h"
|
||||
|
||||
@implementation NSBundle (TUIImagePicker)
|
||||
|
||||
+ (NSBundle *)tz_imagePickerBundle {
|
||||
#ifdef SWIFT_PACKAGE
|
||||
NSBundle *bundle = SWIFTPM_MODULE_BUNDLE;
|
||||
#else
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[TUIMultimediaNavController class]];
|
||||
#endif
|
||||
NSURL *url = [bundle URLForResource:@"TUIMultimediaPicker" withExtension:@"bundle"];
|
||||
bundle = [NSBundle bundleWithURL:url];
|
||||
return bundle;
|
||||
}
|
||||
|
||||
+ (NSString *)tui_localizedStringForKey:(NSString *)key {
|
||||
return [self tui_localizedStringForKey:key value:@""];
|
||||
}
|
||||
|
||||
+ (NSString *)tui_localizedStringForKey:(NSString *)key value:(NSString *)value {
|
||||
NSBundle *bundle = [TUIImagePickerConfig sharedInstance].languageBundle;
|
||||
NSString *value1 = [bundle localizedStringForKey:key value:value table:nil];
|
||||
return value1;
|
||||
}
|
||||
|
||||
@end
|
||||
26
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaAlbumPicker.h
Normal file
26
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaAlbumPicker.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// TUIMultimediaAlbumPicker.h
|
||||
// TUIMultimediaPlugin
|
||||
//
|
||||
// Created by yiliangwang on 2024/11/5.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TUIMultimediaNavController.h"
|
||||
#import "TUIMultimediaProcessor.h"
|
||||
#import <TUIChat/TUIBaseChatViewController.h>
|
||||
#import <TUIChat/TUIVideoMessageCellData.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import <TUIChat/AlbumPicker.h>
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaAlbumPicker : NSObject <IAlbumPicker>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
281
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaAlbumPicker.m
Normal file
281
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaAlbumPicker.m
Normal file
@@ -0,0 +1,281 @@
|
||||
//
|
||||
// TUIMultimediaAlbumPicker.m
|
||||
// TUIMultimediaPlugin
|
||||
//
|
||||
// Created by yiliangwang on 2024/11/5.
|
||||
//
|
||||
|
||||
#import "TUIMultimediaAlbumPicker.h"
|
||||
|
||||
@implementation TUIMultimediaAlbumPicker
|
||||
- (void)pickMediaWithCaller:(UIViewController *)caller originalMediaPicked:(IAlbumPickerCallback)mediaPicked progressCallback:(IAlbumPickerCallback)progressCallback finishedCallback:(IAlbumPickerCallback)finishedCallback
|
||||
{
|
||||
UIViewController * pushVC = caller;
|
||||
TUIMultimediaNavController *imagePickerVc = [self createImagePickerVCoriginalMediaPicked:mediaPicked progressCallback:progressCallback finishedCallback:finishedCallback];
|
||||
if (pushVC && imagePickerVc) {
|
||||
[pushVC presentViewController:imagePickerVc animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (TUIMultimediaNavController *)createImagePickerVCoriginalMediaPicked:(IAlbumPickerCallback)originalMediaPicked
|
||||
progressCallback:(IAlbumPickerCallback)progressCallback
|
||||
finishedCallback:(IAlbumPickerCallback)finishedCallback {
|
||||
|
||||
TUIMultimediaNavController *imagePickerVc = [[TUIMultimediaNavController alloc] initWithMaxImagesCount:9 delegate:(id)self];
|
||||
imagePickerVc.modalPresentationStyle = 0;
|
||||
|
||||
@weakify(self)
|
||||
[imagePickerVc setDidFinishPickingHandle:^(NSArray<TUIAssetPickModel *> *models, BOOL isSelectOriginalPhoto) {
|
||||
@strongify(self)
|
||||
|
||||
__block BOOL hasFileSizeExceed = NO;
|
||||
__block BOOL hasImageSizeExceed = NO;
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
for (TUIAssetPickModel *model in models) {
|
||||
dispatch_group_enter(group);
|
||||
if ([[TUIImageManager defaultManager] isVideo:model.asset]) {
|
||||
[self handleVideoAsset:model
|
||||
originalMediaPicked:^(NSDictionary *param) {
|
||||
if (originalMediaPicked) {
|
||||
originalMediaPicked(param);
|
||||
}
|
||||
}
|
||||
progressCallback:^(NSDictionary *param) {
|
||||
if (progressCallback) {
|
||||
progressCallback(param);
|
||||
}
|
||||
}
|
||||
finishedCallback:^(NSDictionary *param) {
|
||||
if (finishedCallback) {
|
||||
finishedCallback(param);
|
||||
}
|
||||
}
|
||||
completion:^(BOOL limitedFlag) {
|
||||
if (limitedFlag) {
|
||||
hasFileSizeExceed = YES;
|
||||
}
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
} else {
|
||||
[self handleImageAsset:model
|
||||
isSelectOriginalPhoto:isSelectOriginalPhoto
|
||||
originalMediaPicked:^(NSDictionary *param) {
|
||||
if (originalMediaPicked) {
|
||||
originalMediaPicked(param);
|
||||
}
|
||||
} progressCallback:^(NSDictionary *param) {
|
||||
if (progressCallback) {
|
||||
progressCallback(param);
|
||||
}
|
||||
} finishedCallback:^(NSDictionary *param) {
|
||||
if (finishedCallback) {
|
||||
finishedCallback(param);
|
||||
}
|
||||
} completion:^(BOOL limitedFlag) {
|
||||
if (limitedFlag) {
|
||||
hasImageSizeExceed = YES;
|
||||
}
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||
if (hasFileSizeExceed || hasImageSizeExceed) {
|
||||
[self showFileCheckToast:YES];
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
return imagePickerVc;
|
||||
}
|
||||
- (void)handleVideoAsset:(TUIAssetPickModel *)model
|
||||
originalMediaPicked:(IAlbumPickerCallback)mediaPicked
|
||||
progressCallback:(IAlbumPickerCallback)progressCallback
|
||||
finishedCallback:(IAlbumPickerCallback)finishedCallback
|
||||
completion:(void (^)(BOOL limitedFlag))completion {
|
||||
|
||||
if (model.editurl) {
|
||||
//Edited; no transcoding needed, can be sent directly.
|
||||
UIImage * snapaImage = [[TUIImageManager defaultManager] getImageWithVideoURL:model.editurl];
|
||||
NSString *snapshotPath = [[TUIImageManager defaultManager] genImagePath:snapaImage isVideoSnapshot:YES];
|
||||
NSInteger duration = [[TUIImageManager defaultManager] getDurationWithVideoURL:model.editurl];
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createVideoMessage:model.editurl.path type:model.editurl.path.pathExtension duration:(int)duration snapshotPath:snapshotPath];
|
||||
NSDictionary *param = @{@"message": message,@"type":@"video"};
|
||||
if (finishedCallback) {
|
||||
finishedCallback(param);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
[[TUIImageManager defaultManager] getAssetBytes:model.asset completion:^(NSInteger assetBytes) {
|
||||
// For videos larger than 200MB, directly report a file size exceeded warning and do not compress.
|
||||
if (assetBytes > 200 * 1024 * 1024) {
|
||||
completion(YES);
|
||||
return;
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Show video snapshot on screen first
|
||||
NSString *snapshotPath = [[TUIImageManager defaultManager] genImagePath:model.image isVideoSnapshot:YES];
|
||||
TUIMessageCellData *placeHolderCellData = [TUIVideoMessageCellData placeholderCellDataWithSnapshotUrl:snapshotPath thubImage:model.image];
|
||||
NSString *random = [NSString stringWithFormat:@"%u", arc4random()];
|
||||
placeHolderCellData.msgID = [NSString stringWithFormat:@"%@%@", snapshotPath, random];
|
||||
NSDictionary *param = @{@"placeHolderCellData" : placeHolderCellData,@"type":@"video"};
|
||||
if (mediaPicked) {
|
||||
mediaPicked(param);
|
||||
}
|
||||
__weak typeof(self)weakSelf = self;
|
||||
//Video transcoding and compression; replace the placeholder image after completion.
|
||||
[self getVideoOutputPathWithAsset:model.asset placeHolderCellData:placeHolderCellData
|
||||
progress:^(NSDictionary *param) {
|
||||
if (progressCallback) {
|
||||
progressCallback(param);
|
||||
}
|
||||
}
|
||||
complete:^(NSString *videoPath) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createVideoMessage:videoPath type:videoPath.pathExtension duration:model.asset.duration snapshotPath:snapshotPath];
|
||||
NSDictionary *param = @{@"message": message,
|
||||
@"placeHolderCellData" : placeHolderCellData,
|
||||
@"type":@"video"};
|
||||
if (finishedCallback) {
|
||||
finishedCallback(param);
|
||||
}
|
||||
});
|
||||
} failure:^(NSString *errorMessage, NSError *error) {
|
||||
// Handle error if needed
|
||||
}];
|
||||
});
|
||||
}
|
||||
completion(NO);
|
||||
}];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)handleImageAsset:(TUIAssetPickModel *)model
|
||||
isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto
|
||||
originalMediaPicked:(IAlbumPickerCallback)mediaPicked
|
||||
progressCallback:(IAlbumPickerCallback)progressCallback
|
||||
finishedCallback:(IAlbumPickerCallback)finishedCallback
|
||||
completion:(void (^)(BOOL limitedFlag))completion {
|
||||
if (model.editImage != nil) {
|
||||
[self sendImageMessage:model.editImage finishedCallback:finishedCallback];
|
||||
return;
|
||||
}
|
||||
|
||||
[[TUIImageManager defaultManager] getAssetBytes:model.asset completion:^(NSInteger assetBytes) {
|
||||
if (assetBytes > 28 * 1024 * 1024) {
|
||||
completion(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mediaPicked) {
|
||||
mediaPicked(nil);
|
||||
}
|
||||
|
||||
if (isSelectOriginalPhoto) {
|
||||
[[TUIImageManager defaultManager] getOriginalPhotoDataWithAsset:model.asset
|
||||
progressHandler:nil
|
||||
completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) {
|
||||
if (!isDegraded) {
|
||||
[self sendImageMessage:[UIImage imageWithData:data] finishedCallback:finishedCallback];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self sendImageMessage:model.image finishedCallback:finishedCallback];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendImageMessage:(UIImage*) image finishedCallback:(IAlbumPickerCallback)finishedCallback{
|
||||
NSString *imagePath = [[TUIImageManager defaultManager] genImagePath:image isVideoSnapshot:NO];
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createImageMessage:imagePath];
|
||||
NSDictionary *param = @{@"message": message,@"type":@"image"};
|
||||
if (finishedCallback != nil) {
|
||||
finishedCallback(param);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)getVideoOutputPathWithAsset:(PHAsset *)asset placeHolderCellData:(TUIMessageCellData *)placeHolderCellData
|
||||
progress:(IAlbumPickerCallback)progressHandler
|
||||
complete:(void(^)(NSString *))completeBlock failure:(void (^)(NSString *errorMessage, NSError *error))failure {
|
||||
[[TUIImageManager defaultManager] requestVideoURLWithAsset:asset success:^(NSURL *videoURL) {
|
||||
NSNumber *videoBytes;
|
||||
[videoURL getResourceValue:&videoBytes forKey:NSURLFileSizeKey error:nil];
|
||||
// The maximum video size sent by IMSDK is 100M
|
||||
NSString *presetName = (videoBytes.intValue > 100 * 1024 * 1024 ? AVAssetExportPreset1280x720 : AVAssetExportPresetPassthrough);
|
||||
if (videoBytes.intValue > 200 * 1024 * 1024) {
|
||||
NSLog(@"requestVideoURLWithAsset falied, The maximum video size sent by IMSDK is 100M");
|
||||
if (failure) {
|
||||
failure(@"The current video size is greater than 200M",nil);
|
||||
}
|
||||
return;
|
||||
}
|
||||
BOOL suportMultimedia = YES;
|
||||
if (suportMultimedia) {
|
||||
[[TUIMultimediaProcessor shareInstance] transcodeVideo:videoURL complete:^(TranscodeResult *result) {
|
||||
NSLog(@"TUIMultimediaMediaProcessor complete %@ ",result.transcodeUri.path);
|
||||
NSData *videoData = [NSData dataWithContentsOfURL:result.transcodeUri];
|
||||
NSString *videoPath = [NSString stringWithFormat:@"%@%@_%u.mp4", TUIKit_Video_Path, [TUITool genVideoName:nil],arc4random()];
|
||||
[[NSFileManager defaultManager] createFileAtPath:videoPath contents:videoData attributes:nil];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (completeBlock) {
|
||||
completeBlock(videoPath);
|
||||
}
|
||||
});
|
||||
} progress:^(float progress) {
|
||||
NSLog(@"TUIMultimediaMediaProcessor videoTranscodingProgress %f ",progress);
|
||||
placeHolderCellData.videoTranscodingProgress = progress;
|
||||
if (progressHandler) {
|
||||
NSNumber *numberValue = @(progress);
|
||||
NSDictionary *param = @{
|
||||
@"progress": numberValue
|
||||
};
|
||||
progressHandler(param);
|
||||
}
|
||||
} ];
|
||||
}
|
||||
else {
|
||||
[[TUIImageManager defaultManager] getVideoOutputPathWithAsset:asset presetName:presetName progress:^(CGFloat progress) {
|
||||
placeHolderCellData.videoTranscodingProgress = progress;
|
||||
} success:^(NSString *outputPath) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (completeBlock) {
|
||||
completeBlock(outputPath);
|
||||
}
|
||||
});
|
||||
} failure:^(NSString *errorMessage, NSError *error) {
|
||||
NSLog(@"getVideoOutputPathWithAsset falied, errorMessage:%@", errorMessage);
|
||||
if (failure) {
|
||||
failure(errorMessage,error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
} failure:^(NSDictionary *info) {
|
||||
NSLog(@"requestVideoURLWithAsset falied, errorInfo:%@", info);
|
||||
if (failure) {
|
||||
failure(info.description,nil);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showFileCheckToast:(BOOL)isFile {
|
||||
UIAlertController *ac = [UIAlertController alertControllerWithTitle:isFile?
|
||||
TIMCommonLocalizableString(TUIKitFileSizeCheckLimited):
|
||||
TIMCommonLocalizableString(TUIKitImageSizeCheckLimited)
|
||||
message:nil preferredStyle:UIAlertControllerStyleAlert];
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm) style:UIAlertActionStyleDefault handler:nil]];
|
||||
UIViewController *topViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
while (topViewController.presentedViewController) {
|
||||
topViewController = topViewController.presentedViewController;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[topViewController presentViewController:ac animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
107
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaNavController.h
Normal file
107
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaNavController.h
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// TUIMultimediaNavController.h
|
||||
// TUIMultimediaNavController
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
#import "NSBundle+TUIImagePicker.h"
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
#import "TUIPhotoPreviewController.h"
|
||||
#import "TUIPhotoPreviewCell.h"
|
||||
|
||||
@class TUIAlbumCell, TUIAssetCell;
|
||||
@protocol TUIMultimediaNavControllerDelegate;
|
||||
@interface TUIMultimediaNavController : UINavigationController
|
||||
|
||||
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TUIMultimediaNavControllerDelegate>)delegate;
|
||||
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TUIMultimediaNavControllerDelegate>)delegate;
|
||||
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TUIMultimediaNavControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc;
|
||||
|
||||
#pragma mark -
|
||||
/// Maximum number of photos allowed to be selected (Default is 9)
|
||||
@property (nonatomic, assign) NSInteger maxImagesCount;
|
||||
/// The number of photos displayed in each row (Default is 4)
|
||||
@property (nonatomic, assign) NSInteger columnNumber;
|
||||
/// Default is 600px
|
||||
@property (nonatomic, assign) CGFloat photoPreviewMaxWidth;
|
||||
|
||||
/// The photos user have selected
|
||||
@property (nonatomic, strong) NSMutableArray *selectedAssets;
|
||||
@property (nonatomic, strong) NSMutableArray<TUIAssetModel *> *selectedModels;
|
||||
@property (nonatomic, strong) NSMutableArray *selectedAssetIds;
|
||||
- (void)addSelectedModel:(TUIAssetModel *)model;
|
||||
- (void)removeSelectedModel:(TUIAssetModel *)model;
|
||||
|
||||
- (UIAlertController *)showAlertWithTitle:(NSString *)title;
|
||||
- (void)showProgressHUD;
|
||||
- (void)hideProgressHUD;
|
||||
@property (nonatomic, assign) BOOL isSelectOriginalPhoto;
|
||||
@property (assign, nonatomic) BOOL needShowStatusBar;
|
||||
@property (nonatomic, copy) NSString *takePictureImageName;
|
||||
@property (nonatomic, copy) NSString *photoSelImageName;
|
||||
@property (nonatomic, copy) NSString *photoDefImageName;
|
||||
@property (nonatomic, copy) NSString *photoOriginSelImageName;
|
||||
@property (nonatomic, copy) NSString *photoOriginDefImageName;
|
||||
@property (nonatomic, copy) NSString *photoPreviewOriginDefImageName;
|
||||
@property (nonatomic, copy) NSString *photoNumberIconImageName;
|
||||
@property (nonatomic, strong) UIImage *takePictureImage;
|
||||
@property (nonatomic, strong) UIImage *addMorePhotoImage;
|
||||
@property (nonatomic, strong) UIImage *photoSelImage;
|
||||
@property (nonatomic, strong) UIImage *photoDefImage;
|
||||
@property (nonatomic, strong) UIImage *photoOriginSelImage;
|
||||
@property (nonatomic, strong) UIImage *photoOriginDefImage;
|
||||
@property (nonatomic, strong) UIImage *photoPreviewOriginDefImage;
|
||||
@property (nonatomic, strong) UIImage *photoNumberIconImage;
|
||||
|
||||
@property (nonatomic, strong) UIColor *oKButtonTitleColorNormal;
|
||||
@property (nonatomic, strong) UIColor *oKButtonTitleColorDisabled;
|
||||
@property (nonatomic, strong) UIColor *naviBgColor;
|
||||
@property (nonatomic, strong) UIColor *naviTitleColor;
|
||||
@property (nonatomic, strong) UIFont *naviTitleFont;
|
||||
@property (nonatomic, strong) UIColor *barItemTextColor;
|
||||
@property (nonatomic, strong) UIFont *barItemTextFont;
|
||||
|
||||
@property (nonatomic, copy) NSString *doneBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *cancelBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *previewBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *editImageBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *fullImageBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *settingBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *processHintStr;
|
||||
@property (nonatomic, copy) NSString *editBtnTitleStr;
|
||||
@property (nonatomic, copy) NSString *editViewCancelBtnTitleStr;
|
||||
@property (strong, nonatomic) UIColor *iconThemeColor;
|
||||
|
||||
@property (nonatomic, copy) void (^didFinishPickingHandle)(NSArray<TUIAssetPickModel *> *models, BOOL isSelectOriginalPhoto);
|
||||
@property (nonatomic, copy) void (^didFinishPickingPhotosWithInfosHandle)(NSArray<TUIAssetPickModel *> *models,BOOL isSelectOriginalPhoto,NSArray<NSDictionary *> *infos);
|
||||
|
||||
- (void)onCancelButtonClick;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface UIImage (MyBundle)
|
||||
+ (UIImage *)tui_imageNamedFromMyBundle:(NSString *)name;
|
||||
@end
|
||||
|
||||
@interface TUICommonTools : NSObject
|
||||
+ (UIEdgeInsets)tui_safeAreaInsets;
|
||||
+ (BOOL)tui_isIPhoneX;
|
||||
+ (BOOL)tui_isLandscape;
|
||||
+ (CGFloat)tui_statusBarHeight;
|
||||
+ (NSDictionary *)tui_getInfoDictionary;
|
||||
+ (NSString *)tui_getAppName;
|
||||
+ (BOOL)tui_isRightToLeftLayout;
|
||||
+ (void)configBarButtonItem:(UIBarButtonItem *)item multiMediaNavVC:(TUIMultimediaNavController *)multiMediaNavVC;
|
||||
+ (BOOL)isICloudSyncError:(NSError *)error;
|
||||
@end
|
||||
|
||||
|
||||
@interface TUIImagePickerConfig : NSObject
|
||||
+ (instancetype)sharedInstance;
|
||||
@property (copy, nonatomic) NSString *preferredLanguage;
|
||||
@property (strong, nonatomic) NSBundle *languageBundle;
|
||||
@end
|
||||
698
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaNavController.m
Normal file
698
TUIKit/TUIMultimediaPlugin/Pick/TUIMultimediaNavController.m
Normal file
@@ -0,0 +1,698 @@
|
||||
//
|
||||
// TUIMultimediaNavController.m
|
||||
// TUIMultimediaNavController
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIMultimediaNavController.h"
|
||||
#import "TUIPhotoPickerController.h"
|
||||
#import "TUIPhotoPreviewController.h"
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
#import <TUIMultimediaCore/TUIAssetCell.h>
|
||||
#import <TUICore/UIView+TUILayout.h>
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@interface TUIMultimediaNavController () {
|
||||
NSTimer *_timer;
|
||||
UILabel *_tipLabel;
|
||||
UIButton *_settingBtn;
|
||||
BOOL _pushPhotoPickerVc;
|
||||
BOOL _didPushPhotoPickerVc;
|
||||
CGRect _cropRect;
|
||||
|
||||
UIButton *_progressHUD;
|
||||
UIView *_HUDContainer;
|
||||
UIActivityIndicatorView *_HUDIndicatorView;
|
||||
UILabel *_HUDLabel;
|
||||
|
||||
UIStatusBarStyle _originStatusBarStyle;
|
||||
}
|
||||
@property (nonatomic, assign) NSInteger timeout;
|
||||
@property (nonatomic, assign) NSInteger HUDTimeoutCount;
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaNavController
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self = [self initWithMaxImagesCount:9 delegate:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
self.view.backgroundColor = UIColor.tertiarySystemBackgroundColor;
|
||||
} else {
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
self.navigationBar.barStyle = UIBarStyleBlack;
|
||||
self.navigationBar.translucent = YES;
|
||||
[TUIImageManager defaultManager].shouldFixOrientation = NO;
|
||||
|
||||
self.oKButtonTitleColorNormal = TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF");
|
||||
self.oKButtonTitleColorDisabled = [TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF") colorWithAlphaComponent:0.5];
|
||||
|
||||
self.navigationBar.barTintColor = [UIColor colorWithRed:(34/255.0) green:(34/255.0) blue:(34/255.0) alpha:1.0];
|
||||
self.navigationBar.tintColor = [UIColor whiteColor];
|
||||
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||
if (self.needShowStatusBar) [UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
|
||||
- (void)setNaviBgColor:(UIColor *)naviBgColor {
|
||||
_naviBgColor = naviBgColor;
|
||||
self.navigationBar.barTintColor = naviBgColor;
|
||||
[self configNavigationBarAppearance];
|
||||
}
|
||||
|
||||
- (void)setNaviTitleColor:(UIColor *)naviTitleColor {
|
||||
_naviTitleColor = naviTitleColor;
|
||||
[self configNaviTitleAppearance];
|
||||
}
|
||||
|
||||
- (void)setNaviTitleFont:(UIFont *)naviTitleFont {
|
||||
_naviTitleFont = naviTitleFont;
|
||||
[self configNaviTitleAppearance];
|
||||
}
|
||||
|
||||
- (void)configNaviTitleAppearance {
|
||||
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
|
||||
if (self.naviTitleColor) {
|
||||
textAttrs[NSForegroundColorAttributeName] = self.naviTitleColor;
|
||||
}
|
||||
if (self.naviTitleFont) {
|
||||
textAttrs[NSFontAttributeName] = self.naviTitleFont;
|
||||
}
|
||||
self.navigationBar.titleTextAttributes = textAttrs;
|
||||
[self configNavigationBarAppearance];
|
||||
}
|
||||
|
||||
- (void)configNavigationBarAppearance {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
UINavigationBarAppearance *barAppearance = [[UINavigationBarAppearance alloc] init];
|
||||
if (self.navigationBar.isTranslucent) {
|
||||
UIColor *barTintColor = self.navigationBar.barTintColor;
|
||||
barAppearance.backgroundColor = [barTintColor colorWithAlphaComponent:0.85];
|
||||
} else {
|
||||
barAppearance.backgroundColor = self.navigationBar.barTintColor;
|
||||
}
|
||||
barAppearance.titleTextAttributes = self.navigationBar.titleTextAttributes;
|
||||
self.navigationBar.standardAppearance = barAppearance;
|
||||
self.navigationBar.scrollEdgeAppearance = barAppearance;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBarItemTextFont:(UIFont *)barItemTextFont {
|
||||
_barItemTextFont = barItemTextFont;
|
||||
[self configBarButtonItemAppearance];
|
||||
}
|
||||
|
||||
- (void)setBarItemTextColor:(UIColor *)barItemTextColor {
|
||||
_barItemTextColor = barItemTextColor;
|
||||
[self configBarButtonItemAppearance];
|
||||
}
|
||||
|
||||
- (void)configBarButtonItemAppearance {
|
||||
UIBarButtonItem *barItem;
|
||||
if (@available(iOS 9, *)) {
|
||||
barItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TUIMultimediaNavController class]]];
|
||||
} else {
|
||||
barItem = [UIBarButtonItem appearanceWhenContainedIn:[TUIMultimediaNavController class], nil];
|
||||
}
|
||||
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
|
||||
textAttrs[NSForegroundColorAttributeName] = self.barItemTextColor;
|
||||
textAttrs[NSFontAttributeName] = self.barItemTextFont;
|
||||
[barItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
|
||||
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
|
||||
[self configNavigationBarAppearance];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
|
||||
[self hideProgressHUD];
|
||||
}
|
||||
|
||||
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TUIMultimediaNavControllerDelegate>)delegate {
|
||||
return [self initWithMaxImagesCount:maxImagesCount columnNumber:4 delegate:delegate pushPhotoPickerVc:YES];
|
||||
}
|
||||
|
||||
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TUIMultimediaNavControllerDelegate>)delegate {
|
||||
return [self initWithMaxImagesCount:maxImagesCount columnNumber:columnNumber delegate:delegate pushPhotoPickerVc:YES];
|
||||
}
|
||||
|
||||
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TUIMultimediaNavControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc {
|
||||
_pushPhotoPickerVc = pushPhotoPickerVc;
|
||||
// TUIAlbumPickerController *albumPickerVc = [[TUIAlbumPickerController alloc] init];
|
||||
// albumPickerVc.isFirstAppear = YES;
|
||||
// albumPickerVc.columnNumber = columnNumber;
|
||||
|
||||
self = [super initWithRootViewController:[UIViewController new]];
|
||||
if (self) {
|
||||
self.maxImagesCount = maxImagesCount > 0 ? maxImagesCount : 9; // Default is 9 / 默认最大可选9张图片
|
||||
self.selectedAssets = [NSMutableArray array];
|
||||
|
||||
self.columnNumber = columnNumber;
|
||||
[self configDefaultSetting];
|
||||
|
||||
if (![[TUIImageManager defaultManager] authorizationStatusAuthorized]) {
|
||||
_tipLabel = [[UILabel alloc] init];
|
||||
_tipLabel.frame = CGRectMake(8, 120, self.view.mm_w - 16, 60);
|
||||
_tipLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_tipLabel.numberOfLines = 0;
|
||||
_tipLabel.font = [UIFont systemFontOfSize:16];
|
||||
_tipLabel.textColor = [UIColor blackColor];
|
||||
_tipLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
|
||||
NSString *appName = [TUICommonTools tui_getAppName];
|
||||
NSString *tipText = [NSString stringWithFormat:[NSBundle tui_localizedStringForKey:@"Allow %@ to access your album in \"Settings -> Privacy -> Photos\""],appName];
|
||||
_tipLabel.text = tipText;
|
||||
[self.view addSubview:_tipLabel];
|
||||
|
||||
_settingBtn = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[_settingBtn setTitle:self.settingBtnTitleStr forState:UIControlStateNormal];
|
||||
_settingBtn.frame = CGRectMake(0, 180, self.view.mm_w, 44);
|
||||
_settingBtn.titleLabel.font = [UIFont systemFontOfSize:18];
|
||||
[_settingBtn addTarget:self action:@selector(settingBtnClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
_settingBtn.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
|
||||
[self.view addSubview:_settingBtn];
|
||||
|
||||
if ([PHPhotoLibrary authorizationStatus] == 0) {
|
||||
_timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(observeAuthrizationStatusChange) userInfo:nil repeats:NO];
|
||||
}
|
||||
} else {
|
||||
[self pushPhotoPickerVc];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configDefaultSetting {
|
||||
self.timeout = 30;
|
||||
self.photoPreviewMaxWidth = 600;
|
||||
self.naviTitleColor = [UIColor whiteColor];
|
||||
self.naviTitleFont = [UIFont systemFontOfSize:17];
|
||||
self.barItemTextFont = [UIFont systemFontOfSize:15];
|
||||
self.barItemTextColor = [UIColor whiteColor];
|
||||
|
||||
self.iconThemeColor = TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF");
|
||||
[self configDefaultBtnTitle];
|
||||
}
|
||||
|
||||
- (void)configDefaultImageName {
|
||||
self.takePictureImageName = @"takePicture80";
|
||||
self.photoDefImage = [self createHollowCircleWithColor:UIColor.whiteColor size:CGSizeMake(24, 24) lineWidth:0.6];
|
||||
UIImage *baseImage = [self createImageWithColor:nil size:CGSizeMake(24, 24) radius:12];
|
||||
UIImage *overlayImage = [UIImage tui_imageNamedFromMyBundle:@"selected_tick_icon"];;
|
||||
UIImage *combinedImage = [self overlayImage:baseImage withImage:overlayImage];
|
||||
self.photoSelImage = combinedImage;
|
||||
|
||||
self.photoNumberIconImage = [self createImageWithColor:nil size:CGSizeMake(24, 24) radius:12]; // @"photo_number_icon";
|
||||
self.photoPreviewOriginDefImageName = @"preview_original_def";
|
||||
self.photoOriginDefImageName = @"photo_original_def";
|
||||
self.photoOriginSelImageName = @"photo_original_sel";
|
||||
|
||||
self.photoOriginSelImage = [self resizeImage:combinedImage toSize:CGSizeMake(20, 20)];
|
||||
self.addMorePhotoImage = [UIImage tui_imageNamedFromMyBundle:@"addMore"];
|
||||
}
|
||||
|
||||
- (void)setTakePictureImageName:(NSString *)takePictureImageName {
|
||||
_takePictureImageName = takePictureImageName;
|
||||
_takePictureImage = [UIImage tui_imageNamedFromMyBundle:takePictureImageName];
|
||||
}
|
||||
|
||||
- (void)setPhotoSelImageName:(NSString *)photoSelImageName {
|
||||
_photoSelImageName = photoSelImageName;
|
||||
_photoSelImage = [UIImage tui_imageNamedFromMyBundle:photoSelImageName];
|
||||
}
|
||||
|
||||
- (void)setPhotoDefImageName:(NSString *)photoDefImageName {
|
||||
_photoDefImageName = photoDefImageName;
|
||||
_photoDefImage = [UIImage tui_imageNamedFromMyBundle:photoDefImageName];
|
||||
}
|
||||
|
||||
- (void)setPhotoNumberIconImageName:(NSString *)photoNumberIconImageName {
|
||||
_photoNumberIconImageName = photoNumberIconImageName;
|
||||
_photoNumberIconImage = [UIImage tui_imageNamedFromMyBundle:photoNumberIconImageName];
|
||||
}
|
||||
|
||||
- (void)setPhotoPreviewOriginDefImageName:(NSString *)photoPreviewOriginDefImageName {
|
||||
_photoPreviewOriginDefImageName = photoPreviewOriginDefImageName;
|
||||
_photoPreviewOriginDefImage = [UIImage tui_imageNamedFromMyBundle:photoPreviewOriginDefImageName];
|
||||
}
|
||||
|
||||
- (void)setPhotoOriginDefImageName:(NSString *)photoOriginDefImageName {
|
||||
_photoOriginDefImageName = photoOriginDefImageName;
|
||||
_photoOriginDefImage = [UIImage tui_imageNamedFromMyBundle:photoOriginDefImageName];
|
||||
}
|
||||
|
||||
- (void)setPhotoOriginSelImageName:(NSString *)photoOriginSelImageName {
|
||||
_photoOriginSelImageName = photoOriginSelImageName;
|
||||
_photoOriginSelImage = [UIImage tui_imageNamedFromMyBundle:photoOriginSelImageName];
|
||||
}
|
||||
|
||||
- (void)setTakePictureImage:(UIImage *)takePictureImage {
|
||||
_takePictureImage = takePictureImage;
|
||||
_takePictureImageName = @"";
|
||||
}
|
||||
|
||||
- (void)setIconThemeColor:(UIColor *)iconThemeColor {
|
||||
_iconThemeColor = iconThemeColor;
|
||||
[self configDefaultImageName];
|
||||
}
|
||||
|
||||
- (void)configDefaultBtnTitle {
|
||||
self.doneBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Done"];
|
||||
self.cancelBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Cancel"];
|
||||
self.previewBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Preview"];
|
||||
self.editImageBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Edit"];
|
||||
self.fullImageBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Full image"];
|
||||
self.settingBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Setting"];
|
||||
self.processHintStr = [NSBundle tui_localizedStringForKey:@"Processing..."];
|
||||
self.editBtnTitleStr = [NSBundle tui_localizedStringForKey:@"Edit"];
|
||||
}
|
||||
|
||||
- (void)observeAuthrizationStatusChange {
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
if ([PHPhotoLibrary authorizationStatus] == 0) {
|
||||
_timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(observeAuthrizationStatusChange) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
if ([[TUIImageManager defaultManager] authorizationStatusAuthorized]) {
|
||||
[_tipLabel removeFromSuperview];
|
||||
[_settingBtn removeFromSuperview];
|
||||
|
||||
[self pushPhotoPickerVc];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pushPhotoPickerVc {
|
||||
_didPushPhotoPickerVc = NO;
|
||||
if (!_didPushPhotoPickerVc && _pushPhotoPickerVc) {
|
||||
TUIPhotoPickerController *photoPickerVc = [[TUIPhotoPickerController alloc] init];
|
||||
photoPickerVc.isFirstAppear = YES;
|
||||
photoPickerVc.columnNumber = self.columnNumber;
|
||||
[[TUIImageManager defaultManager] getCameraRollAlbum:NO completion:^(TUIAlbumModel *model) {
|
||||
photoPickerVc.albumModel = model;
|
||||
[self pushViewController:photoPickerVc animated:YES];
|
||||
self->_didPushPhotoPickerVc = YES;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIAlertController *)showAlertWithTitle:(NSString *)title {
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:[NSBundle tui_localizedStringForKey:@"OK"] style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
return alertController;
|
||||
}
|
||||
|
||||
- (void)showProgressHUD {
|
||||
if (!_progressHUD) {
|
||||
_progressHUD = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_progressHUD setBackgroundColor:[UIColor clearColor]];
|
||||
|
||||
_HUDContainer = [[UIView alloc] init];
|
||||
_HUDContainer.layer.cornerRadius = 8;
|
||||
_HUDContainer.clipsToBounds = YES;
|
||||
_HUDContainer.backgroundColor = [UIColor darkGrayColor];
|
||||
_HUDContainer.alpha = 0.7;
|
||||
|
||||
_HUDIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
|
||||
_HUDLabel = [[UILabel alloc] init];
|
||||
_HUDLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_HUDLabel.text = self.processHintStr;
|
||||
_HUDLabel.font = [UIFont systemFontOfSize:15];
|
||||
_HUDLabel.textColor = [UIColor whiteColor];
|
||||
|
||||
[_HUDContainer addSubview:_HUDLabel];
|
||||
[_HUDContainer addSubview:_HUDIndicatorView];
|
||||
[_progressHUD addSubview:_HUDContainer];
|
||||
}
|
||||
[_HUDIndicatorView startAnimating];
|
||||
UIWindow *applicationWindow;
|
||||
if ([[[UIApplication sharedApplication] delegate] respondsToSelector:@selector(window)]) {
|
||||
applicationWindow = [[[UIApplication sharedApplication] delegate] window];
|
||||
} else {
|
||||
applicationWindow = [[UIApplication sharedApplication] keyWindow];
|
||||
}
|
||||
[applicationWindow addSubview:_progressHUD];
|
||||
[self.view setNeedsLayout];
|
||||
|
||||
self.HUDTimeoutCount++;
|
||||
@weakify(self)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
@strongify(self)
|
||||
self.HUDTimeoutCount--;
|
||||
if (self.HUDTimeoutCount <= 0) {
|
||||
self.HUDTimeoutCount = 0;
|
||||
[self hideProgressHUD];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)hideProgressHUD {
|
||||
if (_progressHUD) {
|
||||
[_HUDIndicatorView stopAnimating];
|
||||
[_progressHUD removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMaxImagesCount:(NSInteger)maxImagesCount {
|
||||
_maxImagesCount = maxImagesCount;
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSInteger)timeout {
|
||||
_timeout = timeout;
|
||||
if (timeout < 5) {
|
||||
_timeout = 5;
|
||||
} else if (_timeout > 600) {
|
||||
_timeout = 600;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setColumnNumber:(NSInteger)columnNumber {
|
||||
_columnNumber = columnNumber;
|
||||
if (columnNumber <= 2) {
|
||||
_columnNumber = 2;
|
||||
} else if (columnNumber >= 6) {
|
||||
_columnNumber = 6;
|
||||
}
|
||||
|
||||
// TUIAlbumPickerController *albumPickerVc = [self.childViewControllers firstObject];
|
||||
// albumPickerVc.columnNumber = _columnNumber;
|
||||
[TUIImageManager defaultManager].columnNumber = _columnNumber;
|
||||
}
|
||||
|
||||
- (void)setPhotoPreviewMaxWidth:(CGFloat)photoPreviewMaxWidth {
|
||||
_photoPreviewMaxWidth = photoPreviewMaxWidth;
|
||||
if (photoPreviewMaxWidth > 800) {
|
||||
_photoPreviewMaxWidth = 800;
|
||||
} else if (photoPreviewMaxWidth < 500) {
|
||||
_photoPreviewMaxWidth = 500;
|
||||
}
|
||||
[TUIImageManager defaultManager].photoPreviewMaxWidth = _photoPreviewMaxWidth;
|
||||
}
|
||||
|
||||
- (void)setSelectedAssets:(NSMutableArray *)selectedAssets {
|
||||
_selectedAssets = selectedAssets;
|
||||
_selectedModels = [NSMutableArray array];
|
||||
_selectedAssetIds = [NSMutableArray array];
|
||||
for (PHAsset *asset in selectedAssets) {
|
||||
TUIAssetModel *model = [TUIAssetModel modelWithAsset:asset type:[[TUIImageManager defaultManager] getAssetMediaType:asset]];
|
||||
model.isSelected = YES;
|
||||
[self addSelectedModel:model];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)settingBtnClick {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
}
|
||||
|
||||
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
|
||||
viewController.automaticallyAdjustsScrollViewInsets = NO;
|
||||
[super pushViewController:viewController animated:animated];
|
||||
}
|
||||
|
||||
- (UIImage *)createImageWithColor:(UIColor *)color size:(CGSize)size radius:(CGFloat)radius {
|
||||
if (!color) {
|
||||
color = self.iconThemeColor;
|
||||
}
|
||||
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSetFillColorWithColor(context, [color CGColor]);
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
CGContextFillPath(context);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)overlayImage:(UIImage *)baseImage withImage:(UIImage *)overlayImage {
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:baseImage.size];
|
||||
UIImage *resultImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull context) {
|
||||
|
||||
[baseImage drawInRect:CGRectMake(0, 0, baseImage.size.width, baseImage.size.height)];
|
||||
CGFloat overlayX = (baseImage.size.width - overlayImage.size.width) / 2.0;
|
||||
CGFloat overlayY = (baseImage.size.height - overlayImage.size.height) / 2.0;
|
||||
CGPoint overlayPosition = CGPointMake(overlayX, overlayY);
|
||||
[overlayImage drawAtPoint:overlayPosition];
|
||||
}];
|
||||
return resultImage;
|
||||
}
|
||||
|
||||
- (UIImage *)createHollowCircleWithColor:(UIColor *)color size:(CGSize)size lineWidth:(CGFloat)lineWidth {
|
||||
if (!color) {
|
||||
color = [UIColor blackColor];
|
||||
}
|
||||
|
||||
CGFloat diameter = MIN(size.width, size.height);
|
||||
CGFloat scale = [UIScreen mainScreen].scale;
|
||||
CGSize scaledSize = CGSizeMake(diameter * scale, diameter * scale);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(scaledSize, NO, scale);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetShouldAntialias(context, YES);
|
||||
|
||||
CGContextClearRect(context, CGRectMake(0, 0, scaledSize.width, scaledSize.height));
|
||||
|
||||
CGContextSetStrokeColorWithColor(context, [color CGColor]);
|
||||
CGContextSetLineWidth(context, lineWidth * scale);
|
||||
|
||||
CGRect scaledRect = CGRectMake(0.0f, 0.0f, scaledSize.width, scaledSize.height);
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:scaledRect];
|
||||
CGContextAddPath(context, path.CGPath);
|
||||
|
||||
CGContextStrokePath(context);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
- (UIImage *)resizeImage:(UIImage *)image toSize:(CGSize)newSize {
|
||||
UIGraphicsBeginImageContextWithOptions(newSize, NO, [UIScreen mainScreen].scale);
|
||||
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
|
||||
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return newImage;
|
||||
}
|
||||
#pragma mark - UIContentContainer
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (![UIApplication sharedApplication].statusBarHidden) {
|
||||
if (self.needShowStatusBar) [UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
CGFloat progressHUDY = CGRectGetMaxY(self.navigationBar.frame);
|
||||
_progressHUD.frame = CGRectMake(0, progressHUDY, self.view.mm_w, self.view.mm_h - progressHUDY);
|
||||
_HUDContainer.frame = CGRectMake((self.view.mm_w - 120) / 2, (_progressHUD.mm_h - 90 - progressHUDY) / 2, 120, 90);
|
||||
_HUDIndicatorView.frame = CGRectMake(45, 15, 30, 30);
|
||||
_HUDLabel.frame = CGRectMake(0,40, 120, 50);
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
- (void)onCancelButtonClick {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
|
||||
@implementation UIImage (MyBundle)
|
||||
|
||||
+ (UIImage *)tui_imageNamedFromMyBundle:(NSString *)name {
|
||||
NSBundle *imageBundle = [NSBundle tz_imagePickerBundle];
|
||||
name = [name stringByAppendingString:@"@2x"];
|
||||
NSString *imagePath = [imageBundle pathForResource:name ofType:@"png"];
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
|
||||
if (!image) {
|
||||
// Compatible with the way the business side sets images
|
||||
name = [name stringByReplacingOccurrencesOfString:@"@2x" withString:@""];
|
||||
image = [UIImage imageNamed:name];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TUICommonTools
|
||||
|
||||
+ (UIEdgeInsets)tui_safeAreaInsets {
|
||||
UIWindow *window = [UIApplication sharedApplication].windows.firstObject;
|
||||
if (![window isKeyWindow]) {
|
||||
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
|
||||
if (CGRectEqualToRect(keyWindow.bounds, [UIScreen mainScreen].bounds)) {
|
||||
window = keyWindow;
|
||||
}
|
||||
}
|
||||
if (@available(iOS 11.0, *)) {
|
||||
UIEdgeInsets insets = [window safeAreaInsets];
|
||||
return insets;
|
||||
}
|
||||
return UIEdgeInsetsZero;
|
||||
}
|
||||
|
||||
+ (BOOL)tui_isIPhoneX {
|
||||
if ([UIWindow instancesRespondToSelector:@selector(safeAreaInsets)]) {
|
||||
return [self tui_safeAreaInsets].bottom > 0;
|
||||
}
|
||||
return (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)) ||
|
||||
CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(390, 844)) ||
|
||||
CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(844, 390)) ||
|
||||
CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(428, 926)) ||
|
||||
CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(926, 428)));
|
||||
}
|
||||
|
||||
+ (BOOL)tui_isLandscape {
|
||||
if ([UIApplication sharedApplication].statusBarOrientation == UIDeviceOrientationLandscapeRight ||
|
||||
[UIApplication sharedApplication].statusBarOrientation == UIDeviceOrientationLandscapeLeft) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+ (CGFloat)tui_statusBarHeight {
|
||||
if ([UIWindow instancesRespondToSelector:@selector(safeAreaInsets)]) {
|
||||
return [self tui_safeAreaInsets].top ?: 20;
|
||||
}
|
||||
return 20;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)tui_getInfoDictionary {
|
||||
NSDictionary *infoDict = [NSBundle mainBundle].localizedInfoDictionary;
|
||||
if (!infoDict || !infoDict.count) {
|
||||
infoDict = [NSBundle mainBundle].infoDictionary;
|
||||
}
|
||||
if (!infoDict || !infoDict.count) {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
|
||||
infoDict = [NSDictionary dictionaryWithContentsOfFile:path];
|
||||
}
|
||||
return infoDict ? infoDict : @{};
|
||||
}
|
||||
|
||||
+ (NSString *)tui_getAppName {
|
||||
NSDictionary *infoDict = [self tui_getInfoDictionary];
|
||||
NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"];
|
||||
if (!appName) appName = [infoDict valueForKey:@"CFBundleName"];
|
||||
if (!appName) appName = [infoDict valueForKey:@"CFBundleExecutable"];
|
||||
if (!appName) {
|
||||
infoDict = [NSBundle mainBundle].infoDictionary;
|
||||
appName = [infoDict valueForKey:@"CFBundleDisplayName"];
|
||||
if (!appName) appName = [infoDict valueForKey:@"CFBundleName"];
|
||||
if (!appName) appName = [infoDict valueForKey:@"CFBundleExecutable"];
|
||||
}
|
||||
return appName;
|
||||
}
|
||||
|
||||
+ (BOOL)tui_isRightToLeftLayout {
|
||||
return [TUIGlobalization getRTLOption];
|
||||
}
|
||||
|
||||
+ (void)configBarButtonItem:(UIBarButtonItem *)item multiMediaNavVC:(TUIMultimediaNavController *)multiMediaNavVC {
|
||||
item.tintColor = multiMediaNavVC.barItemTextColor;
|
||||
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
|
||||
textAttrs[NSForegroundColorAttributeName] = multiMediaNavVC.barItemTextColor;
|
||||
textAttrs[NSFontAttributeName] = multiMediaNavVC.barItemTextFont;
|
||||
[item setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
|
||||
|
||||
NSMutableDictionary *textAttrsHighlighted = [NSMutableDictionary dictionary];
|
||||
textAttrsHighlighted[NSFontAttributeName] = multiMediaNavVC.barItemTextFont;
|
||||
[item setTitleTextAttributes:textAttrsHighlighted forState:UIControlStateHighlighted];
|
||||
}
|
||||
|
||||
+ (BOOL)isICloudSyncError:(NSError *)error {
|
||||
if (!error) return NO;
|
||||
if ([error.domain isEqualToString:@"CKErrorDomain"] || [error.domain isEqualToString:@"CloudPhotoLibraryErrorDomain"]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUIImagePickerConfig ()
|
||||
@property (strong, nonatomic) NSSet *supportedLanguages;
|
||||
@end
|
||||
|
||||
@implementation TUIImagePickerConfig
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static TUIImagePickerConfig *config = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (config == nil) {
|
||||
config = [[TUIImagePickerConfig alloc] init];
|
||||
config.supportedLanguages = [NSSet setWithObjects:@"zh-Hans",@"zh-Hant",@"en", @"ar", nil];
|
||||
config.preferredLanguage = nil;
|
||||
}
|
||||
});
|
||||
NSString *identifer = [TUIGlobalization getPreferredLanguage];
|
||||
[config setPreferredLanguage:identifer];
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onChangeLanguage) name:TUIChangeLanguageNotification object:nil];
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
+ (void)onChangeLanguage {
|
||||
NSString *identifer = [TUIGlobalization getPreferredLanguage];
|
||||
[[self sharedInstance] setPreferredLanguage:identifer];
|
||||
}
|
||||
|
||||
- (void)setPreferredLanguage:(NSString *)preferredLanguage {
|
||||
_preferredLanguage = preferredLanguage;
|
||||
|
||||
if (!preferredLanguage || !preferredLanguage.length) {
|
||||
preferredLanguage = [NSLocale preferredLanguages].firstObject;
|
||||
}
|
||||
|
||||
NSString *usedLanguage = @"en";
|
||||
for (NSString *language in self.supportedLanguages) {
|
||||
if ([preferredLanguage hasPrefix:language]) {
|
||||
usedLanguage = language;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_languageBundle = [NSBundle bundleWithPath:[[NSBundle tz_imagePickerBundle] pathForResource:usedLanguage ofType:@"lproj"]];
|
||||
}
|
||||
|
||||
@end
|
||||
16
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPickerController.h
Normal file
16
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPickerController.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// TUIPhotoPickerController.h
|
||||
// TUIPhotoPickerController
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
@interface TUIPhotoPickerController : UIViewController
|
||||
|
||||
@property (nonatomic, assign) BOOL isFirstAppear;
|
||||
@property (nonatomic, assign) NSInteger columnNumber;
|
||||
@property (nonatomic, strong) TUIAlbumModel *albumModel;//默认选中的类别 bucket
|
||||
@property (nonatomic, strong) NSMutableArray *assetModels;//该类别下所有的照片
|
||||
@end
|
||||
927
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPickerController.m
Executable file
927
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPickerController.m
Executable file
@@ -0,0 +1,927 @@
|
||||
//
|
||||
// TUIPhotoPickerController.m
|
||||
// TUIPhotoPickerController
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import "TUIPhotoPickerController.h"
|
||||
#import "TUIMultimediaNavController.h"
|
||||
#import "TUIPhotoPreviewController.h"
|
||||
#import <TUIMultimediaCore/TUIAssetCell.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
#import <TUIMultimediaCore/TUIAuthFooterTipView.h>
|
||||
#import <TUICore/UIView+TUILayout.h>
|
||||
#import <TUICore/UIView+TUIUtil.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import "TUIRequestOperation.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <PhotosUI/PhotosUI.h>
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
#import <TUIMultimediaCore/TUIAlbumCollectionView.h>
|
||||
#import <TUIMultimediaCore/TUIMultimediaCore.h>
|
||||
|
||||
@interface TUIPhotoPickerController ()<UICollectionViewDataSource,UICollectionViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate, PHPhotoLibraryChangeObserver,UITableViewDataSource,UITableViewDelegate> {
|
||||
TUIAlbumModel *_albumModel;
|
||||
|
||||
UIView *_bottomToolBar;
|
||||
UIButton *_previewButton;
|
||||
UIButton *_doneButton;
|
||||
UIImageView *_numberImageView;
|
||||
UILabel *_numberLabel;
|
||||
UIButton *_originalPhotoButton;
|
||||
UILabel *_originalPhotoLabel;
|
||||
|
||||
BOOL _shouldScrollToBottom;
|
||||
BOOL _showTakePhotoBtn;
|
||||
BOOL _authorizationLimited;
|
||||
|
||||
CGFloat _offsetItemCount;
|
||||
}
|
||||
@property CGRect previousPreheatRect;
|
||||
@property (nonatomic, assign) BOOL isSelectOriginalPhoto;
|
||||
@property (nonatomic, strong) TUIAlbumCollectionView *collectionView;
|
||||
@property (nonatomic, strong) TUIAuthFooterTipView *authFooterTipView;
|
||||
@property (nonatomic, strong) UICollectionViewFlowLayout *layout;
|
||||
@property (nonatomic, strong) UIImagePickerController *imagePickerVc;
|
||||
@property (nonatomic, strong) CLLocation *location;
|
||||
@property (nonatomic, strong) NSOperationQueue *operationQueue;
|
||||
@property (nonatomic, assign) BOOL isSavingMedia;
|
||||
@property (nonatomic, assign) BOOL isFetchingMedia;
|
||||
|
||||
@property (nonatomic, strong) UIButton *albumChangeButton;
|
||||
@property (nonatomic, strong) UIImageView *albumChangeIcon;
|
||||
@property (nonatomic, strong) UIView *albumBKView;
|
||||
@property (nonatomic, strong) UITableView *albumTableView;
|
||||
@property (nonatomic, strong) NSMutableArray *albumList;
|
||||
@property (nonatomic, assign) BOOL isShowingAlbums;
|
||||
|
||||
@end
|
||||
|
||||
static CGSize AssetGridThumbnailSize;
|
||||
static CGFloat itemMargin = 5;
|
||||
|
||||
@implementation TUIPhotoPickerController
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
- (UIImagePickerController *)imagePickerVc {
|
||||
if (_imagePickerVc == nil) {
|
||||
_imagePickerVc = [[UIImagePickerController alloc] init];
|
||||
_imagePickerVc.delegate = self;
|
||||
_imagePickerVc.navigationBar.barTintColor = self.navigationController.navigationBar.barTintColor;
|
||||
_imagePickerVc.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;
|
||||
UIBarButtonItem *tzBarItem, *BarItem;
|
||||
if (@available(iOS 9, *)) {
|
||||
tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TUIMultimediaNavController class]]];
|
||||
BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]];
|
||||
} else {
|
||||
tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TUIMultimediaNavController class], nil];
|
||||
BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil];
|
||||
}
|
||||
NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal];
|
||||
[BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal];
|
||||
}
|
||||
return _imagePickerVc;
|
||||
}
|
||||
|
||||
- (void)setAlbumModel:(TUIAlbumModel *)albumModel {
|
||||
if (albumModel) {
|
||||
_albumModel = albumModel;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self fetchAlbums];
|
||||
[self fetchAssets];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (TUIAlbumModel *)albumModel {
|
||||
return _albumModel;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
multiMediaNavVC.isSelectOriginalPhoto = _isSelectOriginalPhoto;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
// Determine the size of the thumbnails to request from the PHCachingImageManager
|
||||
CGFloat scale = 2.0;
|
||||
if ([UIScreen mainScreen].bounds.size.width > 600) {
|
||||
scale = 1.0;
|
||||
}
|
||||
CGSize cellSize = ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).itemSize;
|
||||
AssetGridThumbnailSize = CGSizeMake(cellSize.width * scale, cellSize.height * scale);
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
self.isFirstAppear = NO;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)navLeftBarButtonClick{
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
if ([[TUIImageManager defaultManager] authorizationStatusAuthorized] || [System_Version floatValue] < 15.0) {
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
|
||||
}
|
||||
self.isFirstAppear = YES;
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
_isSelectOriginalPhoto = multiMediaNavVC.isSelectOriginalPhoto;
|
||||
_shouldScrollToBottom = YES;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
self.view.backgroundColor = UIColor.tertiarySystemBackgroundColor;
|
||||
} else {
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
UIView *albumChangeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];
|
||||
albumChangeView.backgroundColor = [UIColor clearColor];
|
||||
self.albumChangeButton =[UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[self.albumChangeButton addTarget:self action:@selector(onAlbumChangeButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[albumChangeView addSubview:self.albumChangeButton];
|
||||
|
||||
self.albumChangeIcon = [[UIImageView alloc] init];
|
||||
self.albumChangeIcon.image = [UIImage tui_imageNamedFromMyBundle:@"change_album"];
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onAlbumChangeButtonClick)];
|
||||
[self.albumChangeIcon addGestureRecognizer:tap];
|
||||
self.albumChangeIcon.userInteractionEnabled = YES;
|
||||
[albumChangeView addSubview:self.albumChangeIcon];
|
||||
self.navigationItem.titleView = albumChangeView;
|
||||
[self refreshAlbumChangeView];
|
||||
|
||||
UIBarButtonItem *cancelItem = [[UIBarButtonItem alloc] initWithTitle:multiMediaNavVC.cancelBtnTitleStr style:UIBarButtonItemStylePlain target:multiMediaNavVC action:@selector(onCancelButtonClick)];
|
||||
[TUICommonTools configBarButtonItem:cancelItem multiMediaNavVC:multiMediaNavVC];
|
||||
self.navigationItem.leftBarButtonItem = cancelItem;
|
||||
|
||||
_showTakePhotoBtn = NO;
|
||||
_authorizationLimited = self.albumModel.isCameraRoll && [[TUIImageManager defaultManager] isPHAuthorizationStatusLimited];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
|
||||
|
||||
self.operationQueue = [[NSOperationQueue alloc] init];
|
||||
self.operationQueue.maxConcurrentOperationCount = 1;
|
||||
|
||||
[self configCollectionView];
|
||||
[self configBottomToolBar];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
self.view.backgroundColor = [UIColor blackColor];
|
||||
|
||||
CGFloat top = 0;
|
||||
CGFloat collectionViewHeight = 0;
|
||||
CGFloat naviBarHeight = self.navigationController.navigationBar.mm_h;
|
||||
CGFloat footerTipViewH = _authorizationLimited ? 80 : 0;
|
||||
BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden;
|
||||
BOOL isFullScreen = self.view.mm_h == [UIScreen mainScreen].bounds.size.height;
|
||||
CGFloat toolBarHeight = 50 + [TUICommonTools tui_safeAreaInsets].bottom;
|
||||
if (self.navigationController.navigationBar.isTranslucent) {
|
||||
top = naviBarHeight;
|
||||
if (!isStatusBarHidden && isFullScreen) {
|
||||
top += [TUICommonTools tui_statusBarHeight];
|
||||
}
|
||||
collectionViewHeight = self.view.mm_h - toolBarHeight - top;
|
||||
} else {
|
||||
collectionViewHeight = self.view.mm_h - toolBarHeight;
|
||||
}
|
||||
collectionViewHeight -= footerTipViewH;
|
||||
|
||||
self.collectionView.frame = CGRectMake(0, top, self.view.mm_w, collectionViewHeight);
|
||||
CGFloat itemWH = (self.view.mm_w - (self.columnNumber + 1) * itemMargin) / self.columnNumber;
|
||||
_layout.itemSize = CGSizeMake(itemWH, itemWH);
|
||||
_layout.minimumInteritemSpacing = itemMargin;
|
||||
_layout.minimumLineSpacing = itemMargin;
|
||||
[self.collectionView setCollectionViewLayout:_layout];
|
||||
if (_offsetItemCount > 0) {
|
||||
CGFloat offsetY = _offsetItemCount * (_layout.itemSize.height + _layout.minimumLineSpacing);
|
||||
[self.collectionView setContentOffset:CGPointMake(0, offsetY)];
|
||||
}
|
||||
|
||||
CGFloat toolBarTop = 0;
|
||||
if (!self.navigationController.navigationBar.isHidden) {
|
||||
toolBarTop = self.view.mm_h - toolBarHeight;
|
||||
} else {
|
||||
CGFloat navigationHeight = naviBarHeight + [TUICommonTools tui_statusBarHeight];
|
||||
toolBarTop = self.view.mm_h - toolBarHeight - navigationHeight;
|
||||
}
|
||||
_bottomToolBar.frame = CGRectMake(0, toolBarTop, self.view.mm_w, toolBarHeight);
|
||||
if (_authFooterTipView) {
|
||||
CGFloat footerTipViewY = _bottomToolBar ? toolBarTop - footerTipViewH : self.view.mm_h - footerTipViewH;
|
||||
_authFooterTipView.frame = CGRectMake(0, footerTipViewY, self.view.mm_w, footerTipViewH);;
|
||||
}
|
||||
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
CGFloat previewWidth = [multiMediaNavVC.previewBtnTitleStr boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil].size.width + 2;
|
||||
_previewButton.frame = CGRectMake(10, 3, previewWidth, 44);
|
||||
|
||||
CGFloat fullImageWidth = [multiMediaNavVC.fullImageBtnTitleStr boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} context:nil].size.width;
|
||||
fullImageWidth += 40;
|
||||
_originalPhotoButton.frame = CGRectMake(_bottomToolBar.mm_w / 2 - fullImageWidth / 2, 0, fullImageWidth, 44);
|
||||
[_originalPhotoLabel sizeToFit];
|
||||
_originalPhotoLabel.frame = CGRectMake(self.view.mm_w /2 -_originalPhotoLabel.mm_w /2 ,
|
||||
33,
|
||||
_originalPhotoLabel.mm_w,
|
||||
_originalPhotoLabel.mm_h);
|
||||
[_doneButton sizeToFit];
|
||||
_doneButton.frame = CGRectMake(self.view.mm_w - _doneButton.mm_w - 12, 0, MAX(44, _doneButton.mm_w), 50);
|
||||
_numberImageView.frame = CGRectMake(_doneButton.mm_x - 24 - 5, 13, 24, 24);
|
||||
_numberLabel.frame = _numberImageView.frame;
|
||||
|
||||
if (isRTL()) {
|
||||
for (UIView *subview in _bottomToolBar.subviews) {
|
||||
[subview resetFrameToFitRTL];
|
||||
}
|
||||
}
|
||||
[TUIImageManager defaultManager].columnNumber = [TUIImageManager defaultManager].columnNumber;
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Album Logic
|
||||
- (UIView *)albumBKView{
|
||||
if (!_albumBKView) {
|
||||
_albumBKView = [[UIView alloc] initWithFrame:CGRectMake(self.collectionView.mm_x, self.collectionView.mm_y,
|
||||
self.collectionView.mm_w, self.view.mm_h - self.collectionView.mm_x)];
|
||||
_albumBKView.backgroundColor = [UIColor blackColor];
|
||||
_albumBKView.alpha = 0;
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClickAlbumBKView)];
|
||||
[_albumBKView addGestureRecognizer:tap];
|
||||
[self.view insertSubview:_albumBKView belowSubview:self.albumTableView];
|
||||
}
|
||||
return _albumBKView;
|
||||
}
|
||||
|
||||
- (UITableView *)albumTableView {
|
||||
if (!_albumTableView) {
|
||||
CGFloat tableViewHeight = self.collectionView.mm_h - 100;
|
||||
_albumTableView = [[UITableView alloc] initWithFrame:CGRectMake(self.collectionView.mm_x, self.collectionView.mm_y - tableViewHeight,
|
||||
self.collectionView.mm_w, tableViewHeight) style:UITableViewStylePlain];
|
||||
_albumTableView.rowHeight = 54;
|
||||
_albumTableView.backgroundColor = RGB(45, 45, 45);
|
||||
_albumTableView.separatorColor = RGB(75, 75, 75);
|
||||
_albumTableView.tableFooterView = [[UIView alloc] init];
|
||||
_albumTableView.dataSource = self;
|
||||
_albumTableView.delegate = self;
|
||||
[_albumTableView registerClass:[TUIAlbumCell class] forCellReuseIdentifier:@"TUIAlbumCell"];
|
||||
[self.view addSubview:_albumTableView];
|
||||
}
|
||||
return _albumTableView;
|
||||
}
|
||||
|
||||
- (void)onAlbumChangeButtonClick {
|
||||
if (self.isShowingAlbums) {
|
||||
[self hideAllAlbums];
|
||||
} else {
|
||||
[self showAllAlbums];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onClickAlbumBKView {
|
||||
[self hideAllAlbums];
|
||||
}
|
||||
|
||||
- (void)fetchAlbums {
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
@weakify(self)
|
||||
[[TUIMultimediaCore defaultManager] getBucketList:!self.isFirstAppear completion:^(NSArray<TUIAlbumModel *> * _Nonnull bucketList) {
|
||||
@strongify(self)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.albumList = [NSMutableArray arrayWithArray:bucketList];
|
||||
});
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)showAllAlbums {
|
||||
[self refreshAlbumTableView];
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
self.albumBKView.alpha = 0.5;
|
||||
self.albumTableView.frame = CGRectOffset(self.albumTableView.frame, 0, self.albumTableView.mm_h);
|
||||
self.albumChangeIcon.transform = CGAffineTransformMakeRotation(M_PI);
|
||||
}];
|
||||
self.isShowingAlbums = YES;
|
||||
}
|
||||
|
||||
- (void)hideAllAlbums {
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
self.albumBKView.alpha = 0;
|
||||
self.albumTableView.frame = CGRectOffset(self.albumTableView.frame, 0, -self.albumTableView.mm_h);
|
||||
self.albumChangeIcon.transform = CGAffineTransformMakeRotation(0);
|
||||
}];
|
||||
self.isShowingAlbums = NO;
|
||||
}
|
||||
|
||||
- (void)refreshAlbumTableView {
|
||||
[self updateAlbumStatus];
|
||||
[self.albumTableView reloadData];
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
@weakify(self)
|
||||
[[TUIMultimediaCore defaultManager] getBucketList:!self.isFirstAppear completion:^(NSArray<TUIAlbumModel *> * _Nonnull bucketList) {
|
||||
@strongify(self)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.albumList = [NSMutableArray arrayWithArray:bucketList];
|
||||
[self updateAlbumStatus];
|
||||
[self.albumTableView reloadData];
|
||||
});
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateAlbumStatus {
|
||||
for (TUIAlbumModel *model in self.albumList) {
|
||||
if ([model.name isEqualToString:self.albumModel.name]) {
|
||||
model.isSelected = YES;
|
||||
} else {
|
||||
model.isSelected = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshAlbumChangeView {
|
||||
CGFloat albumChangeIconSize = 20;
|
||||
[self.albumChangeButton setTitle:self.albumModel.name forState:UIControlStateNormal];
|
||||
[self.albumChangeButton sizeToFit];
|
||||
self.albumChangeButton.mm_y = 5;
|
||||
self.albumChangeButton.mm_centerX = (self.navigationItem.titleView.mm_w - albumChangeIconSize) / 2;
|
||||
self.albumChangeIcon.mm_width(20).mm_height(20).mm_left(self.albumChangeButton.mm_maxX + 4).mm__centerY(self.albumChangeButton.mm_centerY);
|
||||
if (isRTL()) {
|
||||
[self.albumChangeButton resetFrameToFitRTL];
|
||||
[self.albumChangeIcon resetFrameToFitRTL];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Asset Logic
|
||||
- (void)configCollectionView {
|
||||
if (!self.collectionView) {
|
||||
_layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
self.collectionView = [[TUIAlbumCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout];
|
||||
self.collectionView.backgroundColor = RGB(25, 25, 25);
|
||||
self.collectionView.alwaysBounceHorizontal = NO;
|
||||
self.collectionView.contentInset = UIEdgeInsetsMake(itemMargin, itemMargin, itemMargin, itemMargin);
|
||||
self.collectionView.maxSelectDescribeText = [NSBundle tui_localizedStringForKey:@"Select a maximum of %zd photos"];
|
||||
[self.view addSubview:self.collectionView];
|
||||
if (self.albumModel) {
|
||||
[self.collectionView setBucket:self.albumModel];
|
||||
[self prepareScrollCollectionViewToBottom];
|
||||
}
|
||||
[self.collectionView setClickListener:(id)self];
|
||||
}
|
||||
self.collectionView.contentSize = CGSizeMake(self.view.mm_w, (([self getAllCellCount] + self.columnNumber - 1) / self.columnNumber) * self.view.mm_w);
|
||||
|
||||
if (!_authFooterTipView && _authorizationLimited) {
|
||||
_authFooterTipView = [[TUIAuthFooterTipView alloc] initWithFrame:CGRectMake(0, 0, self.view.mm_w, 80)];
|
||||
_authFooterTipView.tipImage = [UIImage tui_imageNamedFromMyBundle:@"tip"];
|
||||
_authFooterTipView.deftailImage = [UIImage tui_imageNamedFromMyBundle:@"right_arrow"];
|
||||
UITapGestureRecognizer *footTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openSettingsApplication)];
|
||||
[_authFooterTipView addGestureRecognizer:footTap];
|
||||
[self.view addSubview:_authFooterTipView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fetchAssets {
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
CGFloat systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
|
||||
if (self->_showTakePhotoBtn || self->_isFirstAppear || !self.albumModel.assetModels || systemVersion >= 14.0) {
|
||||
@weakify(self)
|
||||
[[TUIImageManager defaultManager] getAssetsFromFetchResult:self.albumModel.result completion:^(NSArray<TUIAssetModel *> *models) {
|
||||
@strongify(self)
|
||||
self.assetModels = [NSMutableArray arrayWithArray:models];
|
||||
}];
|
||||
} else {
|
||||
self.assetModels = [NSMutableArray arrayWithArray:self.albumModel.assetModels];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark ToolBar Logic
|
||||
- (void)configBottomToolBar {
|
||||
if (_bottomToolBar) return;
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
|
||||
_bottomToolBar = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_bottomToolBar.backgroundColor = RGBA(29, 29, 29, 0.98);
|
||||
|
||||
_previewButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[_previewButton addTarget:self action:@selector(onPreviewButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
_previewButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_previewButton setTitle:multiMediaNavVC.previewBtnTitleStr forState:UIControlStateNormal];
|
||||
[_previewButton setTitle:multiMediaNavVC.previewBtnTitleStr forState:UIControlStateDisabled];
|
||||
[_previewButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_previewButton setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
|
||||
_previewButton.enabled = [self.collectionView getSelectedPhotoList].count;
|
||||
|
||||
_originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, [TUICommonTools tui_isRightToLeftLayout] ? 10 : -10, 0, 0);
|
||||
[_originalPhotoButton addTarget:self action:@selector(onOriginalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
_originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_originalPhotoButton setTitle:multiMediaNavVC.fullImageBtnTitleStr forState:UIControlStateNormal];
|
||||
[_originalPhotoButton setTitle:multiMediaNavVC.fullImageBtnTitleStr forState:UIControlStateSelected];
|
||||
[_originalPhotoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_originalPhotoButton setImage:multiMediaNavVC.photoOriginDefImage forState:UIControlStateNormal];
|
||||
[_originalPhotoButton setImage:multiMediaNavVC.photoOriginSelImage forState:UIControlStateSelected];
|
||||
_originalPhotoButton.imageView.clipsToBounds = YES;
|
||||
_originalPhotoButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
_originalPhotoButton.selected = _isSelectOriginalPhoto;
|
||||
|
||||
_originalPhotoLabel = [[UILabel alloc] init];
|
||||
_originalPhotoLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_originalPhotoLabel.font = [UIFont systemFontOfSize:12];
|
||||
_originalPhotoLabel.textColor = [UIColor grayColor];
|
||||
if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes];
|
||||
|
||||
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_doneButton addTarget:self action:@selector(onDoneButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_doneButton setTitle:multiMediaNavVC.doneBtnTitleStr forState:UIControlStateNormal];
|
||||
[_doneButton setTitle:multiMediaNavVC.doneBtnTitleStr forState:UIControlStateDisabled];
|
||||
[_doneButton setTitleColor:multiMediaNavVC.oKButtonTitleColorNormal forState:UIControlStateNormal];
|
||||
[_doneButton setTitleColor:multiMediaNavVC.oKButtonTitleColorDisabled forState:UIControlStateDisabled];
|
||||
_doneButton.enabled = [self.collectionView getSelectedPhotoList].count;
|
||||
|
||||
_numberImageView = [[UIImageView alloc] initWithImage:multiMediaNavVC.photoNumberIconImage];
|
||||
_numberImageView.hidden = [self.collectionView getSelectedPhotoList].count <= 0;
|
||||
_numberImageView.clipsToBounds = YES;
|
||||
_numberImageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
_numberImageView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
_numberLabel = [[UILabel alloc] init];
|
||||
_numberLabel.font = [UIFont systemFontOfSize:15];
|
||||
_numberLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_numberLabel.textColor = [UIColor whiteColor];
|
||||
_numberLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_numberLabel.text = [NSString stringWithFormat:@"%zd",[self.collectionView getSelectedPhotoList].count];
|
||||
_numberLabel.hidden = [self.collectionView getSelectedPhotoList].count <= 0;
|
||||
_numberLabel.backgroundColor = [UIColor clearColor];
|
||||
_numberLabel.userInteractionEnabled = YES;
|
||||
|
||||
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDoneButtonClick)];
|
||||
[_numberLabel addGestureRecognizer:tapGesture];
|
||||
|
||||
[_bottomToolBar addSubview:_previewButton];
|
||||
[_bottomToolBar addSubview:_doneButton];
|
||||
[_bottomToolBar addSubview:_numberImageView];
|
||||
[_bottomToolBar addSubview:_numberLabel];
|
||||
[_bottomToolBar addSubview:_originalPhotoButton];
|
||||
[_bottomToolBar addSubview:_originalPhotoLabel];
|
||||
[self.view addSubview:_bottomToolBar];
|
||||
}
|
||||
|
||||
- (void)onPreviewButtonClick {
|
||||
TUIPhotoPreviewController *photoPreviewVc = [[TUIPhotoPreviewController alloc] init];
|
||||
NSArray<TUIAssetModel *> * selectArray = [self.collectionView getSelectedPhotoList];
|
||||
if (selectArray.count > 0) {
|
||||
TUIAssetModel *firstSelectedModel = selectArray.firstObject;
|
||||
NSUInteger currentIndex = 0;
|
||||
for (TUIAssetModel *modelItem in self.assetModels) {
|
||||
if ([modelItem.asset.localIdentifier isEqualToString:firstSelectedModel.asset.localIdentifier]) {
|
||||
photoPreviewVc.currentShowIndex = currentIndex;
|
||||
break;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
@weakify(self)
|
||||
photoPreviewVc.addAssetBlock = ^(TUIAssetModel *model) {
|
||||
model.isSelected = YES;
|
||||
[self.collectionView setSelected:model selected:YES];
|
||||
};
|
||||
photoPreviewVc.refreshAssetBlock = ^(TUIAssetModel *model) {
|
||||
[self.collectionView freshPhoto:model];
|
||||
};
|
||||
photoPreviewVc.delAssetBlock = ^(TUIAssetModel *model) {
|
||||
NSArray *selectedModels = [NSArray arrayWithArray:[self.collectionView getSelectedPhotoList]];
|
||||
for (TUIAssetModel *modelItem in selectedModels) {
|
||||
if ([model.asset.localIdentifier isEqualToString:modelItem.asset.localIdentifier]) {
|
||||
model.isSelected = NO;
|
||||
[self.collectionView setSelected:modelItem selected:NO];
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
[self pushPhotoPrevireViewController:photoPreviewVc needCheckSelectedModels:YES];
|
||||
}
|
||||
|
||||
- (void)onOriginalPhotoButtonClick {
|
||||
_originalPhotoButton.selected = !_originalPhotoButton.isSelected;
|
||||
_isSelectOriginalPhoto = _originalPhotoButton.selected;
|
||||
_originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;
|
||||
if (_isSelectOriginalPhoto) {
|
||||
[self getSelectedPhotoBytes];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onDoneButtonClick {
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
[multiMediaNavVC showProgressHUD];
|
||||
_doneButton.enabled = NO;
|
||||
self.isFetchingMedia = YES;
|
||||
|
||||
NSMutableArray *pickModels = [NSMutableArray array];
|
||||
__block BOOL havenotShowAlert = YES;
|
||||
__block UIAlertController *alertView;
|
||||
[TUIImageManager defaultManager].shouldFixOrientation = YES;
|
||||
for (NSInteger i = 0; i < [self.collectionView getSelectedPhotoList].count; i++) {
|
||||
TUIAssetModel *model = [self.collectionView getSelectedPhotoList][i];
|
||||
TUIRequestOperation *operation = [[TUIRequestOperation alloc] initWithAsset:model.asset completed:^(UIImage * _Nonnull photo, NSDictionary * _Nonnull info) {
|
||||
TUIAssetPickModel *pickModel = [[TUIAssetPickModel alloc] init];
|
||||
pickModel.image = photo;
|
||||
pickModel.info = info;
|
||||
pickModel.asset = model.asset;
|
||||
if (model.editurl) {
|
||||
pickModel.editurl = model.editurl;
|
||||
}
|
||||
|
||||
if (model.editImage) {
|
||||
pickModel.editImage = model.editImage;
|
||||
}
|
||||
|
||||
[pickModels addObject:pickModel];
|
||||
if (pickModels.count != [self.collectionView getSelectedPhotoList].count) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (havenotShowAlert && alertView) {
|
||||
[alertView dismissViewControllerAnimated:YES completion:^{
|
||||
alertView = nil;
|
||||
[self didGetAllPhotos:pickModels];
|
||||
}];
|
||||
} else {
|
||||
[self didGetAllPhotos:pickModels];
|
||||
}
|
||||
} progress:^(double progress, NSError * _Nonnull error, BOOL * _Nonnull stop, NSDictionary * _Nonnull info) {
|
||||
if (progress < 1 && havenotShowAlert && !alertView) {
|
||||
alertView = [multiMediaNavVC showAlertWithTitle:[NSBundle tui_localizedStringForKey:@"Synchronizing photos from iCloud"]];
|
||||
havenotShowAlert = NO;
|
||||
return;
|
||||
}
|
||||
if (progress >= 1) {
|
||||
havenotShowAlert = YES;
|
||||
}
|
||||
}];
|
||||
[self.operationQueue addOperation:operation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didGetAllPhotos:(NSArray<TUIAssetPickModel *> *)pickModels {
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
[multiMediaNavVC hideProgressHUD];
|
||||
_doneButton.enabled = YES;
|
||||
self.isFetchingMedia = NO;
|
||||
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:^{
|
||||
[self callDelegateMethodWithPhotos:pickModels];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)callDelegateMethodWithPhotos:(NSArray<TUIAssetPickModel *> *)pickModels {
|
||||
NSMutableArray *photos = [NSMutableArray array];
|
||||
NSMutableArray *assets = [NSMutableArray array];
|
||||
NSMutableArray *infoArr = [NSMutableArray array];
|
||||
for (TUIAssetPickModel *model in pickModels) {
|
||||
[photos addObject:model.image];
|
||||
[assets addObject:model.asset];
|
||||
[infoArr addObject:model.info];
|
||||
}
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
if (multiMediaNavVC.didFinishPickingHandle) {
|
||||
multiMediaNavVC.didFinishPickingHandle(pickModels,_isSelectOriginalPhoto);
|
||||
}
|
||||
if (multiMediaNavVC.didFinishPickingPhotosWithInfosHandle) {
|
||||
multiMediaNavVC.didFinishPickingPhotosWithInfosHandle(pickModels,_isSelectOriginalPhoto,infoArr);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Notification
|
||||
- (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti {
|
||||
_offsetItemCount = self.collectionView.contentOffset.y / (_layout.itemSize.height + _layout.minimumLineSpacing);
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource && Delegate
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.albumList.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIAlbumCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TUIAlbumCell"];
|
||||
TUIMultimediaNavController *imagePickerVc = (TUIMultimediaNavController *)self.navigationController;
|
||||
|
||||
cell.selectedCountButton.backgroundColor = imagePickerVc.iconThemeColor;
|
||||
if (indexPath.row < self.albumList.count) {
|
||||
[cell fillWithData:self.albumList[indexPath.row]];
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (indexPath.row < self.albumList.count) {
|
||||
self.albumModel = self.albumList[indexPath.row];
|
||||
[self.collectionView setBucket:self.albumModel];
|
||||
_shouldScrollToBottom = YES;
|
||||
[self refreshAlbumChangeView];
|
||||
[self hideAllAlbums];
|
||||
[self fetchAssets];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - AlbumClickListener
|
||||
- (void)onClick:(NSInteger)index model:(nonnull TUIAssetModel *)model{
|
||||
TUIPhotoPreviewController *photoPreviewVc = [[TUIPhotoPreviewController alloc] init];
|
||||
photoPreviewVc.currentShowIndex = index;
|
||||
photoPreviewVc.assetModels = self.assetModels;
|
||||
@weakify(self);
|
||||
photoPreviewVc.addAssetBlock = ^(TUIAssetModel *model) {
|
||||
model.isSelected = YES;
|
||||
[self.collectionView setSelected:model selected:YES];
|
||||
};
|
||||
photoPreviewVc.refreshAssetBlock = ^(TUIAssetModel *model) {
|
||||
[self.collectionView freshPhoto:model];
|
||||
};
|
||||
photoPreviewVc.delAssetBlock = ^(TUIAssetModel *model) {
|
||||
NSArray *selectedModels = [NSArray arrayWithArray:[self.collectionView getSelectedPhotoList]];
|
||||
for (TUIAssetModel *modelItem in selectedModels) {
|
||||
if ([model.asset.localIdentifier isEqualToString:modelItem.asset.localIdentifier]) {
|
||||
model.isSelected = NO;
|
||||
[self.collectionView setSelected:modelItem selected:NO];
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
[self pushPhotoPrevireViewController:photoPreviewVc];
|
||||
}
|
||||
- (void)onSelectChanged:(TUIAssetModel *)bean {
|
||||
[self checkSelectedModels];
|
||||
[self refreshBottomToolBarStatus];
|
||||
}
|
||||
|
||||
#pragma mark - Private Method
|
||||
|
||||
- (NSInteger)getAllCellCount {
|
||||
NSInteger count = self.assetModels.count;
|
||||
if (_showTakePhotoBtn) {
|
||||
count += 1;
|
||||
}
|
||||
if (_authorizationLimited) {
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
- (NSInteger)getTakePhotoCellIndex {
|
||||
if (!_showTakePhotoBtn) {
|
||||
return -1;
|
||||
}
|
||||
return [self getAllCellCount] - 1;
|
||||
}
|
||||
|
||||
- (NSInteger)getAddMorePhotoCellIndex {
|
||||
if (!_authorizationLimited) {
|
||||
return -1;
|
||||
}
|
||||
if (_showTakePhotoBtn) {
|
||||
return [self getAllCellCount] - 2;
|
||||
}
|
||||
return [self getAllCellCount] - 1;
|
||||
}
|
||||
|
||||
- (void)openSettingsApplication {
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
||||
}
|
||||
|
||||
- (void)addMorePhoto {
|
||||
if (@available(iOS 14, *)) {
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshBottomToolBarStatus {
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
|
||||
_previewButton.enabled = [self.collectionView getSelectedPhotoList].count > 0;
|
||||
_doneButton.enabled = [self.collectionView getSelectedPhotoList].count > 0;
|
||||
|
||||
_numberImageView.hidden = [self.collectionView getSelectedPhotoList].count <= 0;
|
||||
_numberLabel.hidden = [self.collectionView getSelectedPhotoList].count <= 0;
|
||||
_numberLabel.text = [NSString stringWithFormat:@"%zd",[self.collectionView getSelectedPhotoList].count];
|
||||
|
||||
_originalPhotoButton.selected = (_isSelectOriginalPhoto && _originalPhotoButton.enabled);
|
||||
_originalPhotoLabel.hidden = (!_originalPhotoButton.isSelected);
|
||||
if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes];
|
||||
}
|
||||
|
||||
- (void)pushPhotoPrevireViewController:(TUIPhotoPreviewController *)photoPreviewVc {
|
||||
[self pushPhotoPrevireViewController:photoPreviewVc needCheckSelectedModels:NO];
|
||||
}
|
||||
|
||||
- (void)pushPhotoPrevireViewController:(TUIPhotoPreviewController *)photoPreviewVc needCheckSelectedModels:(BOOL)needCheckSelectedModels {
|
||||
@weakify(self)
|
||||
photoPreviewVc.assetModels = self.assetModels;
|
||||
photoPreviewVc.selectedModels = [NSMutableArray arrayWithArray:[self.collectionView getSelectedPhotoList]];
|
||||
photoPreviewVc.isSelectOriginalPhoto = _isSelectOriginalPhoto;
|
||||
[photoPreviewVc setBackButtonClickBlock:^(BOOL isSelectOriginalPhoto) {
|
||||
@strongify(self)
|
||||
self.isSelectOriginalPhoto = isSelectOriginalPhoto;
|
||||
if (needCheckSelectedModels) {
|
||||
[self checkSelectedModels];
|
||||
}
|
||||
[self.collectionView reloadData];
|
||||
[self refreshBottomToolBarStatus];
|
||||
}];
|
||||
[photoPreviewVc setDoneButtonClickBlock:^(BOOL isSelectOriginalPhoto) {
|
||||
@strongify(self)
|
||||
self.isSelectOriginalPhoto = isSelectOriginalPhoto;
|
||||
[self onDoneButtonClick];
|
||||
}];
|
||||
[photoPreviewVc setDoneButtonClickBlockCropMode:^(UIImage *cropedImage, id asset) {
|
||||
@strongify(self)
|
||||
TUIAssetPickModel *model = [[TUIAssetPickModel alloc] init];
|
||||
model.image = cropedImage;
|
||||
model.asset = asset;
|
||||
[self didGetAllPhotos:@[model]];
|
||||
}];
|
||||
[self.navigationController pushViewController:photoPreviewVc animated:YES];
|
||||
}
|
||||
|
||||
- (void)getSelectedPhotoBytes {
|
||||
NSArray *selectedModels = [self.collectionView getSelectedPhotoList];
|
||||
if (0 == selectedModels.count) {
|
||||
self->_originalPhotoLabel.text = @"";
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@weakify(self)
|
||||
[[TUIImageManager defaultManager] getPhotosTotalBytes:selectedModels completion:^(NSString *totalBytes) {
|
||||
@strongify(self)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->_originalPhotoLabel.text = [NSString stringWithFormat:@"%@ %@",[NSBundle tui_localizedStringForKey:@"TUIAlbumSelectorTotal"],totalBytes];
|
||||
[self->_originalPhotoLabel sizeToFit];
|
||||
});
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)prepareScrollCollectionViewToBottom {
|
||||
if (_shouldScrollToBottom && self.assetModels.count > 0) {
|
||||
NSInteger item = [self getAllCellCount] - 1;
|
||||
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:NO];
|
||||
self->_shouldScrollToBottom = NO;
|
||||
self.collectionView.hidden = NO;
|
||||
} else {
|
||||
self.collectionView.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkSelectedModels {
|
||||
NSArray *selectedModels = [self.collectionView getSelectedPhotoList];
|
||||
NSMutableSet *selectedAssets = [NSMutableSet setWithCapacity:selectedModels.count];
|
||||
for (TUIAssetModel *model in selectedModels) {
|
||||
[selectedAssets addObject:model.asset];
|
||||
}
|
||||
for (TUIAssetModel *model in self.assetModels) {
|
||||
model.isSelected = NO;
|
||||
if ([selectedAssets containsObject:model.asset]) {
|
||||
model.isSelected = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIImagePickerControllerDelegate
|
||||
|
||||
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
|
||||
if ([type isEqualToString:@"public.image"]) {
|
||||
TUIMultimediaNavController *imagePickerVc = (TUIMultimediaNavController *)self.navigationController;
|
||||
[imagePickerVc showProgressHUD];
|
||||
UIImage *photo = [info objectForKey:UIImagePickerControllerOriginalImage];
|
||||
NSDictionary *meta = [info objectForKey:UIImagePickerControllerMediaMetadata];
|
||||
if (photo) {
|
||||
self.isSavingMedia = YES;
|
||||
[[TUIImageManager defaultManager] savePhotoWithImage:photo meta:meta location:self.location completion:^(PHAsset *asset, NSError *error){
|
||||
self.isSavingMedia = NO;
|
||||
if (!error && asset) {
|
||||
[self addPHAsset:asset];
|
||||
} else {
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
[multiMediaNavVC hideProgressHUD];
|
||||
}
|
||||
}];
|
||||
self.location = nil;
|
||||
}
|
||||
} else if ([type isEqualToString:@"public.movie"]) {
|
||||
TUIMultimediaNavController *imagePickerVc = (TUIMultimediaNavController *)self.navigationController;
|
||||
[imagePickerVc showProgressHUD];
|
||||
NSURL *videoUrl = [info objectForKey:UIImagePickerControllerMediaURL];
|
||||
if (videoUrl) {
|
||||
self.isSavingMedia = YES;
|
||||
[[TUIImageManager defaultManager] saveVideoWithUrl:videoUrl location:self.location completion:^(PHAsset *asset, NSError *error) {
|
||||
self.isSavingMedia = NO;
|
||||
if (!error && asset) {
|
||||
[self addPHAsset:asset];
|
||||
} else {
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
[multiMediaNavVC hideProgressHUD];
|
||||
}
|
||||
}];
|
||||
self.location = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addPHAsset:(PHAsset *)asset {
|
||||
TUIAssetModel *assetModel = [[TUIImageManager defaultManager] createModelWithAsset:asset];
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
[multiMediaNavVC hideProgressHUD];
|
||||
[self.assetModels addObject:assetModel];
|
||||
|
||||
if (multiMediaNavVC.maxImagesCount < 1) {
|
||||
[self.collectionView setSelected:assetModel selected:YES];
|
||||
[self onDoneButtonClick];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self.collectionView getSelectedPhotoList].count < multiMediaNavVC.maxImagesCount) {
|
||||
assetModel.isSelected = YES;
|
||||
[self.collectionView setSelected:assetModel selected:YES];
|
||||
[self refreshBottomToolBarStatus];
|
||||
}
|
||||
self.collectionView.hidden = YES;
|
||||
[self.collectionView reloadData];
|
||||
|
||||
_shouldScrollToBottom = YES;
|
||||
[self prepareScrollCollectionViewToBottom];
|
||||
}
|
||||
|
||||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - PHPhotoLibraryChangeObserver
|
||||
- (void)photoLibraryDidChange:(PHChange *)changeInstance {
|
||||
if (self.isSavingMedia || self.isFetchingMedia) {
|
||||
return;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.isShowingAlbums) {
|
||||
[self refreshAlbumTableView];
|
||||
}
|
||||
|
||||
PHFetchResultChangeDetails *changeDetail = [changeInstance changeDetailsForFetchResult:self.albumModel.result];
|
||||
if (changeDetail == nil) return;
|
||||
if ([[TUIImageManager defaultManager] isPHAuthorizationStatusLimited]) {
|
||||
NSArray *changedObjects = [changeDetail changedObjects];
|
||||
changeDetail = [PHFetchResultChangeDetails changeDetailsFromFetchResult:self.albumModel.result toFetchResult:changeDetail.fetchResultAfterChanges changedObjects:changedObjects];
|
||||
if (changeDetail && changeDetail.removedObjects.count) {
|
||||
[self handleRemovedAssets:changeDetail.removedObjects];
|
||||
}
|
||||
}
|
||||
|
||||
if (changeDetail.hasIncrementalChanges == NO) {
|
||||
[self.albumModel refreshFetchResult];
|
||||
[self fetchAssets];
|
||||
} else {
|
||||
NSInteger insertedCount = changeDetail.insertedObjects.count;
|
||||
NSInteger removedCount = changeDetail.removedObjects.count;
|
||||
NSInteger changedCount = changeDetail.changedObjects.count;
|
||||
if (insertedCount > 0 || removedCount > 0 || changedCount > 0) {
|
||||
self.albumModel.result = changeDetail.fetchResultAfterChanges;
|
||||
self.albumModel.photoCount = changeDetail.fetchResultAfterChanges.count;
|
||||
[self fetchAssets];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)handleRemovedAssets:(NSArray<PHAsset *> *)removedObjects {
|
||||
TUIMultimediaNavController *multiMediaNavVC = (TUIMultimediaNavController *)self.navigationController;
|
||||
for (PHAsset *asset in removedObjects) {
|
||||
Boolean isSelected = [multiMediaNavVC.selectedAssetIds containsObject:asset.localIdentifier];
|
||||
if (!isSelected) continue;
|
||||
NSArray *selectedModels = [NSArray arrayWithArray:[self.collectionView getSelectedPhotoList]];
|
||||
for (TUIAssetModel *modelItem in selectedModels) {
|
||||
if ([asset.localIdentifier isEqualToString:modelItem.asset.localIdentifier]) {
|
||||
[self.collectionView setSelected:modelItem selected:NO];
|
||||
}
|
||||
}
|
||||
[self refreshBottomToolBarStatus];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@end
|
||||
|
||||
74
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewCell.h
Normal file
74
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewCell.h
Normal file
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// TUIPhotoPreviewCell.h
|
||||
// TUIPhotoPreviewCell
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
#import <TUIMultimediaCore/TUIProgressView.h>
|
||||
|
||||
@interface TUIAssetPreviewCell : UICollectionViewCell
|
||||
@property (nonatomic, strong) TUIAssetModel *model;
|
||||
@property (nonatomic, copy) void (^singleTapGestureBlock)(void);
|
||||
- (void)configSubviews;
|
||||
- (void)photoPreviewCollectionViewDidScroll;
|
||||
@end
|
||||
|
||||
|
||||
@class TUIPhotoPreviewView;
|
||||
@interface TUIPhotoPreviewCell : TUIAssetPreviewCell
|
||||
|
||||
@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress);
|
||||
|
||||
@property (nonatomic, strong) TUIPhotoPreviewView *previewView;
|
||||
|
||||
@property (nonatomic, assign) BOOL allowCrop;
|
||||
@property (nonatomic, assign) CGRect cropRect;
|
||||
@property (nonatomic, assign) BOOL scaleAspectFillCrop;
|
||||
|
||||
- (void)recoverSubviews;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUIPhotoPreviewView : UIView
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||
@property (nonatomic, strong) UIView *imageContainerView;
|
||||
@property (nonatomic, strong) TUIProgressView *progressView;
|
||||
@property (nonatomic, strong) UIImageView *iCloudErrorIcon;
|
||||
@property (nonatomic, strong) UILabel *iCloudErrorLabel;
|
||||
@property (nonatomic, copy) void (^iCloudSyncFailedHandle)(id asset, BOOL isSyncFailed);
|
||||
|
||||
@property (nonatomic, assign) CGRect cropRect;
|
||||
@property (nonatomic, assign) BOOL scaleAspectFillCrop;
|
||||
@property (nonatomic, strong) TUIAssetModel *model;
|
||||
@property (nonatomic, strong) id asset;
|
||||
@property (nonatomic, copy) void (^singleTapGestureBlock)(void);
|
||||
@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress);
|
||||
|
||||
@property (nonatomic, assign) int32_t imageRequestID;
|
||||
|
||||
- (void)recoverSubviews;
|
||||
@end
|
||||
|
||||
|
||||
@class AVPlayer, AVPlayerLayer;
|
||||
@interface TUIVideoPreviewCell : TUIAssetPreviewCell
|
||||
@property (strong, nonatomic) AVPlayer *player;
|
||||
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
|
||||
@property (strong, nonatomic) UIButton *playButton;
|
||||
@property (strong, nonatomic) UIImage *cover;
|
||||
@property (nonatomic, strong) NSURL *videoURL;
|
||||
@property (nonatomic, strong) UIImageView *iCloudErrorIcon;
|
||||
@property (nonatomic, strong) UILabel *iCloudErrorLabel;
|
||||
@property (nonatomic, copy) void (^iCloudSyncFailedHandle)(id asset, BOOL isSyncFailed);
|
||||
- (void)pausePlayerAndShowNaviBar;
|
||||
@end
|
||||
|
||||
|
||||
@interface TUIGifPreviewCell : TUIAssetPreviewCell
|
||||
@property (strong, nonatomic) TUIPhotoPreviewView *previewView;
|
||||
@end
|
||||
562
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewCell.m
Normal file
562
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewCell.m
Normal file
@@ -0,0 +1,562 @@
|
||||
//
|
||||
// TUIPhotoPreviewCell.m
|
||||
// TUIPhotoPreviewCell
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import "TUIPhotoPreviewCell.h"
|
||||
#import "TUIMultimediaNavController.h"
|
||||
#import <MediaPlayer/MediaPlayer.h>
|
||||
#import <TUICore/UIView+TUILayout.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
#import <TUIMultimediaCore/TUIProgressView.h>
|
||||
|
||||
@implementation TUIAssetPreviewCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.backgroundColor = [UIColor blackColor];
|
||||
[self configSubviews];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(photoPreviewCollectionViewDidScroll) name:@"photoPreviewCollectionViewDidScroll" object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configSubviews {
|
||||
// Subclass override
|
||||
}
|
||||
|
||||
#pragma mark - Notification
|
||||
|
||||
- (void)photoPreviewCollectionViewDidScroll {
|
||||
// Subclass override
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TUIPhotoPreviewCell
|
||||
|
||||
- (void)configSubviews {
|
||||
self.previewView = [[TUIPhotoPreviewView alloc] initWithFrame:CGRectZero];
|
||||
@weakify(self)
|
||||
[self.previewView setSingleTapGestureBlock:^{
|
||||
@strongify(self)
|
||||
if (self.singleTapGestureBlock) {
|
||||
self.singleTapGestureBlock();
|
||||
}
|
||||
}];
|
||||
[self.previewView setImageProgressUpdateBlock:^(double progress) {
|
||||
@strongify(self)
|
||||
if (self.imageProgressUpdateBlock) {
|
||||
self.imageProgressUpdateBlock(progress);
|
||||
}
|
||||
}];
|
||||
[self.contentView addSubview:self.previewView];
|
||||
}
|
||||
|
||||
- (void)setModel:(TUIAssetModel *)model {
|
||||
[super setModel:model];
|
||||
self.previewView.model = model;
|
||||
}
|
||||
|
||||
- (void)recoverSubviews {
|
||||
[self.previewView recoverSubviews];
|
||||
}
|
||||
|
||||
- (void)setScaleAspectFillCrop:(BOOL)scaleAspectFillCrop {
|
||||
_scaleAspectFillCrop = scaleAspectFillCrop;
|
||||
self.previewView.scaleAspectFillCrop = scaleAspectFillCrop;
|
||||
}
|
||||
|
||||
- (void)setCropRect:(CGRect)cropRect {
|
||||
_cropRect = cropRect;
|
||||
self.previewView.cropRect = cropRect;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
self.previewView.frame = self.bounds;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface TUIPhotoPreviewView ()<UIScrollViewDelegate>
|
||||
@property (assign, nonatomic) BOOL isRequestingGIF;
|
||||
@end
|
||||
|
||||
@implementation TUIPhotoPreviewView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self.scrollView = [[UIScrollView alloc] init];
|
||||
self.scrollView.bouncesZoom = YES;
|
||||
self.scrollView.maximumZoomScale = 4;
|
||||
self.scrollView.minimumZoomScale = 1.0;
|
||||
self.scrollView.multipleTouchEnabled = YES;
|
||||
self.scrollView.delegate = self;
|
||||
self.scrollView.scrollsToTop = NO;
|
||||
self.scrollView.showsHorizontalScrollIndicator = NO;
|
||||
self.scrollView.showsVerticalScrollIndicator = YES;
|
||||
self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.scrollView.delaysContentTouches = NO;
|
||||
self.scrollView.canCancelContentTouches = YES;
|
||||
self.scrollView.alwaysBounceVertical = NO;
|
||||
if (@available(iOS 11, *)) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[self addSubview:self.scrollView];
|
||||
|
||||
self.imageContainerView = [[UIView alloc] init];
|
||||
self.imageContainerView.clipsToBounds = YES;
|
||||
self.imageContainerView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
[self.scrollView addSubview:self.imageContainerView];
|
||||
|
||||
self.imageView = [[UIImageView alloc] init];
|
||||
self.imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500];
|
||||
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
self.imageView.clipsToBounds = YES;
|
||||
[self.imageContainerView addSubview:self.imageView];
|
||||
|
||||
self.iCloudErrorIcon = [[UIImageView alloc] init];
|
||||
self.iCloudErrorIcon.image = [UIImage tui_imageNamedFromMyBundle:@"iCloudError"];
|
||||
self.iCloudErrorIcon.hidden = YES;
|
||||
[self addSubview:self.iCloudErrorIcon];
|
||||
|
||||
self.iCloudErrorLabel = [[UILabel alloc] init];
|
||||
self.iCloudErrorLabel.font = [UIFont systemFontOfSize:10];
|
||||
self.iCloudErrorLabel.textColor = [UIColor whiteColor];
|
||||
self.iCloudErrorLabel.text = [NSBundle tui_localizedStringForKey:@"iCloud sync failed"];
|
||||
self.iCloudErrorLabel.hidden = YES;
|
||||
[self addSubview:self.iCloudErrorLabel];
|
||||
|
||||
UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
|
||||
[self addGestureRecognizer:tap1];
|
||||
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
|
||||
tap2.numberOfTapsRequired = 2;
|
||||
[tap1 requireGestureRecognizerToFail:tap2];
|
||||
[self addGestureRecognizer:tap2];
|
||||
|
||||
self.progressView = [[TUIProgressView alloc] init];
|
||||
self.progressView.hidden = YES;
|
||||
[self addSubview:self.progressView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setModel:(TUIAssetModel *)model {
|
||||
_model = model;
|
||||
self.isRequestingGIF = NO;
|
||||
[self.scrollView setZoomScale:1.0 animated:NO];
|
||||
if (TUIAssetMediaTypePhotoGif == model.type) {
|
||||
// Show thumbnails first
|
||||
[[TUIImageManager defaultManager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
|
||||
if (photo) {
|
||||
self.imageView.image = photo;
|
||||
}
|
||||
[self resizeSubviews];
|
||||
if (self.isRequestingGIF) {
|
||||
return;
|
||||
}
|
||||
// Show gif
|
||||
self.isRequestingGIF = YES;
|
||||
[[TUIImageManager defaultManager] getOriginalPhotoDataWithAsset:model.asset progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
|
||||
progress = progress > 0.02 ? progress : 0.02;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL iCloudSyncFailed = [TUICommonTools isICloudSyncError:error];
|
||||
self.iCloudErrorLabel.hidden = !iCloudSyncFailed;
|
||||
self.iCloudErrorIcon.hidden = !iCloudSyncFailed;
|
||||
if (self.iCloudSyncFailedHandle) {
|
||||
self.iCloudSyncFailedHandle(model.asset, iCloudSyncFailed);
|
||||
}
|
||||
|
||||
self.progressView.progress = progress;
|
||||
if (progress >= 1) {
|
||||
self.progressView.hidden = YES;
|
||||
} else {
|
||||
self.progressView.hidden = NO;
|
||||
}
|
||||
});
|
||||
#ifdef DEBUG
|
||||
NSLog(@"[TUIMultimediaNavController] getOriginalPhotoDataWithAsset:%f error:%@", progress, error);
|
||||
#endif
|
||||
} completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) {
|
||||
if (!isDegraded) {
|
||||
self.isRequestingGIF = NO;
|
||||
self.progressView.hidden = YES;
|
||||
self.imageView.image = [UIImage tui_animatedGIFWithData:data];
|
||||
[self resizeSubviews];
|
||||
}
|
||||
}];
|
||||
} progressHandler:nil networkAccessAllowed:NO];
|
||||
} else {
|
||||
if (model.type == TUIAssetMediaTypePhoto && model.editImage != nil) {
|
||||
self.imageView.image = model.editImage;
|
||||
[self resizeSubviews];
|
||||
|
||||
self.progressView.hidden = YES;
|
||||
if (self.imageProgressUpdateBlock) {
|
||||
self.imageProgressUpdateBlock(1);
|
||||
}
|
||||
} else {
|
||||
self.asset = model.asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAsset:(PHAsset *)asset {
|
||||
if (_asset && self.imageRequestID) {
|
||||
[[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID];
|
||||
}
|
||||
|
||||
_asset = asset;
|
||||
@weakify(self)
|
||||
self.imageRequestID = [[TUIImageManager defaultManager] getPhotoWithAsset:asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
|
||||
@strongify(self)
|
||||
BOOL iCloudSyncFailed = !photo && [TUICommonTools isICloudSyncError:info[PHImageErrorKey]];
|
||||
self.iCloudErrorLabel.hidden = !iCloudSyncFailed;
|
||||
self.iCloudErrorIcon.hidden = !iCloudSyncFailed;
|
||||
if (self.iCloudSyncFailedHandle) {
|
||||
self.iCloudSyncFailedHandle(asset, iCloudSyncFailed);
|
||||
}
|
||||
if (![asset isEqual:self->_asset]) return;
|
||||
if (photo) {
|
||||
self.imageView.image = photo;
|
||||
}
|
||||
[self resizeSubviews];
|
||||
|
||||
self.progressView.hidden = YES;
|
||||
if (self.imageProgressUpdateBlock) {
|
||||
self.imageProgressUpdateBlock(1);
|
||||
}
|
||||
if (!isDegraded) {
|
||||
self.imageRequestID = 0;
|
||||
}
|
||||
} progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
|
||||
@strongify(self)
|
||||
if (![asset isEqual:self->_asset]) return;
|
||||
self.progressView.hidden = NO;
|
||||
[self bringSubviewToFront:self.progressView];
|
||||
progress = progress > 0.02 ? progress : 0.02;
|
||||
self.progressView.progress = progress;
|
||||
if (self.imageProgressUpdateBlock && progress < 1) {
|
||||
self.imageProgressUpdateBlock(progress);
|
||||
}
|
||||
|
||||
if (progress >= 1) {
|
||||
self.progressView.hidden = YES;
|
||||
self.imageRequestID = 0;
|
||||
}
|
||||
} networkAccessAllowed:YES];
|
||||
|
||||
[self configMaximumZoomScale];
|
||||
}
|
||||
|
||||
- (void)recoverSubviews {
|
||||
[self.scrollView setZoomScale:self.scrollView.minimumZoomScale animated:NO];
|
||||
[self resizeSubviews];
|
||||
}
|
||||
|
||||
- (void)resizeSubviews {
|
||||
self.imageContainerView.mm_origin = CGPointZero;
|
||||
self.imageContainerView.mm_w = self.scrollView.mm_w;
|
||||
|
||||
UIImage *image = self.imageView.image;
|
||||
if (image.size.height / image.size.width > self.mm_h / self.scrollView.mm_w) {
|
||||
CGFloat width = image.size.width / image.size.height * self.scrollView.mm_h;
|
||||
if (width < 1 || isnan(width)) width = self.mm_w;
|
||||
width = floor(width);
|
||||
|
||||
self.imageContainerView.mm_w = width;
|
||||
self.imageContainerView.mm_h = self.mm_h;
|
||||
self.imageContainerView.mm_centerX = self.scrollView.mm_w / 2;
|
||||
} else {
|
||||
CGFloat height = image.size.height / image.size.width * self.scrollView.mm_w;
|
||||
if (height < 1 || isnan(height)) height = self.mm_h;
|
||||
height = floor(height);
|
||||
self.imageContainerView.mm_h = height;
|
||||
self.imageContainerView.mm_centerY = self.mm_h / 2;
|
||||
}
|
||||
if (self.imageContainerView.mm_h > self.mm_h && self.imageContainerView.mm_h - self.mm_h <= 1) {
|
||||
self.imageContainerView.mm_h = self.mm_h;
|
||||
}
|
||||
CGFloat contentSizeH = MAX(self.imageContainerView.mm_h, self.mm_h);
|
||||
self.scrollView.contentSize = CGSizeMake(self.scrollView.mm_w, contentSizeH);
|
||||
[self.scrollView scrollRectToVisible:self.bounds animated:NO];
|
||||
self.scrollView.alwaysBounceVertical = self.imageContainerView.mm_h <= self.mm_h ? NO : YES;
|
||||
self.imageView.frame = self.imageContainerView.bounds;
|
||||
|
||||
[self refreshScrollViewContentSize];
|
||||
}
|
||||
|
||||
- (void)configMaximumZoomScale {
|
||||
self.scrollView.maximumZoomScale = 4.0;
|
||||
|
||||
if ([self.asset isKindOfClass:[PHAsset class]]) {
|
||||
PHAsset *phAsset = (PHAsset *)self.asset;
|
||||
CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight;
|
||||
if (aspectRatio > 1.5) {
|
||||
self.scrollView.maximumZoomScale *= aspectRatio / 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshScrollViewContentSize {
|
||||
// to do
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
self.scrollView.frame = CGRectMake(10, 0, self.mm_w - 20, self.mm_h);
|
||||
static CGFloat progressWH = 40;
|
||||
CGFloat progressX = (self.mm_w - progressWH) / 2;
|
||||
CGFloat progressY = (self.mm_h - progressWH) / 2;
|
||||
self.progressView.frame = CGRectMake(progressX, progressY, progressWH, progressWH);
|
||||
[self recoverSubviews];
|
||||
self.iCloudErrorIcon.frame = CGRectMake(20, [TUICommonTools tui_statusBarHeight] + 44 + 10, 28, 28);
|
||||
self.iCloudErrorLabel.frame = CGRectMake(53, [TUICommonTools tui_statusBarHeight] + 44 + 10, self.mm_w - 63, 28);
|
||||
}
|
||||
|
||||
#pragma mark - UITapGestureRecognizer Event
|
||||
|
||||
- (void)doubleTap:(UITapGestureRecognizer *)tap {
|
||||
if (self.scrollView.zoomScale > self.scrollView.minimumZoomScale) {
|
||||
self.scrollView.contentInset = UIEdgeInsetsZero;
|
||||
[self.scrollView setZoomScale:self.scrollView.minimumZoomScale animated:YES];
|
||||
} else {
|
||||
CGPoint touchPoint = [tap locationInView:self.imageView];
|
||||
CGFloat newZoomScale = MIN(self.scrollView.maximumZoomScale, 2.5);
|
||||
CGFloat xsize = self.frame.size.width / newZoomScale;
|
||||
CGFloat ysize = self.frame.size.height / newZoomScale;
|
||||
[self.scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)singleTap:(UITapGestureRecognizer *)tap {
|
||||
if (self.singleTapGestureBlock) {
|
||||
self.singleTapGestureBlock();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
|
||||
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
|
||||
return self.imageContainerView;
|
||||
}
|
||||
|
||||
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
|
||||
scrollView.contentInset = UIEdgeInsetsZero;
|
||||
}
|
||||
|
||||
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
|
||||
[self refreshImageContainerViewCenter];
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
|
||||
[self refreshScrollViewContentSize];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)refreshImageContainerViewCenter {
|
||||
CGFloat offsetX = (self.scrollView.mm_w > self.scrollView.contentSize.width) ? ((self.scrollView.mm_w - self.scrollView.contentSize.width) * 0.5) : 0.0;
|
||||
CGFloat offsetY = (self.scrollView.mm_h > self.scrollView.contentSize.height) ? ((self.scrollView.mm_h - self.scrollView.contentSize.height) * 0.5) : 0.0;
|
||||
self.imageContainerView.center = CGPointMake(self.scrollView.contentSize.width * 0.5 + offsetX, self.scrollView.contentSize.height * 0.5 + offsetY);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TUIVideoPreviewCell
|
||||
|
||||
- (void)configSubviews {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
self.iCloudErrorIcon = [[UIImageView alloc] init];
|
||||
self.iCloudErrorIcon.image = [UIImage tui_imageNamedFromMyBundle:@"iCloudError"];
|
||||
self.iCloudErrorIcon.hidden = YES;
|
||||
self.iCloudErrorLabel = [[UILabel alloc] init];
|
||||
self.iCloudErrorLabel.font = [UIFont systemFontOfSize:10];
|
||||
self.iCloudErrorLabel.textColor = [UIColor whiteColor];
|
||||
self.iCloudErrorLabel.text = [NSBundle tui_localizedStringForKey:@"iCloud sync failed"];
|
||||
self.iCloudErrorLabel.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)configPlayButton {
|
||||
if (self.playButton) {
|
||||
[self.playButton removeFromSuperview];
|
||||
}
|
||||
self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[self.playButton setImage:[UIImage tui_imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
|
||||
[self.playButton setImage:[UIImage tui_imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted];
|
||||
[self.playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.playButton.frame = CGRectMake(0, 64, self.mm_w, self.mm_h - 64 - 44);
|
||||
[self.contentView addSubview:self.playButton];
|
||||
[self.contentView addSubview:self.iCloudErrorIcon];
|
||||
[self.contentView addSubview:self.iCloudErrorLabel];
|
||||
}
|
||||
|
||||
- (void)setModel:(TUIAssetModel *)model {
|
||||
[super setModel:model];
|
||||
[self configMoviePlayer];
|
||||
}
|
||||
|
||||
- (void)setVideoURL:(NSURL *)videoURL {
|
||||
_videoURL = videoURL;
|
||||
[self configMoviePlayer];
|
||||
}
|
||||
|
||||
- (void)configMoviePlayer {
|
||||
if (self.player) {
|
||||
[self.playerLayer removeFromSuperlayer];
|
||||
self.playerLayer = nil;
|
||||
[self.player pause];
|
||||
self.player = nil;
|
||||
}
|
||||
//有编辑时,优先使用编辑的url 和 Image
|
||||
if (self.model.editImage && self.model.type == TUIAssetMediaTypePhoto) {
|
||||
self.cover = self.model.editImage;
|
||||
} else if (self.model.editurl) {
|
||||
if (self.model.editImage) {
|
||||
self.cover = self.model.editImage;
|
||||
}
|
||||
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:self.model.editurl];
|
||||
[self configPlayerWithItem:playerItem];
|
||||
} else {
|
||||
if (self.model && self.model.asset) {
|
||||
[[TUIImageManager defaultManager] getPhotoWithAsset:self.model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
|
||||
BOOL iCloudSyncFailed = !photo && [TUICommonTools isICloudSyncError:info[PHImageErrorKey]];
|
||||
self.iCloudErrorLabel.hidden = !iCloudSyncFailed;
|
||||
self.iCloudErrorIcon.hidden = !iCloudSyncFailed;
|
||||
if (self.iCloudSyncFailedHandle) {
|
||||
self.iCloudSyncFailedHandle(self.model.asset, iCloudSyncFailed);
|
||||
}
|
||||
if (photo) {
|
||||
self.cover = photo;
|
||||
}
|
||||
}];
|
||||
[[TUIImageManager defaultManager] getVideoWithAsset:self.model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL iCloudSyncFailed = !playerItem && [TUICommonTools isICloudSyncError:info[PHImageErrorKey]];
|
||||
self.iCloudErrorLabel.hidden = !iCloudSyncFailed;
|
||||
self.iCloudErrorIcon.hidden = !iCloudSyncFailed;
|
||||
if (self.iCloudSyncFailedHandle) {
|
||||
self.iCloudSyncFailedHandle(self.model.asset, iCloudSyncFailed);
|
||||
}
|
||||
[self configPlayerWithItem:playerItem];
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:self.videoURL];
|
||||
[self configPlayerWithItem:playerItem];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)configPlayerWithItem:(AVPlayerItem *)playerItem {
|
||||
self.player = [AVPlayer playerWithPlayerItem:playerItem];
|
||||
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
|
||||
self.playerLayer.backgroundColor = [UIColor blackColor].CGColor;
|
||||
self.playerLayer.frame = self.bounds;
|
||||
[self.contentView.layer addSublayer:self.playerLayer];
|
||||
[self configPlayButton];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
self.playerLayer.frame = self.bounds;
|
||||
self.playButton.frame = CGRectMake(0, 64, self.mm_w, self.mm_h - 64 - 44);
|
||||
self.iCloudErrorIcon.frame = CGRectMake(20, [TUICommonTools tui_statusBarHeight] + 44 + 10, 28, 28);
|
||||
self.iCloudErrorLabel.frame = CGRectMake(53, [TUICommonTools tui_statusBarHeight] + 44 + 10, self.mm_w - 63, 28);
|
||||
}
|
||||
|
||||
- (void)photoPreviewCollectionViewDidScroll {
|
||||
if (self.player && self.player.rate != 0.0) {
|
||||
[self pausePlayerAndShowNaviBar];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Notification
|
||||
|
||||
- (void)appWillResignActiveNotification {
|
||||
if (self.player && self.player.rate != 0.0) {
|
||||
[self pausePlayerAndShowNaviBar];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Click Event
|
||||
|
||||
- (void)playButtonClick {
|
||||
CMTime currentTime = self.player.currentItem.currentTime;
|
||||
CMTime durationTime = self.player.currentItem.duration;
|
||||
if (self.player.rate == 0.0f) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"TZ_VIDEO_PLAY_NOTIFICATION" object:self.player];
|
||||
if (currentTime.value == durationTime.value) [self.player.currentItem seekToTime:CMTimeMake(0, 1)];
|
||||
[self.player play];
|
||||
[self.playButton setImage:nil forState:UIControlStateNormal];
|
||||
[UIApplication sharedApplication].statusBarHidden = YES;
|
||||
if (self.singleTapGestureBlock) {
|
||||
self.singleTapGestureBlock();
|
||||
}
|
||||
} else {
|
||||
[self pausePlayerAndShowNaviBar];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pausePlayerAndShowNaviBar {
|
||||
[self.player pause];
|
||||
[self.playButton setImage:[UIImage tui_imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal];
|
||||
if (self.singleTapGestureBlock) {
|
||||
self.singleTapGestureBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TUIGifPreviewCell
|
||||
|
||||
- (void)configSubviews {
|
||||
[self configPreviewView];
|
||||
}
|
||||
|
||||
- (void)configPreviewView {
|
||||
_previewView = [[TUIPhotoPreviewView alloc] initWithFrame:CGRectZero];
|
||||
@weakify(self)
|
||||
[_previewView setSingleTapGestureBlock:^{
|
||||
@strongify(self)
|
||||
[self signleTapAction];
|
||||
}];
|
||||
[self.contentView addSubview:_previewView];
|
||||
}
|
||||
|
||||
- (void)setModel:(TUIAssetModel *)model {
|
||||
[super setModel:model];
|
||||
_previewView.model = self.model;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
_previewView.frame = self.bounds;
|
||||
}
|
||||
|
||||
#pragma mark - Click Event
|
||||
|
||||
- (void)signleTapAction {
|
||||
if (self.singleTapGestureBlock) {
|
||||
self.singleTapGestureBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
28
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewController.h
Normal file
28
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewController.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// TUIPhotoPreviewController.h
|
||||
// TUIPhotoPreviewController
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
@interface TUIPhotoPreviewController : UIViewController
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *assetModels; ///< All photo models
|
||||
@property (nonatomic, strong) NSMutableArray *selectedModels; ///< Select photo models
|
||||
@property (nonatomic, strong) NSMutableArray *photos; ///< All photos
|
||||
@property (nonatomic, assign) NSInteger currentShowIndex; ///< Index of the photo user click
|
||||
@property (nonatomic, assign) BOOL isSelectOriginalPhoto; ///< If YES,return original photo
|
||||
@property (nonatomic, assign) BOOL isCropImage;
|
||||
|
||||
/// Return the new selected photos
|
||||
@property (nonatomic, copy) void (^backButtonClickBlock)(BOOL isSelectOriginalPhoto);
|
||||
@property (nonatomic, copy) void (^doneButtonClickBlock)(BOOL isSelectOriginalPhoto);
|
||||
@property (nonatomic, copy) void (^doneButtonClickBlockCropMode)(UIImage *cropedImage,id asset);
|
||||
|
||||
@property (nonatomic, copy) void (^addAssetBlock)(TUIAssetModel * model);
|
||||
@property (nonatomic, copy) void (^delAssetBlock)(TUIAssetModel * model);
|
||||
@property (nonatomic, copy) void (^refreshAssetBlock)(TUIAssetModel * model);
|
||||
|
||||
@end
|
||||
747
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewController.m
Normal file
747
TUIKit/TUIMultimediaPlugin/Pick/TUIPhotoPreviewController.m
Normal file
@@ -0,0 +1,747 @@
|
||||
//
|
||||
// TUIPhotoPreviewController.m
|
||||
// TUIPhotoPreviewController
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import "TUIPhotoPreviewController.h"
|
||||
#import "TUIPhotoPreviewCell.h"
|
||||
#import "TUIMultimediaProcessor.h"
|
||||
#import "TUIMultimediaNavController.h"
|
||||
#import <TUICore/UIView+TUILayout.h>
|
||||
#import <TUICore/UIView+TUIUtil.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUIMultimediaCore/TUISelectedPhotosView.h>
|
||||
#import <TUIMultimediaCore/TUIAssetModel.h>
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
|
||||
@interface TUIPhotoPreviewController ()<UICollectionViewDataSource,UICollectionViewDelegate,UIScrollViewDelegate> {
|
||||
UICollectionView *_collectionView;
|
||||
UICollectionViewFlowLayout *_layout;
|
||||
UIView *_naviBar;
|
||||
UIButton *_backButton;
|
||||
UIButton *_selectButton;
|
||||
UILabel *_indexLabel;
|
||||
|
||||
UIView *_toolBar;
|
||||
UIButton *_doneButton;
|
||||
UIImageView *_numberImageView;
|
||||
UILabel *_numberLabel;
|
||||
TUISelectedPhotosView *_selectedScrollView;
|
||||
|
||||
UIButton *_editPhotoButton;
|
||||
UIButton *_originalPhotoButton;
|
||||
UILabel *_originalPhotoLabel;
|
||||
|
||||
CGFloat _offsetItemCount;
|
||||
|
||||
BOOL _didSetIsSelectOriginalPhoto;
|
||||
}
|
||||
@property (nonatomic, assign) BOOL isHideNaviBar;
|
||||
|
||||
@property (nonatomic, assign) double progress;
|
||||
@property (strong, nonatomic) UIAlertController *alertView;
|
||||
@property (nonatomic, strong) UIView *iCloudErrorView;
|
||||
|
||||
@property (nonatomic, strong) TUIMultimediaNavController *multiMediaNavVC;
|
||||
@end
|
||||
|
||||
@implementation TUIPhotoPreviewController
|
||||
|
||||
- (void)setIsSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto {
|
||||
_isSelectOriginalPhoto = isSelectOriginalPhoto;
|
||||
_didSetIsSelectOriginalPhoto = YES;
|
||||
}
|
||||
|
||||
- (void)setPhotos:(NSMutableArray *)photos {
|
||||
_photos = photos;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self.navigationController setNavigationBarHidden:YES animated:YES];
|
||||
[UIApplication sharedApplication].statusBarHidden = YES;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
if (self.multiMediaNavVC.needShowStatusBar) {
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
[self.navigationController setNavigationBarHidden:NO animated:YES];
|
||||
[TUIImageManager defaultManager].shouldFixOrientation = NO;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.clipsToBounds = YES;
|
||||
[TUIImageManager defaultManager].shouldFixOrientation = YES;
|
||||
|
||||
if (!_didSetIsSelectOriginalPhoto) {
|
||||
_isSelectOriginalPhoto = self.multiMediaNavVC.isSelectOriginalPhoto;
|
||||
}
|
||||
|
||||
[self configCollectionView];
|
||||
[self configCustomNaviBar];
|
||||
[self configSelectedScrollView];
|
||||
[self configBottomToolBar];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
|
||||
|
||||
[_collectionView setContentOffset:CGPointMake((self.view.mm_w + 20) * self.currentShowIndex, 0) animated:NO];
|
||||
[self refreshNaviBarAndBottomBarState];
|
||||
[self updateSelectedScrollViewWithModels:self.selectedModels];
|
||||
[self onScrollToItemAtIndex:self.currentShowIndex];
|
||||
|
||||
}
|
||||
|
||||
- (TUIMultimediaNavController *)multiMediaNavVC {
|
||||
return (TUIMultimediaNavController *)self.navigationController;
|
||||
}
|
||||
|
||||
- (void)configCustomNaviBar {
|
||||
_naviBar = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_naviBar.backgroundColor = RGBA(29, 29, 29, 0.98);
|
||||
|
||||
_backButton = [[UIButton alloc] initWithFrame:CGRectZero];
|
||||
|
||||
[_backButton setImage:[[UIImage tui_imageNamedFromMyBundle:@"navi_back"]
|
||||
rtl_imageFlippedForRightToLeftLayoutDirection]
|
||||
forState:UIControlStateNormal];
|
||||
[_backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[_backButton addTarget:self action:@selector(onBackButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
_selectButton = [[UIButton alloc] initWithFrame:CGRectZero];
|
||||
[_selectButton setImage:self.multiMediaNavVC.photoDefImage forState:UIControlStateNormal];
|
||||
[_selectButton setImage:self.multiMediaNavVC.photoSelImage forState:UIControlStateSelected];
|
||||
_selectButton.imageView.clipsToBounds = YES;
|
||||
_selectButton.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 0);
|
||||
_selectButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[_selectButton addTarget:self action:@selector(onSelectButtonClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
_selectButton.hidden = YES;
|
||||
|
||||
_indexLabel = [[UILabel alloc] init];
|
||||
_indexLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_indexLabel.font = [UIFont systemFontOfSize:14];
|
||||
_indexLabel.textColor = [UIColor whiteColor];
|
||||
_indexLabel.textAlignment = NSTextAlignmentCenter;
|
||||
|
||||
[_naviBar addSubview:_selectButton];
|
||||
[_naviBar addSubview:_indexLabel];
|
||||
[_naviBar addSubview:_backButton];
|
||||
[self.view addSubview:_naviBar];
|
||||
}
|
||||
|
||||
- (void)configSelectedScrollView {
|
||||
_selectedScrollView = [[TUISelectedPhotosView alloc] init];
|
||||
[_selectedScrollView setPhotoList:self.selectedModels];
|
||||
[_selectedScrollView setClickListener:self];
|
||||
_selectedScrollView.backgroundColor = RGBA(29, 29, 29, 0.98);
|
||||
_selectedScrollView.bouncesZoom = YES;
|
||||
_selectedScrollView.maximumZoomScale = 4;
|
||||
_selectedScrollView.minimumZoomScale = 1.0;
|
||||
_selectedScrollView.multipleTouchEnabled = YES;
|
||||
// _selectedScrollView.delegate = self;
|
||||
_selectedScrollView.scrollsToTop = NO;
|
||||
_selectedScrollView.showsHorizontalScrollIndicator = NO;
|
||||
_selectedScrollView.showsVerticalScrollIndicator = YES;
|
||||
_selectedScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_selectedScrollView.delaysContentTouches = NO;
|
||||
_selectedScrollView.canCancelContentTouches = YES;
|
||||
_selectedScrollView.alwaysBounceVertical = NO;
|
||||
_selectedScrollView.pagingEnabled = NO;
|
||||
_selectedScrollView.bounces = NO;
|
||||
if (@available(iOS 11, *)) {
|
||||
_selectedScrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[self.view addSubview:_selectedScrollView];
|
||||
}
|
||||
|
||||
- (void)configBottomToolBar {
|
||||
_toolBar = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_toolBar.backgroundColor = RGBA(29, 29, 29, 0.98);
|
||||
|
||||
_editPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_editPhotoButton.backgroundColor = [UIColor clearColor];
|
||||
[_editPhotoButton addTarget:self action:@selector(onEditPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
_editPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_editPhotoButton setTitle:self.multiMediaNavVC.editImageBtnTitleStr forState:UIControlStateNormal];
|
||||
[_editPhotoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
|
||||
_originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, [TUICommonTools tui_isRightToLeftLayout] ? 10 : -10, 0, 0);
|
||||
_originalPhotoButton.backgroundColor = [UIColor clearColor];
|
||||
[_originalPhotoButton addTarget:self action:@selector(onOriginalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
_originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_originalPhotoButton setTitle:self.multiMediaNavVC.fullImageBtnTitleStr forState:UIControlStateNormal];
|
||||
[_originalPhotoButton setTitle:self.multiMediaNavVC.fullImageBtnTitleStr forState:UIControlStateSelected];
|
||||
[_originalPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
|
||||
[_originalPhotoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
|
||||
[_originalPhotoButton setImage:self.multiMediaNavVC.photoPreviewOriginDefImage forState:UIControlStateNormal];
|
||||
[_originalPhotoButton setImage:self.multiMediaNavVC.photoOriginSelImage forState:UIControlStateSelected];
|
||||
|
||||
_originalPhotoLabel = [[UILabel alloc] init];
|
||||
_originalPhotoLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_originalPhotoLabel.font = [UIFont systemFontOfSize:12];
|
||||
_originalPhotoLabel.textColor = [UIColor grayColor];
|
||||
_originalPhotoLabel.backgroundColor = [UIColor clearColor];
|
||||
if (_isSelectOriginalPhoto) [self showTotalPhotoBytes];
|
||||
|
||||
_doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_doneButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
[_doneButton addTarget:self action:@selector(onDoneButtonClick) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_doneButton setTitle:self.multiMediaNavVC.doneBtnTitleStr forState:UIControlStateNormal];
|
||||
[_doneButton setTitleColor:self.multiMediaNavVC.oKButtonTitleColorNormal forState:UIControlStateNormal];
|
||||
|
||||
_numberImageView = [[UIImageView alloc] initWithImage:self.multiMediaNavVC.photoNumberIconImage];
|
||||
_numberImageView.backgroundColor = [UIColor clearColor];
|
||||
_numberImageView.clipsToBounds = YES;
|
||||
_numberImageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
_numberImageView.hidden = self.selectedModels.count <= 0;
|
||||
|
||||
_numberLabel = [[UILabel alloc] init];
|
||||
_numberLabel.font = [UIFont systemFontOfSize:15];
|
||||
_numberLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_numberLabel.textColor = [UIColor whiteColor];
|
||||
_numberLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_numberLabel.text = [NSString stringWithFormat:@"%zd",self.selectedModels.count];
|
||||
_numberLabel.hidden = self.selectedModels.count <= 0;
|
||||
_numberLabel.backgroundColor = [UIColor clearColor];
|
||||
_numberLabel.userInteractionEnabled = YES;
|
||||
|
||||
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDoneButtonClick)];
|
||||
[_numberLabel addGestureRecognizer:tapGesture];
|
||||
|
||||
[_toolBar addSubview:_doneButton];
|
||||
[_toolBar addSubview:_editPhotoButton];
|
||||
[_toolBar addSubview:_originalPhotoButton];
|
||||
[_toolBar addSubview:_originalPhotoLabel];
|
||||
[_toolBar addSubview:_numberImageView];
|
||||
[_toolBar addSubview:_numberLabel];
|
||||
[self.view addSubview:_toolBar];
|
||||
}
|
||||
|
||||
- (void)configCollectionView {
|
||||
_layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
_layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout];
|
||||
_collectionView.backgroundColor = [UIColor blackColor];
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.pagingEnabled = YES;
|
||||
_collectionView.scrollsToTop = NO;
|
||||
_collectionView.showsHorizontalScrollIndicator = NO;
|
||||
_collectionView.contentOffset = CGPointMake(0, 0);
|
||||
_collectionView.contentSize = CGSizeMake(self.assetModels.count * (self.view.mm_w + 20), 0);
|
||||
if (@available(iOS 11, *)) {
|
||||
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
[self.view addSubview:_collectionView];
|
||||
[_collectionView registerClass:[TUIPhotoPreviewCell class] forCellWithReuseIdentifier:@"TUIPhotoPreviewCell"];
|
||||
[_collectionView registerClass:[TUIPhotoPreviewCell class] forCellWithReuseIdentifier:@"TUIPhotoPreviewCellGIF"];
|
||||
[_collectionView registerClass:[TUIVideoPreviewCell class] forCellWithReuseIdentifier:@"TUIVideoPreviewCell"];
|
||||
[_collectionView registerClass:[TUIGifPreviewCell class] forCellWithReuseIdentifier:@"TUIGifPreviewCell"];
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
BOOL isFullScreen = (self.view.mm_h == Screen_Height);
|
||||
CGFloat statusBarHeight = isFullScreen ? [TUICommonTools tui_statusBarHeight] : 0;
|
||||
CGFloat statusBarHeightInterval = isFullScreen ? (statusBarHeight - 20) : 0;
|
||||
CGFloat naviBarHeight = statusBarHeight + self.multiMediaNavVC.navigationBar.mm_h;
|
||||
_naviBar.frame = CGRectMake(0, 0, self.view.mm_w, naviBarHeight);
|
||||
_backButton.frame = CGRectMake(10, 10 + statusBarHeightInterval, 44, 44);
|
||||
_selectButton.frame = CGRectMake(self.view.mm_w - 56, 10 + statusBarHeightInterval, 44, 44);
|
||||
_indexLabel.frame = _selectButton.frame;
|
||||
|
||||
_layout.itemSize = CGSizeMake(self.view.mm_w + 20, self.view.mm_h);
|
||||
_layout.minimumInteritemSpacing = 0;
|
||||
_layout.minimumLineSpacing = 0;
|
||||
_collectionView.frame = CGRectMake(-10, 0, self.view.mm_w + 20, self.view.mm_h);
|
||||
[_collectionView setCollectionViewLayout:_layout];
|
||||
if (_offsetItemCount > 0) {
|
||||
CGFloat offsetX = _offsetItemCount * _layout.itemSize.width;
|
||||
[_collectionView setContentOffset:CGPointMake(offsetX, 0)];
|
||||
}
|
||||
|
||||
CGFloat toolBarHeight = 44 + [TUICommonTools tui_safeAreaInsets].bottom + 2;
|
||||
CGFloat toolBarTop = self.view.mm_h - toolBarHeight;
|
||||
_toolBar.frame = CGRectMake(0, toolBarTop, self.view.mm_w, toolBarHeight);
|
||||
|
||||
_editPhotoButton.frame = CGRectMake(0, 0, 56, 44);
|
||||
CGFloat fullImageWidth = [self.multiMediaNavVC.fullImageBtnTitleStr boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} context:nil].size.width;
|
||||
fullImageWidth += 40;
|
||||
_originalPhotoButton.frame = CGRectMake(_toolBar.mm_w / 2 - fullImageWidth / 2, 0, fullImageWidth, 44);
|
||||
|
||||
[_originalPhotoLabel sizeToFit];
|
||||
_originalPhotoLabel.frame = CGRectMake(_toolBar.mm_w /2 -_originalPhotoLabel.mm_w /2 ,
|
||||
33,
|
||||
_originalPhotoLabel.mm_w,
|
||||
_originalPhotoLabel.mm_h);
|
||||
_originalPhotoLabel.alpha = 0;
|
||||
[_doneButton sizeToFit];
|
||||
_doneButton.frame = CGRectMake(self.view.mm_w - _doneButton.mm_w - 12, 0, MAX(44, _doneButton.mm_w), 44);
|
||||
_numberImageView.frame = CGRectMake(_doneButton.mm_x - 24 - 5, 10, 24, 24);
|
||||
_numberLabel.frame = _numberImageView.frame;
|
||||
|
||||
if (isRTL()) {
|
||||
for (UIView *subview in _naviBar.subviews) {
|
||||
[subview resetFrameToFitRTL];
|
||||
}
|
||||
for (UIView *subview in _toolBar.subviews) {
|
||||
[subview resetFrameToFitRTL];
|
||||
}
|
||||
}
|
||||
|
||||
[self layoutSelectedScrollView];
|
||||
}
|
||||
|
||||
- (void)layoutSelectedScrollView {
|
||||
CGFloat photoViewSize = 65;
|
||||
CGFloat photoViewMargin = 15;
|
||||
CGFloat scrollViewHeight = photoViewSize + 2 * photoViewMargin;
|
||||
_selectedScrollView.frame = CGRectMake(0, _toolBar.mm_y - scrollViewHeight, self.view.mm_w, scrollViewHeight);
|
||||
if (isRTL()) {
|
||||
_selectedScrollView.transform = CGAffineTransformMakeRotation(M_PI);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateSelectedScrollViewWithModels:(NSMutableArray *)selectedModels {
|
||||
if (self.isSelectOriginalPhoto) {
|
||||
[self showTotalPhotoBytes];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onScrollToItemAtIndex:(NSUInteger)index {
|
||||
if (index < self.assetModels.count) {
|
||||
TUIAssetModel *model = self.assetModels[index];
|
||||
[self->_selectedScrollView switchPhoto:model];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)findInAllModels:(TUIAssetModel *)model findIndex:(NSUInteger *)findIndex {
|
||||
for (TUIAssetModel *modelItem in self.assetModels) {
|
||||
if ([modelItem.asset.localIdentifier isEqualToString:model.asset.localIdentifier]) {
|
||||
*findIndex = [self.assetModels indexOfObject:modelItem];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)findInSelectedModels:(TUIAssetModel *)model findIndex:(NSUInteger *)findIndex {
|
||||
for (TUIAssetModel *modelItem in self.selectedModels) {
|
||||
if ([modelItem.asset.localIdentifier isEqualToString:model.asset.localIdentifier]) {
|
||||
*findIndex = [self.selectedModels indexOfObject:modelItem];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Notification
|
||||
- (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti {
|
||||
_offsetItemCount = _collectionView.contentOffset.x / _layout.itemSize.width;
|
||||
}
|
||||
|
||||
#pragma mark - Click Event
|
||||
- (void)onSelectButtonClick:(UIButton *)selectButton {
|
||||
[self selectPhoto:selectButton refreshCount:YES];
|
||||
}
|
||||
|
||||
- (void)selectPhoto:(UIButton *)selectButton refreshCount:(BOOL)refreshCount {
|
||||
TUIAssetModel *model = self.assetModels[self.currentShowIndex];
|
||||
if (!selectButton.isSelected) {
|
||||
// 1. select:check if over the maxImagesCount
|
||||
if (self.selectedModels.count >= self.multiMediaNavVC.maxImagesCount) {
|
||||
NSString *title = [NSString stringWithFormat:[NSBundle tui_localizedStringForKey:@"Select a maximum of %zd photos"], self.multiMediaNavVC.maxImagesCount];
|
||||
[self.multiMediaNavVC showAlertWithTitle:title];
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// 2. if not over the maxImagesCount
|
||||
[self.selectedModels addObject:model];
|
||||
[self->_selectedScrollView addPhoto:model];
|
||||
if (self.addAssetBlock) {
|
||||
self.addAssetBlock(model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSArray *selectedModels = [NSArray arrayWithArray:self.selectedModels];
|
||||
for (TUIAssetModel *modelItem in selectedModels) {
|
||||
if ([modelItem.asset.localIdentifier isEqualToString:model.asset.localIdentifier]) {
|
||||
NSArray *selectedModelsTmp = [NSArray arrayWithArray:self.selectedModels];
|
||||
for (NSInteger i = 0; i < selectedModelsTmp.count; i++) {
|
||||
TUIAssetModel *model = selectedModelsTmp[i];
|
||||
if ([model isEqual:modelItem]) {
|
||||
[self.selectedModels removeObject:model];
|
||||
[self->_selectedScrollView removePhoto:model];
|
||||
if (self.delAssetBlock) {
|
||||
self.delAssetBlock(model);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
model.isSelected = !selectButton.isSelected;
|
||||
if (model.isSelected) {
|
||||
[UIView showOscillatoryAnimationWithLayer:selectButton.imageView.layer type:TUIOscillatoryAnimationToBigger];
|
||||
}
|
||||
[UIView showOscillatoryAnimationWithLayer:_numberImageView.layer type:TUIOscillatoryAnimationToSmaller];
|
||||
|
||||
if (refreshCount) {
|
||||
[self refreshNaviBarAndBottomBarState];
|
||||
}
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[self updateSelectedScrollViewWithModels:self.selectedModels];
|
||||
[self onScrollToItemAtIndex:self.currentShowIndex];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)onBackButtonClick {
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
if (self.backButtonClickBlock) {
|
||||
self.backButtonClickBlock(_isSelectOriginalPhoto);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onDoneButtonClick {
|
||||
// Alert user if pictures are being synced from iCloud
|
||||
if (_progress > 0 && _progress < 1 && (_selectButton.isSelected || !self.selectedModels.count )) {
|
||||
_alertView = [self.multiMediaNavVC showAlertWithTitle:[NSBundle tui_localizedStringForKey:@"Synchronizing photos from iCloud"]];
|
||||
return;
|
||||
}
|
||||
|
||||
// If no photo has been selected, click OK to select the currently previewed photo.
|
||||
if (self.selectedModels.count == 0) {
|
||||
[self selectPhoto:_selectButton refreshCount:NO];
|
||||
}
|
||||
|
||||
if (self.doneButtonClickBlock) {
|
||||
self.doneButtonClickBlock(_isSelectOriginalPhoto);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onEditPhotoButtonClick {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
TUIAssetModel * model = [self getCurrentSelectedModel];
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.type == TUIAssetMediaTypeVideo) {
|
||||
[[TUIImageManager defaultManager] requestVideoURLWithAsset:model.asset success:^(NSURL *videoURL) {
|
||||
void (^editVideoBlock)(void) = ^(void) {
|
||||
NSURL *formatUrl = videoURL;
|
||||
if (model.editurl) {
|
||||
formatUrl = model.editurl;
|
||||
}
|
||||
[[TUIMultimediaProcessor shareInstance] editVideo:weakSelf url:formatUrl complete:^(NSURL * _Nullable uri) {
|
||||
NSLog(@"transcode url is %@",videoURL);
|
||||
if (uri != nil) {
|
||||
[self replaceCurrentSelectedModel:uri];
|
||||
}
|
||||
}];
|
||||
};
|
||||
dispatch_async(dispatch_get_main_queue(), editVideoBlock);
|
||||
NSLog(@"%@",videoURL);
|
||||
} failure:^(NSDictionary *info) {
|
||||
|
||||
}];
|
||||
}
|
||||
else if (model.type == TUIAssetMediaTypePhoto) {
|
||||
if (model.editImage != nil) {
|
||||
[self editPictureOnMainQueue:model.editImage];
|
||||
} else {
|
||||
[[TUIImageManager defaultManager] getOriginalPhotoDataWithAsset:model.asset
|
||||
progressHandler:nil
|
||||
completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) {
|
||||
if (!isDegraded) {
|
||||
UIImage *img = [UIImage imageWithData:data];
|
||||
[self editPictureOnMainQueue:img];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)editPictureOnMainQueue:(UIImage*) image {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
void (^editPictureBlock)(void) = ^(void) {
|
||||
[[TUIMultimediaProcessor shareInstance] editPicture:weakSelf picture:image complete:^(UIImage * _Nullable picture) {
|
||||
if (picture != nil) {
|
||||
[self replaceCurrentSelectedPhotoModel:picture];
|
||||
}
|
||||
}];
|
||||
};
|
||||
dispatch_async(dispatch_get_main_queue(), editPictureBlock);
|
||||
}
|
||||
|
||||
- (void)onProvideVideo:(NSURL *)videoURL
|
||||
snapshot:(NSString *)snapshotUrl
|
||||
duration:(NSInteger)duration {
|
||||
|
||||
V2TIMMessage *message = [[V2TIMManager sharedInstance] createVideoMessage:videoURL.path type:videoURL.path.pathExtension duration:(int)duration snapshotPath:snapshotUrl];
|
||||
NSDictionary *param = @{TUICore_TUIChatService_SendMessageMethod_MsgKey : message};
|
||||
NSString *entranceName = [TIMConfig isClassicEntrance]?
|
||||
TUICore_TUIChatService:TUICore_TUIChatService_Minimalist;
|
||||
[TUICore callService:entranceName
|
||||
method:TUICore_TUIChatService_SendMessageMethod param:param];
|
||||
|
||||
}
|
||||
- (TUIAssetModel *)getCurrentSelectedModel {
|
||||
TUIAssetModel *model = self.assetModels[self.currentShowIndex];
|
||||
return model;
|
||||
}
|
||||
- (void)replaceCurrentSelectedModel:(NSURL *)uri {
|
||||
TUIAssetModel * model = [self getCurrentSelectedModel];
|
||||
if (model.type == TUIAssetMediaTypeVideo) {
|
||||
model.editurl = uri;
|
||||
model.editImage = [[TUIImageManager defaultManager] getImageWithVideoURL:uri];
|
||||
if (self.refreshAssetBlock) {
|
||||
self.refreshAssetBlock(model);
|
||||
}
|
||||
[_collectionView reloadData];
|
||||
[self refreshNaviBarAndBottomBarState];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)replaceCurrentSelectedPhotoModel:(UIImage *)image {
|
||||
TUIAssetModel * model = [self getCurrentSelectedModel];
|
||||
if (model == nil || model.type != TUIAssetMediaTypePhoto) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.editImage = image;
|
||||
if (self.refreshAssetBlock) {
|
||||
self.refreshAssetBlock(model);
|
||||
}
|
||||
[_selectedScrollView freshPhoto : model];
|
||||
[_collectionView reloadData];
|
||||
[self refreshNaviBarAndBottomBarState];
|
||||
}
|
||||
|
||||
- (void)onOriginalPhotoButtonClick {
|
||||
_originalPhotoButton.selected = !_originalPhotoButton.isSelected;
|
||||
_isSelectOriginalPhoto = _originalPhotoButton.selected;
|
||||
_originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;
|
||||
if (_isSelectOriginalPhoto) {
|
||||
[self showTotalPhotoBytes];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didTapPreviewCell {
|
||||
self.isHideNaviBar = !self.isHideNaviBar;
|
||||
_naviBar.hidden = self.isHideNaviBar;
|
||||
_toolBar.hidden = self.isHideNaviBar;
|
||||
_selectedScrollView.hidden = (self.isHideNaviBar || 0 == self.selectedModels.count);
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
if (scrollView == _collectionView) {
|
||||
CGFloat offSetWidth = scrollView.contentOffset.x;
|
||||
offSetWidth = offSetWidth + ((self.view.mm_w + 20) * 0.5);
|
||||
|
||||
NSInteger currentShowIndex = offSetWidth / (self.view.mm_w + 20);
|
||||
if (currentShowIndex < self.assetModels.count && _currentShowIndex != currentShowIndex) {
|
||||
_currentShowIndex = currentShowIndex;
|
||||
[self refreshNaviBarAndBottomBarState];
|
||||
[self onScrollToItemAtIndex:self.currentShowIndex];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"photoPreviewCollectionViewDidScroll" object:nil];
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDataSource && Delegate
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return self.assetModels.count;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIAssetModel *model = self.assetModels[indexPath.item];
|
||||
|
||||
TUIAssetPreviewCell *cell;
|
||||
if (TUIAssetMediaTypeVideo == model.type) {
|
||||
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TUIVideoPreviewCell" forIndexPath:indexPath];
|
||||
TUIVideoPreviewCell *currentCell = (TUIVideoPreviewCell *)cell;
|
||||
@weakify(self)
|
||||
currentCell.iCloudSyncFailedHandle = ^(id asset, BOOL isSyncFailed) {
|
||||
@strongify(self)
|
||||
model.iCloudFailed = isSyncFailed;
|
||||
[self didICloudSyncStatusChanged:model];
|
||||
};
|
||||
} else if (TUIAssetMediaTypePhotoGif == model.type) {
|
||||
cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TUIGifPreviewCell" forIndexPath:indexPath];
|
||||
TUIGifPreviewCell *currentCell = (TUIGifPreviewCell *)cell;
|
||||
@weakify(self)
|
||||
currentCell.previewView.iCloudSyncFailedHandle = ^(id asset, BOOL isSyncFailed) {
|
||||
@strongify(self)
|
||||
model.iCloudFailed = isSyncFailed;
|
||||
[self didICloudSyncStatusChanged:model];
|
||||
};
|
||||
} else {
|
||||
NSString *reuseId = model.type == TUIAssetMediaTypePhotoGif ? @"TUIPhotoPreviewCellGIF" : @"TUIPhotoPreviewCell";
|
||||
cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseId forIndexPath:indexPath];
|
||||
TUIPhotoPreviewCell *photoPreviewCell = (TUIPhotoPreviewCell *)cell;
|
||||
@weakify(self)
|
||||
[photoPreviewCell setImageProgressUpdateBlock:^(double progress) {
|
||||
@strongify(self)
|
||||
self.progress = progress;
|
||||
if (progress >= 1) {
|
||||
if (self.isSelectOriginalPhoto) [self showTotalPhotoBytes];
|
||||
if (self.alertView && [self->_collectionView.visibleCells containsObject:photoPreviewCell]) {
|
||||
[self.alertView dismissViewControllerAnimated:YES completion:^{
|
||||
self.alertView = nil;
|
||||
[self onDoneButtonClick];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}];
|
||||
photoPreviewCell.previewView.iCloudSyncFailedHandle = ^(id asset, BOOL isSyncFailed) {
|
||||
@strongify(self)
|
||||
model.iCloudFailed = isSyncFailed;
|
||||
[self didICloudSyncStatusChanged:model];
|
||||
};
|
||||
}
|
||||
|
||||
cell.model = model;
|
||||
|
||||
@weakify(self)
|
||||
[cell setSingleTapGestureBlock:^{
|
||||
@strongify(self)
|
||||
[self didTapPreviewCell];
|
||||
}];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if ([cell isKindOfClass:[TUIPhotoPreviewCell class]]) {
|
||||
[(TUIPhotoPreviewCell *)cell recoverSubviews];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if ([cell isKindOfClass:[TUIPhotoPreviewCell class]]) {
|
||||
[(TUIPhotoPreviewCell *)cell recoverSubviews];
|
||||
} else if ([cell isKindOfClass:[TUIVideoPreviewCell class]]) {
|
||||
TUIVideoPreviewCell *videoCell = (TUIVideoPreviewCell *)cell;
|
||||
if (videoCell.player && videoCell.player.rate != 0.0) {
|
||||
[videoCell pausePlayerAndShowNaviBar];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private Method
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)refreshNaviBarAndBottomBarState {
|
||||
TUIAssetModel *model = self.assetModels[self.currentShowIndex];
|
||||
_selectButton.selected = model.isSelected;
|
||||
[self refreshSelectButtonImageViewContentMode];
|
||||
|
||||
_indexLabel.hidden = YES;
|
||||
_numberLabel.text = [NSString stringWithFormat:@"%zd",self.selectedModels.count];
|
||||
_numberImageView.hidden = (self.selectedModels.count <= 0 || _isHideNaviBar || _isCropImage);
|
||||
_numberLabel.hidden = (self.selectedModels.count <= 0 || _isHideNaviBar || _isCropImage);
|
||||
|
||||
_originalPhotoButton.selected = _isSelectOriginalPhoto;
|
||||
_originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;
|
||||
if (_isSelectOriginalPhoto) [self showTotalPhotoBytes];
|
||||
|
||||
if (!_isHideNaviBar) {
|
||||
_originalPhotoButton.hidden = NO;
|
||||
if (_isSelectOriginalPhoto) _originalPhotoLabel.hidden = NO;
|
||||
}
|
||||
if (model.type == TUIAssetMediaTypeVideo || model.type == TUIAssetMediaTypePhoto) {
|
||||
[_editPhotoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
}
|
||||
else {
|
||||
[_editPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
|
||||
}
|
||||
_doneButton.hidden = NO;
|
||||
_selectButton.hidden = NO;
|
||||
[self didICloudSyncStatusChanged:model];
|
||||
}
|
||||
|
||||
- (void)refreshSelectButtonImageViewContentMode {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (self->_selectButton.imageView.image.size.width <= 27) {
|
||||
self->_selectButton.imageView.contentMode = UIViewContentModeCenter;
|
||||
} else {
|
||||
self->_selectButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)didICloudSyncStatusChanged:(TUIAssetModel *)model{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
TUIAssetModel *currentModel = self.assetModels[self.currentShowIndex];
|
||||
if (self.selectedModels.count <= 0) {
|
||||
self->_doneButton.enabled = !currentModel.iCloudFailed;
|
||||
} else {
|
||||
self->_doneButton.enabled = YES;
|
||||
}
|
||||
self->_selectButton.hidden = currentModel.iCloudFailed;
|
||||
if (currentModel.iCloudFailed) {
|
||||
self->_originalPhotoButton.hidden = YES;
|
||||
self->_originalPhotoLabel.hidden = YES;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)showTotalPhotoBytes {
|
||||
if (0 == self.selectedModels.count) {
|
||||
self->_originalPhotoLabel.text = @"";
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
[[TUIImageManager defaultManager] getPhotosTotalBytes:self.selectedModels completion:^(NSString *totalBytes) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->_originalPhotoLabel.text = [NSString stringWithFormat:@"%@ %@",[NSBundle tui_localizedStringForKey:@"TUIAlbumSelectorTotal"],totalBytes];
|
||||
[self->_originalPhotoLabel sizeToFit];
|
||||
});
|
||||
}];
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (NSInteger)currentShowIndex {
|
||||
return [TUICommonTools tui_isRightToLeftLayout] ? self.assetModels.count - _currentShowIndex - 1 : _currentShowIndex;
|
||||
}
|
||||
|
||||
|
||||
- (void)onClick:(NSInteger)index model:(TUIAssetModel *)model;{
|
||||
NSUInteger findIndex = 0;
|
||||
BOOL findModel = [self findInAllModels:model findIndex:&findIndex];
|
||||
if (findModel && _collectionView) {
|
||||
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:findIndex inSection:0]
|
||||
atScrollPosition:(UICollectionViewScrollPositionLeft)animated:NO];
|
||||
}
|
||||
else {
|
||||
//Other bucket
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
25
TUIKit/TUIMultimediaPlugin/Pick/TUIRequestOperation.h
Normal file
25
TUIKit/TUIMultimediaPlugin/Pick/TUIRequestOperation.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUIRequestOperation.h
|
||||
// TUIRequestOperation
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIRequestOperation : NSOperation
|
||||
|
||||
typedef void(^TUIRequestCompletedBlock)(UIImage *photo, NSDictionary *info);
|
||||
typedef void(^TUIRequestProgressBlock)(double progress, NSError *error, BOOL *stop, NSDictionary *info);
|
||||
|
||||
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
|
||||
@property (assign, nonatomic, getter = isFinished) BOOL finished;
|
||||
|
||||
- (instancetype)initWithAsset:(PHAsset *)asset completed:(TUIRequestCompletedBlock)completedBlock progress:(TUIRequestProgressBlock)progressBlock;
|
||||
- (void)done;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
83
TUIKit/TUIMultimediaPlugin/Pick/TUIRequestOperation.m
Normal file
83
TUIKit/TUIMultimediaPlugin/Pick/TUIRequestOperation.m
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// TUIRequestOperation.m
|
||||
// TUIRequestOperation
|
||||
//
|
||||
// Created by lynx on 2024/8/21.
|
||||
// Copyright © 2024 Tencent. All rights reserved.
|
||||
//
|
||||
#import "TUIRequestOperation.h"
|
||||
#import <TUIMultimediaCore/TUIImageManager.h>
|
||||
|
||||
@interface TUIRequestOperation()
|
||||
@property (nonatomic, copy, nullable) TUIRequestCompletedBlock completedBlock;
|
||||
@property (nonatomic, copy, nullable) TUIRequestProgressBlock progressBlock;
|
||||
@property (nonatomic, strong, nullable) PHAsset *asset;
|
||||
@end
|
||||
|
||||
@implementation TUIRequestOperation
|
||||
|
||||
@synthesize executing = _executing;
|
||||
@synthesize finished = _finished;
|
||||
|
||||
- (instancetype)initWithAsset:(PHAsset *)asset completed:(TUIRequestCompletedBlock)completedBlock progress:(TUIRequestProgressBlock)progressBlock {
|
||||
self = [super init];
|
||||
self.asset = asset;
|
||||
self.completedBlock = completedBlock;
|
||||
self.progressBlock = progressBlock;
|
||||
_executing = NO;
|
||||
_finished = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
self.executing = YES;
|
||||
[[TUIImageManager defaultManager] getPhotoWithAsset:self.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
|
||||
if (isDegraded) {
|
||||
return;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.completedBlock) {
|
||||
self.completedBlock(photo, info);
|
||||
}
|
||||
[self done];
|
||||
});
|
||||
} progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.progressBlock) {
|
||||
self.progressBlock(progress, error, stop, info);
|
||||
}
|
||||
});
|
||||
} networkAccessAllowed:YES];
|
||||
}
|
||||
|
||||
- (void)done {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.finished = YES;
|
||||
self.executing = NO;
|
||||
[self reset];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
self.asset = nil;
|
||||
self.completedBlock = nil;
|
||||
self.progressBlock = nil;
|
||||
}
|
||||
|
||||
- (void)setFinished:(BOOL)finished {
|
||||
[self willChangeValueForKey:@"isFinished"];
|
||||
_finished = finished;
|
||||
[self didChangeValueForKey:@"isFinished"];
|
||||
}
|
||||
|
||||
- (void)setExecuting:(BOOL)executing {
|
||||
[self willChangeValueForKey:@"isExecuting"];
|
||||
_executing = executing;
|
||||
[self didChangeValueForKey:@"isExecuting"];
|
||||
}
|
||||
|
||||
- (BOOL)isAsynchronous {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user