提交
This commit is contained in:
18
TUIKit/TUIMultimediaPlugin/Common/NSArray+Functional.h
Normal file
18
TUIKit/TUIMultimediaPlugin/Common/NSArray+Functional.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSArray <__covariant ObjectType>(Functional)
|
||||
|
||||
- (NSArray *)tui_multimedia_map:(id (^)(ObjectType))f;
|
||||
- (NSArray *)tui_multimedia_mapWithIndex:(id (^)(ObjectType, NSUInteger))f;
|
||||
|
||||
- (NSArray<ObjectType> *)tui_multimedia_filter:(BOOL (^)(ObjectType))f;
|
||||
|
||||
+ (NSArray<ObjectType> *)tui_multimedia_arrayWithArray:(NSArray<ObjectType> *)array append:(NSArray<ObjectType> *)append;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
40
TUIKit/TUIMultimediaPlugin/Common/NSArray+Functional.m
Normal file
40
TUIKit/TUIMultimediaPlugin/Common/NSArray+Functional.m
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "NSArray+Functional.h"
|
||||
|
||||
@implementation NSArray (Functional)
|
||||
|
||||
- (NSArray *)tui_multimedia_map:(id (^)(id))f {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count];
|
||||
for (id x in self) {
|
||||
[array addObject:f(x)];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
- (NSArray *)tui_multimedia_mapWithIndex:(id (^)(id, NSUInteger))f {
|
||||
const NSUInteger count = self.count;
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
|
||||
for (NSInteger i = 0; i < count; i++) {
|
||||
[array addObject:f(self[i], i)];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
- (NSArray *)tui_multimedia_filter:(BOOL (^)(id))f {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count];
|
||||
for (id x in self) {
|
||||
if (f(x)) {
|
||||
[array addObject:x];
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
+ (NSArray *)tui_multimedia_arrayWithArray:(NSArray *)array append:(NSArray *)append {
|
||||
NSMutableArray *res = [NSMutableArray arrayWithArray:array];
|
||||
for (id x in append) {
|
||||
[res addObject:x];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@end
|
||||
27
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaCommon.h
Normal file
27
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaCommon.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TXLiteAVSDK_Professional/TXLiveRecordTypeDef.h>
|
||||
#import <TXLiteAVSDK_Professional/TXVideoEditerTypeDef.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define TUIMultimediaConstCGSize(w, h) ((CGSize){(CGFloat)(w), (CGFloat)(h)})
|
||||
|
||||
static const int TUIMultimediaEditBitrate = 12000;
|
||||
|
||||
@interface TUIMultimediaCommon : NSObject
|
||||
@property(class, readonly) NSBundle *bundle;
|
||||
@property(class, readonly) NSBundle *localizableBundle;
|
||||
+ (nullable UIImage *)bundleImageByName:(NSString *)name;
|
||||
+ (nullable UIImage *)bundleRawImageByName:(NSString *)name;
|
||||
+ (NSString *)localizedStringForKey:(NSString *)key;
|
||||
+ (UIColor *)colorFromHex:(NSString *)hex;
|
||||
+ (NSArray<NSString *> *)sortedBundleResourcesIn:(NSString *)directory withExtension:(NSString *)ext;
|
||||
|
||||
+ (NSURL *)getURLByResourcePath:(NSString *)path;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
75
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaCommon.m
Normal file
75
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaCommon.m
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaCommon.h"
|
||||
#import <TUICore/TUIGlobalization.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "NSArray+Functional.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPersistence.h"
|
||||
|
||||
static NSString *const BundleResourceUrlPrefix = @"file:///asset/";
|
||||
|
||||
@implementation TUIMultimediaCommon
|
||||
|
||||
@dynamic bundle;
|
||||
|
||||
+ (NSBundle *)bundle {
|
||||
NSURL *url = [[NSBundle mainBundle] URLForResource:@"TUIMultimedia" withExtension:@"bundle"];
|
||||
return [NSBundle bundleWithURL:url];
|
||||
}
|
||||
|
||||
+ (NSBundle *)localizableBundle {
|
||||
NSURL *url = [[NSBundle mainBundle] URLForResource:@"TUIMultimediaLocalizable" withExtension:@"bundle"];
|
||||
return [NSBundle bundleWithURL:url];
|
||||
}
|
||||
|
||||
+ (UIImage *)bundleImageByName:(NSString *)name {
|
||||
return [UIImage imageNamed:name inBundle:self.bundle compatibleWithTraitCollection:nil];
|
||||
}
|
||||
|
||||
+ (UIImage *)bundleRawImageByName:(NSString *)name {
|
||||
NSString *path = [NSString stringWithFormat:@"%@/%@", [self bundle].resourcePath, name];
|
||||
return [UIImage imageWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
+ (NSString *)localizedStringForKey:(NSString *)key {
|
||||
NSString *lang = [TUIGlobalization getPreferredLanguage];
|
||||
NSString *path = [self.localizableBundle pathForResource:lang ofType:@"lproj"];
|
||||
if (path == nil) {
|
||||
path = [self.localizableBundle pathForResource:@"en" ofType:@"lproj"];
|
||||
}
|
||||
NSBundle *langBundle = [NSBundle bundleWithPath:path];
|
||||
return [langBundle localizedStringForKey:key value:nil table:nil];
|
||||
}
|
||||
|
||||
+ (UIColor *)colorFromHex:(NSString *)hex {
|
||||
return [UIColor tui_colorWithHex:hex];
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)sortedBundleResourcesIn:(NSString *)directory withExtension:(NSString *)ext {
|
||||
NSArray<NSString *> *res = [TUIMultimediaCommon.bundle pathsForResourcesOfType:ext inDirectory:directory];
|
||||
NSString *basePath = TUIMultimediaCommon.bundle.resourcePath;
|
||||
res = [res tui_multimedia_map:^NSString *(NSString *path) {
|
||||
return [path substringFromIndex:basePath.length + 1];
|
||||
}];
|
||||
return [res sortedArrayUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
|
||||
return [a compare:b];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (NSURL *)getURLByResourcePath:(NSString *)path {
|
||||
if (path == nil) {
|
||||
return nil;
|
||||
}
|
||||
if (![path startsWith:BundleResourceUrlPrefix]) {
|
||||
NSURL *url = [NSURL URLWithString:path];
|
||||
if (url.scheme == nil) {
|
||||
return [NSURL fileURLWithPath:path];
|
||||
}
|
||||
return url;
|
||||
}
|
||||
NSURL *bundleUrl = [[self bundle] resourceURL];
|
||||
NSURL *url = [[NSURL alloc] initWithString:[path substringFromIndex:BundleResourceUrlPrefix.length] relativeToURL:bundleUrl];
|
||||
return url;
|
||||
}
|
||||
@end
|
||||
32
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaConfig.h
Normal file
32
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaConfig.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Created by eddardliu on 2024/10/21.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaConfig : NSObject
|
||||
+ (instancetype)sharedInstance;
|
||||
- (void)setConfig:(NSString*)jsonString;
|
||||
- (BOOL)isSupportRecordBeauty;
|
||||
- (BOOL)isSupportRecordAspect;
|
||||
- (BOOL)isSupportRecordTorch;
|
||||
- (BOOL)isSupportRecordScrollFilter;
|
||||
- (BOOL)isSupportVideoEditGraffiti;
|
||||
- (BOOL)isSupportVideoEditPaster;
|
||||
- (BOOL)isSupportVideoEditSubtitle;
|
||||
- (BOOL)isSupportVideoEditBGM;
|
||||
- (BOOL)isSupportPictureEditMosaic;
|
||||
- (BOOL)isSupportPictureEditGraffiti;
|
||||
- (BOOL)isSupportPictureEditPaster;
|
||||
- (BOOL)isSupportPictureEditSubtitle;
|
||||
- (BOOL)isSupportPictureEditCrop;
|
||||
- (UIColor*)getThemeColor;
|
||||
- (int)getMaxRecordDurationMs;
|
||||
- (int)getMinRecordDurationMs;
|
||||
- (int)getVideoQuality;
|
||||
- (NSString *)getPicturePasterConfigFilePath;
|
||||
- (NSString *)getBGMConfigFilePath;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
169
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaConfig.m
Normal file
169
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaConfig.m
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Created by eddardliu on 2024/10/21.
|
||||
|
||||
#import <TUICore/UIColor+TUIHexColor.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
|
||||
#import "TUIMultimediaConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
|
||||
#define DEFAULT_CONFIG_FILE @"config/default_config"
|
||||
|
||||
@interface TUIMultimediaConfig() {
|
||||
NSDictionary * _jsonDicFromFile;
|
||||
NSDictionary * _jsonDicFromSetting;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaConfig
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static TUIMultimediaConfig *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init{
|
||||
self = [super init];
|
||||
NSData *jsonData = [NSData dataWithContentsOfFile:[TUIMultimediaCommon.bundle pathForResource:DEFAULT_CONFIG_FILE ofType:@"json"]];
|
||||
NSError *err = nil;
|
||||
_jsonDicFromFile = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
|
||||
if (err || ![_jsonDicFromFile isKindOfClass:[NSDictionary class]]) {
|
||||
NSLog(@"[TUIMultimedia] Json parse failed: %@", err);
|
||||
_jsonDicFromFile = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setConfig:(NSString*)jsonString {
|
||||
_jsonDicFromFile = nil;
|
||||
if (jsonString == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *err = nil;
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (jsonData) {
|
||||
_jsonDicFromFile = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
|
||||
if (err || ![_jsonDicFromFile isKindOfClass:[NSDictionary class]]) {
|
||||
NSLog(@"[TUIMultimediaConfig setConfig] Json parse failed: %@", err);
|
||||
_jsonDicFromFile = nil;
|
||||
}
|
||||
} else {
|
||||
NSLog(@"Error converting string to data");
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isSupportRecordBeauty {
|
||||
return [self getBoolFromDic:@"support_record_beauty" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportRecordAspect {
|
||||
return [self getBoolFromDic:@"support_record_aspect" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportRecordTorch {
|
||||
return [self getBoolFromDic:@"support_record_torch" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportRecordScrollFilter {
|
||||
return [self getBoolFromDic:@"support_record_scroll_filter" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportVideoEditGraffiti {
|
||||
return [self getBoolFromDic:@"support_video_edit_graffiti" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportVideoEditPaster {
|
||||
return [self getBoolFromDic:@"support_video_edit_paster" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportVideoEditSubtitle {
|
||||
return [self getBoolFromDic:@"support_video_edit_subtitle" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportVideoEditBGM {
|
||||
return [self getBoolFromDic:@"support_video_edit_bgm" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportPictureEditMosaic {
|
||||
return [self getBoolFromDic:@"support_picture_edit_mosaic" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportPictureEditGraffiti {
|
||||
return [self getBoolFromDic:@"support_picture_edit_graffiti" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportPictureEditPaster {
|
||||
return [self getBoolFromDic:@"support_picture_edit_paster" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportPictureEditSubtitle {
|
||||
return [self getBoolFromDic:@"support_picture_edit_subtitle" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSupportPictureEditCrop {
|
||||
return [self getBoolFromDic:@"support_picture_edit_crop" defaultValue:YES];
|
||||
}
|
||||
|
||||
- (UIColor *)getThemeColor {
|
||||
return TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF");
|
||||
}
|
||||
|
||||
- (int)getVideoQuality {
|
||||
return [self getIntFromDic:@"video_quality" defaultValue:2];
|
||||
}
|
||||
|
||||
- (int)getMaxRecordDurationMs {
|
||||
return [self getIntFromDic:@"max_record_duration_ms" defaultValue:15000];
|
||||
}
|
||||
|
||||
- (int)getMinRecordDurationMs {
|
||||
return [self getIntFromDic:@"min_record_duration_ms" defaultValue:2000];
|
||||
}
|
||||
|
||||
- (NSString *)getPicturePasterConfigFilePath {
|
||||
return [self getStringFromDic:@"picture_paster_config_file_path" defaultValue:@"config/picture_paster_data"];
|
||||
}
|
||||
|
||||
- (NSString *)getBGMConfigFilePath {
|
||||
return [self getStringFromDic:@"bgm_config_file_path" defaultValue:@"config/bgm_data"];
|
||||
}
|
||||
|
||||
-(BOOL) getBoolFromDic:(NSString*) dicKey defaultValue:(BOOL) defaultValue{
|
||||
if (_jsonDicFromSetting != nil) {
|
||||
return [_jsonDicFromSetting[dicKey] caseInsensitiveCompare:@"true"] == NSOrderedSame;
|
||||
}
|
||||
|
||||
if (_jsonDicFromFile != nil) {
|
||||
return [_jsonDicFromFile[dicKey] caseInsensitiveCompare:@"true"] == NSOrderedSame;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
-(int) getIntFromDic:(NSString*) dicKey defaultValue:(int) defaultValue{
|
||||
if (_jsonDicFromSetting != nil) {
|
||||
return [(NSNumber *)_jsonDicFromFile[dicKey] intValue];
|
||||
}
|
||||
|
||||
if (_jsonDicFromFile != nil) {
|
||||
return [(NSNumber *)_jsonDicFromFile[dicKey] intValue];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
-(NSString*) getStringFromDic:(NSString*) dicKey defaultValue:(NSString*) defaultValue{
|
||||
if (_jsonDicFromSetting != nil) {
|
||||
return _jsonDicFromSetting[dicKey];
|
||||
}
|
||||
|
||||
if (_jsonDicFromFile != nil) {
|
||||
return _jsonDicFromFile[dicKey];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@end
|
||||
15
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaConstant.h
Normal file
15
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaConstant.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#define VIDEO_EDIT_RESULT_CODE_NO_EDIT 1
|
||||
#define VIDEO_EDIT_RESULT_CODE_GENERATE_SUCCESS 0
|
||||
#define VIDEO_EDIT_RESULT_CODE_CANCEL -1
|
||||
#define VIDEO_EDIT_RESULT_CODE_GENERATE_FAIL -2
|
||||
|
||||
#define PHOTO_EDIT_RESULT_CODE_NO_EDIT 1
|
||||
#define PHOTO_EDIT_RESULT_CODE_GENERATE_SUCCESS 0
|
||||
#define PHOTO_EDIT_RESULT_CODE_CANCEL -1
|
||||
#define PHOTO_EDIT_RESULT_CODE_GENERATE_FAIL -2
|
||||
|
||||
#define SOURCE_TYPE_RECORD 1
|
||||
#define SOURCE_TYPE_ALBUM 2
|
||||
20
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaGeometry.h
Normal file
20
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaGeometry.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static CGFloat Vec2Len(CGPoint v) { return sqrt(v.x * v.x + v.y * v.y); }
|
||||
|
||||
static CGPoint Vec2Mul(CGPoint v, CGFloat k) { return CGPointMake(v.x * k, v.y * k); }
|
||||
|
||||
static CGPoint Vec2AddVector(CGPoint v, CGPoint va) { return CGPointMake(v.x + va.x, v.y + va.y); }
|
||||
|
||||
static CGPoint Vec2AddOffset(CGPoint v, CGFloat k) { return CGPointMake(v.x + k, v.y + k); }
|
||||
|
||||
static CGFloat Vec2Degree(CGPoint v1, CGPoint v2) { return atan2(v2.y, v2.x) - atan2(v1.y, v1.x); }
|
||||
|
||||
static CGPoint Vec2Rotate(CGPoint p, CGFloat r) {
|
||||
CGFloat s = sin(r);
|
||||
CGFloat c = cos(r);
|
||||
return CGPointMake(p.x * c - p.y * s, p.x * s + p.y * c);
|
||||
}
|
||||
17
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImagePicker.h
Normal file
17
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImagePicker.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^TUIMultimediaImagePickerCallback)(UIImage *_Nullable image);
|
||||
|
||||
@interface TUIMultimediaImagePicker : NSObject
|
||||
@property(nullable, nonatomic) TUIMultimediaImagePickerCallback callback;
|
||||
- (instancetype)init;
|
||||
- (void)presentOn:(UIViewController *)viewController;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
38
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImagePicker.m
Normal file
38
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImagePicker.m
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaImagePicker.h"
|
||||
@interface TUIMultimediaImagePicker () <UINavigationControllerDelegate, UIImagePickerControllerDelegate> {
|
||||
UIImagePickerController *_pickerVC;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaImagePicker
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_pickerVC = [[UIImagePickerController alloc] init];
|
||||
_pickerVC.delegate = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)presentOn:(UIViewController *)viewController {
|
||||
[viewController presentViewController:_pickerVC animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey, id> *)info {
|
||||
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
if (_callback != nil) {
|
||||
_callback([info objectForKey:UIImagePickerControllerOriginalImage]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
|
||||
[picker.presentingViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
if (_callback != nil) {
|
||||
_callback(nil);
|
||||
}
|
||||
}
|
||||
@end
|
||||
28
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImageUtil.h
Normal file
28
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImageUtil.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaImageUtil : NSObject
|
||||
// 对View截图
|
||||
+ (UIImage *)imageFromView:(UIView *)view;
|
||||
|
||||
// 对View截图,带旋转
|
||||
+ (UIImage *)imageFromView:(UIView *)view withRotate:(CGFloat)rotate;
|
||||
|
||||
// 以另一图片为模版,创建一个形状大小相同,但颜色不同的图片,保留透明通道,不保留灰度信息
|
||||
+ (UIImage *)imageFromImage:(UIImage *)img withTintColor:(UIColor *)tintColor;
|
||||
|
||||
+ (UIImage *)resizeImage:(UIImage *)img toSize:(CGSize)size;
|
||||
|
||||
+ (UIImage *)rotateImage:(UIImage *)img angle:(float)angle;
|
||||
|
||||
+ (UIImage *)imageWithColor:(UIColor *)color; // size: 1x1
|
||||
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size;
|
||||
+ (UIImage *)createBlueCircleWithWhiteBorder:(CGSize)size withColor:(UIColor*) color;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
135
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImageUtil.m
Normal file
135
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaImageUtil.m
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaImageUtil.h"
|
||||
|
||||
@implementation TUIMultimediaImageUtil
|
||||
+ (UIImage *)imageFromView:(UIView *)view {
|
||||
UIGraphicsBeginImageContext(view.bounds.size);
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
[view.layer renderInContext:ctx];
|
||||
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return img;
|
||||
}
|
||||
|
||||
+ (UIImage *)imageFromView:(UIView *)view withRotate:(CGFloat)rotate {
|
||||
// view旋转后占用的矩形大小--最终画布大小
|
||||
CGSize finalSize = CGSizeMake(CGRectGetWidth(view.frame), CGRectGetHeight(view.frame));
|
||||
// view的原始大小
|
||||
CGSize originSize = CGSizeMake(CGRectGetWidth(view.bounds), CGRectGetHeight(view.bounds));
|
||||
|
||||
UIGraphicsBeginImageContext(finalSize);
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
// 根据画布大小,先移动到中心点
|
||||
CGContextTranslateCTM(ctx, finalSize.width / 2, finalSize.height / 2);
|
||||
// 旋转坐标系
|
||||
CGContextRotateCTM(ctx, rotate);
|
||||
// 根据原始大小确定绘制view的起始位置
|
||||
CGContextTranslateCTM(ctx, -originSize.width / 2, -originSize.height / 2);
|
||||
[view.layer renderInContext:ctx];
|
||||
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return img;
|
||||
}
|
||||
|
||||
+ (UIImage *)simpleImageFromImage:(UIImage *)img withTintColor:(UIColor *)tintColor {
|
||||
// We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the
|
||||
// device’s main screen.
|
||||
UIGraphicsBeginImageContextWithOptions(img.size, NO, 0.0f);
|
||||
[tintColor setFill];
|
||||
CGRect bounds = CGRectMake(0, 0, img.size.width, img.size.height);
|
||||
UIRectFill(bounds);
|
||||
|
||||
// Draw the tinted image in context
|
||||
[img drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
|
||||
|
||||
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return tintedImage;
|
||||
}
|
||||
|
||||
+ (UIImage *)imageFromImage:(UIImage *)img withTintColor:(UIColor *)tintColor {
|
||||
UIImage *imgWithTintColor = [self simpleImageFromImage:img withTintColor:tintColor];
|
||||
if (img.imageAsset == nil) {
|
||||
return imgWithTintColor;
|
||||
}
|
||||
UITraitCollection *const scaleTraitCollection = [UITraitCollection currentTraitCollection];
|
||||
UITraitCollection *const darkUnscaledTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
|
||||
UITraitCollection *const darkScaledTraitCollection =
|
||||
[UITraitCollection traitCollectionWithTraitsFromCollections:@[ scaleTraitCollection, darkUnscaledTraitCollection ]];
|
||||
|
||||
UIImage *imageDark = [img.imageAsset imageWithTraitCollection:darkScaledTraitCollection];
|
||||
if (img != imageDark) {
|
||||
[imgWithTintColor.imageAsset registerImage:[self simpleImageFromImage:imageDark withTintColor:tintColor] withTraitCollection:darkScaledTraitCollection];
|
||||
}
|
||||
|
||||
return imgWithTintColor;
|
||||
}
|
||||
|
||||
+ (UIImage *)resizeImage:(UIImage *)img toSize:(CGSize)size {
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
|
||||
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||
[img drawInRect:CGRectMake(0, 0, size.width, size.height)];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (UIImage *)rotateImage:(UIImage *)image angle:(float)angle {
|
||||
CGFloat radians = angle * (M_PI / 180.0);
|
||||
CGSize size = image.size;
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
|
||||
UIImage *rotatedImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) {
|
||||
CGContextRef cgContext = context.CGContext;
|
||||
CGContextTranslateCTM(cgContext, size.width / 2, size.height / 2);
|
||||
CGContextRotateCTM(cgContext, radians);
|
||||
CGContextTranslateCTM(cgContext, -size.width / 2, -size.height / 2);
|
||||
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
|
||||
}];
|
||||
|
||||
return rotatedImage;
|
||||
}
|
||||
|
||||
|
||||
+ (UIImage *)imageWithColor:(UIColor *)color {
|
||||
return [self imageWithColor:color size:CGSizeMake(1, 1)];
|
||||
}
|
||||
|
||||
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
|
||||
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||
[color set];
|
||||
[rendererContext fillRect:CGRectMake(0, 0, size.width, size.height)];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (UIImage *)createBlueCircleWithWhiteBorder : (CGSize)size withColor:(UIColor*) color{
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
if (!context) return nil;
|
||||
|
||||
|
||||
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
|
||||
|
||||
|
||||
CGFloat centerX = size.width / 2;
|
||||
CGFloat centerY = size.width / 2;
|
||||
CGFloat radius = size.width / 2;
|
||||
|
||||
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
|
||||
CGContextSetLineWidth(context, 0);
|
||||
CGContextAddArc(context, centerX, centerY, radius, 0, 2 * M_PI, 0);
|
||||
CGContextDrawPath(context, kCGPathFillStroke);
|
||||
|
||||
|
||||
CGContextSetFillColorWithColor(context, color.CGColor);
|
||||
CGContextSetLineWidth(context, 0);
|
||||
CGContextAddArc(context, centerX, centerY, 5, 0, 2 * M_PI, 0);
|
||||
CGContextDrawPath(context, kCGPathFillStroke);
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
@end
|
||||
18
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaPersistence.h
Normal file
18
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaPersistence.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
持久化工具类
|
||||
所有路径参数均使用相对路径
|
||||
*/
|
||||
@interface TUIMultimediaPersistence : NSObject
|
||||
+ (NSString *)basePath;
|
||||
+ (BOOL)saveData:(NSData *)data toFile:(NSString *)file error:(NSError **_Nullable)error;
|
||||
+ (nullable NSData *)loadDataFromFile:(NSString *)file error:(NSError **_Nullable)error;
|
||||
+ (BOOL)removeFile:(NSString *)file error:(NSError **_Nullable)error;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
51
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaPersistence.m
Normal file
51
TUIKit/TUIMultimediaPlugin/Common/TUIMultimediaPersistence.m
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaPersistence.h"
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
|
||||
#define PASS_ERROR_WITH_LOG(errsrc, errdst, retval) \
|
||||
do { \
|
||||
if (errsrc != nil) { \
|
||||
NSLog(@"[TUIMultimedia] %s error: %@", __func__, errsrc.localizedDescription); \
|
||||
if (errdst != nil) { \
|
||||
*errdst = errsrc; \
|
||||
} \
|
||||
return retval; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static NSString *const BasePath = @"TUIMultimediaPlugin";
|
||||
|
||||
@implementation TUIMultimediaPersistence
|
||||
+ (NSString *)basePath {
|
||||
NSString *res = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
|
||||
return [res stringByAppendingPathComponent:BasePath];
|
||||
}
|
||||
|
||||
+ (BOOL)saveData:(NSData *)data toFile:(NSString *)file error:(NSError **)error {
|
||||
NSAssert([file startsWith:self.basePath], @"Assert that file path should based on BasePath");
|
||||
NSError *err = nil;
|
||||
[NSFileManager.defaultManager createDirectoryAtPath:[file stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:&err];
|
||||
PASS_ERROR_WITH_LOG(err, error, NO);
|
||||
[data writeToFile:file options:NSDataWritingAtomic error:&err];
|
||||
PASS_ERROR_WITH_LOG(err, error, NO);
|
||||
return file;
|
||||
}
|
||||
|
||||
+ (nullable NSData *)loadDataFromFile:(NSString *)file error:(NSError **)error {
|
||||
NSAssert([file startsWith:self.basePath], @"Assert that file path should based on BasePath");
|
||||
NSError *err = nil;
|
||||
NSData *res = [NSData dataWithContentsOfFile:file options:0 error:&err];
|
||||
PASS_ERROR_WITH_LOG(err, error, nil);
|
||||
return res;
|
||||
}
|
||||
|
||||
+ (BOOL)removeFile:(NSString *)file error:(NSError **_Nullable)error {
|
||||
NSAssert([file startsWith:self.basePath], @"Assert that file path should based on BasePath");
|
||||
NSError *err = nil;
|
||||
BOOL res = [NSFileManager.defaultManager removeItemAtPath:file error:&err];
|
||||
PASS_ERROR_WITH_LOG(err, error, nil);
|
||||
return res;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaSignatureChecker : NSObject
|
||||
+ (instancetype)shareInstance;
|
||||
- (void)startUpdateSignature:(void (^)(void)) UpdateSignatureSuccessful;
|
||||
- (BOOL)isFunctionSupport;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaSignatureChecker.h"
|
||||
#import <ImSDK_Plus/V2TIMManager.h>
|
||||
#import <TUICore/TUIGlobalization.h>
|
||||
#import <TUICore/TUILogin.h>
|
||||
#import <TXLiteAVSDK_Professional/TXLiteAVSDK.h>
|
||||
#import <objc/runtime.h>
|
||||
#import "NSArray+Functional.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPersistence.h"
|
||||
|
||||
#define SCHEDULE_UPDATE_SIGNATURE_INTERVAL_WHEN_START 0.f
|
||||
#define SCHEDULE_UPDATE_SIGNATURE_INTERVAL_WHEN_SUCCESS 3600 * 1000.0f
|
||||
#define SCHEDULE_UPDATE_SIGNATURE_INTERVAL_WHEN_FAIL 1000.0f
|
||||
#define SCHEDULE_UPDATE_SIGNATURE_RETRY_TIMES 3
|
||||
|
||||
@interface TUIMultimediaSignatureChecker () {
|
||||
NSString* _signature;
|
||||
NSInteger _expiredTime;
|
||||
NSTimer* _timer;
|
||||
void (^_updateSignatureSuccess)(void);
|
||||
}
|
||||
@property (nonatomic, assign) NSInteger retryCount;
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaSignatureChecker
|
||||
|
||||
+ (instancetype)shareInstance {
|
||||
static id instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[self alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_retryCount = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startUpdateSignature:(void (^)(void))updateSignatureSuccessful {
|
||||
NSLog(@"TUIMultimediaSignatureChecker startUpdateSignature");
|
||||
_updateSignatureSuccess = updateSignatureSuccessful;
|
||||
[self scheduleUpdateSignature:SCHEDULE_UPDATE_SIGNATURE_INTERVAL_WHEN_START];
|
||||
}
|
||||
|
||||
- (void)updateSignatureIfNeed {
|
||||
if ([NSDate.now timeIntervalSince1970] < _expiredTime) {
|
||||
[self scheduleUpdateSignature:(_expiredTime - [NSDate.now timeIntervalSince1970]) *1000];
|
||||
return;
|
||||
}
|
||||
[V2TIMManager.sharedInstance callExperimentalAPI:@"getVideoEditSignature"
|
||||
param:nil
|
||||
succ:^(NSObject *result) {
|
||||
if (result == nil || ![result isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"getVideoEditSignature: data = nil");
|
||||
return;
|
||||
}
|
||||
NSDictionary *data = (NSDictionary *)result;
|
||||
NSLog(@"getVideoEditSignature: data = %@", data);
|
||||
NSNumber *expiredTime = data[@"expired_time"];
|
||||
NSString *signature = data[@"signature"];
|
||||
if (![expiredTime isKindOfClass:NSNumber.class]) {
|
||||
NSLog(@"getVideoEditSignature: expiredTime type error");
|
||||
return;
|
||||
}
|
||||
if (![signature isKindOfClass:NSString.class]) {
|
||||
NSLog(@"getVideoEditSignature: signature type error");
|
||||
return;
|
||||
}
|
||||
self.retryCount = 0;
|
||||
self->_expiredTime = [expiredTime integerValue];
|
||||
self->_signature = signature;
|
||||
NSLog(@"getVideoEditSignature: succeed. signature=%@, expiredTime=%@", self->_signature, @(self->_expiredTime));
|
||||
[self setSignatureToSDK];
|
||||
[self scheduleUpdateSignature:([expiredTime integerValue] - [NSDate.now timeIntervalSince1970]) * 1000];
|
||||
if (self->_updateSignatureSuccess != nil) {
|
||||
self->_updateSignatureSuccess();
|
||||
self->_updateSignatureSuccess = nil;
|
||||
}
|
||||
}
|
||||
fail:^(int code, NSString *desc) {
|
||||
NSLog(@"getVideoEditSignature: failed. code=%@, desc=%@", @(code), desc);
|
||||
if (code == ERR_SDK_INTERFACE_NOT_SUPPORT) {
|
||||
[self cancelTimer];
|
||||
return;
|
||||
}
|
||||
self.retryCount ++;
|
||||
if (self.retryCount > SCHEDULE_UPDATE_SIGNATURE_RETRY_TIMES) {
|
||||
self.retryCount = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
NSLog(@"getVideoEditSignature: Attempting to get signature for the %ld time",self.retryCount);
|
||||
[self scheduleUpdateSignature:SCHEDULE_UPDATE_SIGNATURE_INTERVAL_WHEN_FAIL];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setSignatureToSDK {
|
||||
NSDictionary *param = @{
|
||||
@"api" : @"setSignature",
|
||||
@"params" : @{
|
||||
@"appid" : [@([TUILogin getSdkAppID]) stringValue],
|
||||
@"signature" : _signature,
|
||||
},
|
||||
};
|
||||
NSError *error = nil;
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:param options:0 error:&error];
|
||||
if (error != nil) {
|
||||
NSLog(@"TUIMultimedia GetMultimediaIsSupport Error:%@", error);
|
||||
}
|
||||
NSString *paramStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
NSLog(@"TUIMultimedia setSignature:%@", paramStr);
|
||||
|
||||
SEL selector = NSSelectorFromString(@"callExperimentalAPI:");
|
||||
if ([[TXUGCBase class] respondsToSelector:selector]) {
|
||||
// [TXUGCBase callExperimentalAPI:paramStr];
|
||||
[[TXUGCBase class] performSelector:selector withObject:paramStr];
|
||||
} else {
|
||||
NSLog(@"callExperimentalAPI: method is not available.");
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isFunctionSupport {
|
||||
return _signature != nil && _signature.length > 0 && [NSDate.now timeIntervalSince1970] < _expiredTime;
|
||||
}
|
||||
|
||||
-(void)scheduleUpdateSignature:(float)interval {
|
||||
[self cancelTimer];
|
||||
interval = interval / 1000;
|
||||
_timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(updateSignatureIfNeed) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
- (void)cancelTimer {
|
||||
if (_timer != nil) {
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaAuthorizationPrompter : UIViewController
|
||||
+ (BOOL) verifyPermissionGranted:(UIViewController*)parentView;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaAuthorizationPrompter.h"
|
||||
#import <SafariServices/SafariServices.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaSignatureChecker.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
|
||||
#define IM_MULTIMEDIA_PLUGIN_DOCUMENT_URL @"https://cloud.tencent.com/document/product/269/113290"
|
||||
|
||||
@interface TUIMultimediaAuthorizationPrompter () {
|
||||
UIButton *_confirmButton;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy) void (^dismissHandler)(void);
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaAuthorizationPrompter
|
||||
|
||||
+ (BOOL) verifyPermissionGranted:(UIViewController*)parentView {
|
||||
if ([[TUIMultimediaSignatureChecker shareInstance] isFunctionSupport]) {
|
||||
return YES;
|
||||
}
|
||||
NSLog(@"signature checker do not support function.");
|
||||
if (parentView != nil) {
|
||||
[TUIMultimediaAuthorizationPrompter showPrompterDialogInViewController:parentView];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (void)showPrompterDialogInViewController:(UIViewController *)presentingVC {
|
||||
TUIMultimediaAuthorizationPrompter *dialog = [[TUIMultimediaAuthorizationPrompter alloc] init];
|
||||
dialog.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
dialog.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
|
||||
[presentingVC presentViewController:dialog animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self setupUI];
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
|
||||
|
||||
UIView *container = [[UIView alloc] init];
|
||||
container.backgroundColor = [UIColor tertiarySystemBackgroundColor];
|
||||
container.layer.cornerRadius = 16;
|
||||
container.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.view addSubview:container];
|
||||
|
||||
UIStackView *titleStack = [[UIStackView alloc] init];
|
||||
titleStack.axis = UILayoutConstraintAxisHorizontal;
|
||||
titleStack.spacing = 12;
|
||||
UILabel *titleLabel = [[UILabel alloc] init];
|
||||
titleLabel.text = [TUIMultimediaCommon localizedStringForKey:@"prompter"];
|
||||
titleLabel.font = [UIFont systemFontOfSize:20];
|
||||
titleLabel.textColor = [UIColor labelColor];
|
||||
[titleStack addArrangedSubview:titleLabel];
|
||||
|
||||
UILabel *prompter = [[UILabel alloc] init];
|
||||
prompter.numberOfLines = 0;
|
||||
prompter.text = [TUIMultimediaCommon localizedStringForKey:@"authorization_prompter"];
|
||||
prompter.font = [UIFont systemFontOfSize:14];
|
||||
prompter.textColor = [UIColor secondaryLabelColor];
|
||||
|
||||
UITextView *prompter_access_docments = [[UITextView alloc] init];
|
||||
prompter_access_docments.editable = NO;
|
||||
prompter_access_docments.scrollEnabled = NO;
|
||||
prompter_access_docments.backgroundColor = [UIColor clearColor];
|
||||
prompter_access_docments.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
|
||||
NSString* prompter_accessing_documents = [TUIMultimediaCommon localizedStringForKey:@"authorization_prompter_accessing_documents"];
|
||||
NSString* documents_title = [TUIMultimediaCommon localizedStringForKey:@"authorization_prompter_documents_title"];
|
||||
NSRange title_range = [prompter_accessing_documents rangeOfString:documents_title];
|
||||
|
||||
NSMutableAttributedString *linkText = [[NSMutableAttributedString alloc] initWithString:prompter_accessing_documents];
|
||||
[linkText addAttribute:NSLinkAttributeName
|
||||
value:IM_MULTIMEDIA_PLUGIN_DOCUMENT_URL
|
||||
range:title_range];
|
||||
prompter_access_docments.attributedText = linkText;
|
||||
prompter_access_docments.tintColor = [UIColor systemBlueColor];
|
||||
prompter_access_docments.font = [UIFont systemFontOfSize:14];
|
||||
prompter_access_docments.textColor = [UIColor secondaryLabelColor];
|
||||
|
||||
|
||||
UILabel *prompter_remove_module = [[UILabel alloc] init];
|
||||
prompter_remove_module.numberOfLines = 0;
|
||||
prompter_remove_module.text = [TUIMultimediaCommon localizedStringForKey:@"authorization_prompter_remove_module"];
|
||||
prompter_remove_module.font = [UIFont systemFontOfSize:14];
|
||||
prompter_remove_module.textColor = [UIColor secondaryLabelColor];
|
||||
|
||||
|
||||
_confirmButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[_confirmButton setTitle:[TUIMultimediaCommon localizedStringForKey:@"ok"] forState:UIControlStateNormal];
|
||||
[_confirmButton addTarget:self
|
||||
action:@selector(confirmAction)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
_confirmButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
||||
|
||||
|
||||
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[
|
||||
titleStack, prompter, prompter_access_docments, prompter_remove_module, _confirmButton
|
||||
]];
|
||||
stack.axis = UILayoutConstraintAxisVertical;
|
||||
stack.spacing = 6;
|
||||
stack.alignment = UIStackViewAlignmentLeading;
|
||||
stack.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[container addSubview:stack];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[container.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
|
||||
[container.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
|
||||
[container.widthAnchor constraintEqualToAnchor:self.view.widthAnchor
|
||||
multiplier:0.8],
|
||||
|
||||
[stack.topAnchor constraintEqualToAnchor:container.topAnchor constant:20],
|
||||
[stack.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:20],
|
||||
[stack.trailingAnchor constraintEqualToAnchor:container.trailingAnchor constant:-20],
|
||||
[stack.bottomAnchor constraintEqualToAnchor:container.bottomAnchor constant:-20],
|
||||
|
||||
[_confirmButton.leadingAnchor constraintEqualToAnchor:stack.leadingAnchor]
|
||||
]];
|
||||
}
|
||||
|
||||
- (void)confirmAction {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UITextViewDelegate
|
||||
- (BOOL)textView:(UITextView *)textView
|
||||
shouldInteractWithURL:(NSURL *)URL
|
||||
inRange:(NSRange)characterRange
|
||||
interaction:(UITextItemInteraction)interaction {
|
||||
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL];
|
||||
[self presentViewController:safari animated:YES completion:nil];
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaAutoScrollLabel : UIView
|
||||
@property(nonatomic) CGFloat scrollSpeed;
|
||||
@property(nonatomic) NSTimeInterval pauseInterval;
|
||||
@property(nonatomic) NSAttributedString *text;
|
||||
@property(nonatomic) CGFloat fadeInOutRate;
|
||||
@property(nonatomic) BOOL active;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaAutoScrollLabel.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaGeometry.h"
|
||||
|
||||
static NSString *const ScrollAnimeKey = @"ScrollAnimeKey";
|
||||
|
||||
@interface TUIMultimediaAutoScrollLabel () <CAAnimationDelegate> {
|
||||
UIScrollView *_scrollView;
|
||||
UILabel *_label;
|
||||
UILabel *_label2;
|
||||
CAGradientLayer *_fadeLayer;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaAutoScrollLabel
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
_scrollSpeed = 20;
|
||||
_pauseInterval = 2;
|
||||
_text = [[NSAttributedString alloc] initWithString:@""];
|
||||
_fadeInOutRate = 0.1;
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
_scrollView = [[UIScrollView alloc] init];
|
||||
_scrollView.scrollEnabled = NO;
|
||||
_scrollView.userInteractionEnabled = NO;
|
||||
[self addSubview:_scrollView];
|
||||
|
||||
_label = [[UILabel alloc] init];
|
||||
_label.numberOfLines = 1;
|
||||
[_scrollView addSubview:_label];
|
||||
|
||||
_label2 = [[UILabel alloc] init];
|
||||
_label2.numberOfLines = 1;
|
||||
[_scrollView addSubview:_label2];
|
||||
|
||||
NSArray *fadeColorList = @[
|
||||
(__bridge id)[UIColor.blackColor colorWithAlphaComponent:0].CGColor,
|
||||
(__bridge id)UIColor.blackColor.CGColor,
|
||||
(__bridge id)UIColor.blackColor.CGColor,
|
||||
(__bridge id)[UIColor.blackColor colorWithAlphaComponent:0].CGColor,
|
||||
];
|
||||
_fadeLayer = [[CAGradientLayer alloc] init];
|
||||
_fadeLayer.backgroundColor = UIColor.clearColor.CGColor;
|
||||
_fadeLayer.colors = fadeColorList;
|
||||
_fadeLayer.locations = @[ @0, @(_fadeInOutRate), @(1 - _fadeInOutRate), @1 ];
|
||||
_fadeLayer.startPoint = CGPointMake(0, 0.5);
|
||||
_fadeLayer.endPoint = CGPointMake(1, 0.5);
|
||||
|
||||
[_scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.equalTo(_label.mas_height);
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
[_label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.top.bottom.equalTo(_scrollView);
|
||||
}];
|
||||
[_label2 mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.bottom.equalTo(_scrollView);
|
||||
make.left.equalTo(_label.mas_right).offset(self.bounds.size.width);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
_fadeLayer.frame = self.bounds;
|
||||
if (_active) {
|
||||
[self startScroll];
|
||||
}
|
||||
[_label2 mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_label.mas_right).offset(self.bounds.size.width);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startScroll {
|
||||
CGFloat scrollWidth = _scrollView.bounds.size.width;
|
||||
CGFloat labelWidth = _label.bounds.size.width;
|
||||
|
||||
[_scrollView.layer removeAnimationForKey:ScrollAnimeKey];
|
||||
|
||||
_scrollView.bounds = CGRectMake(0, 0, _scrollView.bounds.size.width, _scrollView.bounds.size.height);
|
||||
|
||||
CABasicAnimation *anime = [CABasicAnimation animationWithKeyPath:@"bounds.origin.x"];
|
||||
anime.fromValue = @(0);
|
||||
anime.toValue = @(labelWidth + scrollWidth);
|
||||
anime.duration = (labelWidth + scrollWidth) / _scrollSpeed;
|
||||
anime.removedOnCompletion = NO;
|
||||
anime.delegate = self;
|
||||
[_scrollView.layer addAnimation:anime forKey:ScrollAnimeKey];
|
||||
}
|
||||
|
||||
- (void)stopScroll {
|
||||
[_scrollView.layer removeAnimationForKey:ScrollAnimeKey];
|
||||
_scrollView.contentOffset = CGPointMake(0, 0);
|
||||
}
|
||||
|
||||
- (void)animationDidStop:(CAAnimation *)anime finished:(BOOL)finished {
|
||||
if (anime == [_scrollView.layer animationForKey:ScrollAnimeKey] && finished && _active) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_pauseInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (self->_active) {
|
||||
[self startScroll];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#pragma mark - Properties
|
||||
- (void)setActive:(BOOL)active {
|
||||
if (!_active && active) {
|
||||
[self startScroll];
|
||||
self.layer.mask = _fadeLayer;
|
||||
} else if (_active && !active) {
|
||||
[self stopScroll];
|
||||
self.layer.mask = nil;
|
||||
}
|
||||
_active = active;
|
||||
}
|
||||
- (void)setText:(NSAttributedString *)text {
|
||||
_label.attributedText = text;
|
||||
[_label sizeToFit];
|
||||
|
||||
_label2.attributedText = text;
|
||||
[_label2 sizeToFit];
|
||||
|
||||
_scrollView.contentSize = _label.bounds.size;
|
||||
_scrollView.contentOffset = CGPointMake(0, 0);
|
||||
if (_active) {
|
||||
[self startScroll];
|
||||
}
|
||||
}
|
||||
- (void)setFadeInOutRate:(CGFloat)fadeInOutRate {
|
||||
_fadeInOutRate = MAX(0, MIN(1, fadeInOutRate));
|
||||
_fadeLayer.locations = @[ @0, @(_fadeInOutRate), @(1 - _fadeInOutRate), @1 ];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaCheckBox : UIControl
|
||||
@property(nonatomic) BOOL on;
|
||||
@property(nonatomic) NSString *text;
|
||||
@property(nonatomic) UIColor *textColor;
|
||||
@property(nonatomic) CGSize iconSize;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaCheckBox.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaImageUtil.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
|
||||
@interface TUIMultimediaCheckBox () {
|
||||
UILabel *_label;
|
||||
UIImageView *_imgView;
|
||||
UITapGestureRecognizer *_tapRec;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaCheckBox
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
_iconSize = CGSizeMake(18, 18);
|
||||
UIImage *imgOff = [TUIMultimediaImageUtil imageFromImage:TUIMultimediaPluginBundleThemeImage(@"checkbox_off_img", @"checkbox_off") withTintColor:[[TUIMultimediaConfig sharedInstance] getThemeColor]];;
|
||||
UIImage *imgOn = [TUIMultimediaImageUtil imageFromImage:TUIMultimediaPluginBundleThemeImage(@"checkbox_on_img", @"checkbox_on") withTintColor:[[TUIMultimediaConfig sharedInstance] getThemeColor]];
|
||||
_imgView = [[UIImageView alloc] initWithImage:imgOff highlightedImage:imgOn];
|
||||
[self addSubview:_imgView];
|
||||
_imgView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
|
||||
_label = [[UILabel alloc] init];
|
||||
[self addSubview:_label];
|
||||
_label.textColor = UIColor.whiteColor;
|
||||
|
||||
[_imgView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.top.bottom.equalTo(self);
|
||||
make.size.mas_equalTo(self->_iconSize);
|
||||
}];
|
||||
[_label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self);
|
||||
make.left.equalTo(_imgView.mas_right).inset(5);
|
||||
make.centerY.equalTo(_imgView);
|
||||
}];
|
||||
|
||||
_tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap)];
|
||||
[self addGestureRecognizer:_tapRec];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onTap {
|
||||
[self setOn:!_on];
|
||||
[self sendActionsForControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
- (void)setOn:(BOOL)on {
|
||||
_on = on;
|
||||
_imgView.highlighted = on;
|
||||
}
|
||||
|
||||
- (void)setIconSize:(CGSize)iconSize {
|
||||
_iconSize = iconSize;
|
||||
[_imgView mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.size.mas_equalTo(self->_iconSize);
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)text {
|
||||
return _label.text;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text {
|
||||
_label.text = text;
|
||||
}
|
||||
|
||||
- (UIColor *)textColor {
|
||||
return _label.textColor;
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)textColor {
|
||||
_label.textColor = textColor;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
环形进度条
|
||||
*/
|
||||
@interface TUIMultimediaCircleProgressView : UIView
|
||||
@property(nonatomic) CGFloat progress; // 0.0 ~ 1.0
|
||||
@property(nonatomic) UIColor *progressColor; // 进度条颜色
|
||||
@property(nonatomic) UIColor *progressBgColor; // 进度条背景颜色
|
||||
@property(nonatomic) CGFloat width; // 圆环宽度
|
||||
@property(nonatomic) BOOL clockwise;
|
||||
@property(nonatomic) CGFloat startAngle;
|
||||
@property(nonatomic) CAShapeLayerLineCap lineCap;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaCircleProgressView.h"
|
||||
|
||||
@interface TUIMultimediaCircleProgressView () {
|
||||
CAShapeLayer *_backLayer; // 背景图层
|
||||
CAShapeLayer *_frontLayer; // 用来填充的图层
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaCircleProgressView
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
_clockwise = YES;
|
||||
_width = 5;
|
||||
_startAngle = -M_PI / 2;
|
||||
_progressBgColor = UIColor.grayColor;
|
||||
_progressColor = UIColor.greenColor;
|
||||
_lineCap = kCALineCapRound;
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)initUI {
|
||||
_backLayer = [CAShapeLayer layer];
|
||||
[self.layer addSublayer:_backLayer];
|
||||
_backLayer.fillColor = nil;
|
||||
_backLayer.strokeColor = _progressBgColor.CGColor;
|
||||
|
||||
_frontLayer = [CAShapeLayer layer];
|
||||
[self.layer addSublayer:_frontLayer];
|
||||
_frontLayer.fillColor = nil;
|
||||
_frontLayer.lineCap = _lineCap;
|
||||
_frontLayer.strokeColor = _progressColor.CGColor;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
CGFloat len = MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
|
||||
CGPoint center = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2);
|
||||
UIBezierPath *backgroundBezierPath = [UIBezierPath bezierPathWithArcCenter:center
|
||||
radius:(len - _width) / 2.0
|
||||
startAngle:0
|
||||
endAngle:M_PI * 2
|
||||
clockwise:_clockwise];
|
||||
UIBezierPath *frontFillBezierPath = [UIBezierPath bezierPathWithArcCenter:center
|
||||
radius:(len - _width) / 2.0
|
||||
startAngle:_startAngle
|
||||
endAngle:_startAngle + (_clockwise ? 2 * M_PI : -2 * M_PI)
|
||||
clockwise:_clockwise];
|
||||
_backLayer.path = backgroundBezierPath.CGPath;
|
||||
_frontLayer.path = frontFillBezierPath.CGPath;
|
||||
_frontLayer.lineWidth = _width;
|
||||
_backLayer.lineWidth = _width;
|
||||
_frontLayer.strokeEnd = 0;
|
||||
}
|
||||
|
||||
#pragma mark - setters
|
||||
- (void)setProgress:(CGFloat)progress {
|
||||
_progress = MAX(0, MIN(1, progress));
|
||||
}
|
||||
- (void)setProgress:(CGFloat)progress animated:(BOOL)animated {
|
||||
_progress = MAX(0, MIN(1, progress));
|
||||
if (animated) {
|
||||
[UIView animateWithDuration:0.25
|
||||
animations:^{
|
||||
self->_frontLayer.strokeEnd = progress;
|
||||
}];
|
||||
} else {
|
||||
self->_frontLayer.strokeEnd = progress;
|
||||
}
|
||||
}
|
||||
- (void)setClockwise:(BOOL)clockwise {
|
||||
_clockwise = clockwise;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
- (void)setStartAngle:(CGFloat)startAngle {
|
||||
_startAngle = startAngle;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
- (void)setWidth:(CGFloat)width {
|
||||
_width = width;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
- (void)setProgressColor:(UIColor *)progressColor {
|
||||
_progressColor = progressColor;
|
||||
_frontLayer.strokeColor = _progressColor.CGColor;
|
||||
}
|
||||
- (void)setProgressBgColor:(UIColor *)progressBgColor {
|
||||
_progressBgColor = progressBgColor;
|
||||
_backLayer.strokeColor = _progressBgColor.CGColor;
|
||||
}
|
||||
- (void)setLineCap:(CAShapeLayerLineCap)lineCap {
|
||||
_lineCap = lineCap;
|
||||
_frontLayer.lineCap = lineCap;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaIconLabelButton : UIButton
|
||||
@property(nonatomic) CGSize iconSize;
|
||||
@property(nonatomic) CGFloat iconLabelGap;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaIconLabelButton.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface TUIMultimediaIconLabelButton () {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaIconLabelButton
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
return self;
|
||||
}
|
||||
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
|
||||
CGFloat midX = CGRectGetMidX(contentRect);
|
||||
CGFloat minY = CGRectGetMinY(contentRect);
|
||||
return CGRectMake(midX - _iconSize.width / 2, minY, _iconSize.width, _iconSize.height);
|
||||
}
|
||||
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
|
||||
CGRect imageRect = [self imageRectForContentRect:contentRect];
|
||||
CGSize size = self.currentAttributedTitle.size;
|
||||
CGFloat midX = CGRectGetMidX(contentRect);
|
||||
return CGRectMake(midX - size.width / 2, CGRectGetMaxY(imageRect) + _iconLabelGap, size.width, size.height);
|
||||
}
|
||||
- (CGSize)intrinsicContentSize {
|
||||
CGSize titleSize = [self titleRectForContentRect:CGRectZero].size;
|
||||
return CGSizeMake(MAX(_iconSize.width, titleSize.width), _iconSize.height + _iconLabelGap + titleSize.height);
|
||||
}
|
||||
- (void)setIconSize:(CGSize)iconSize {
|
||||
_iconSize = iconSize;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
- (void)setIconLabelGap:(CGFloat)iconLabelGap {
|
||||
_iconLabelGap = iconLabelGap;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaLabelCell : UICollectionViewCell
|
||||
@property(nullable, nonatomic) NSAttributedString *attributedText;
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
|
||||
+ (NSString *)reuseIdentifier;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaLabelCell.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface TUIMultimediaLabelCell () {
|
||||
UILabel *_label;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaLabelCell
|
||||
@dynamic attributedText;
|
||||
|
||||
+ (NSString *)reuseIdentifier {
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
_label = [[UILabel alloc] init];
|
||||
[self.contentView addSubview:_label];
|
||||
_label.textAlignment = NSTextAlignmentCenter;
|
||||
[_label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attrText {
|
||||
return _label.attributedText;
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText {
|
||||
_label.attributedText = attributedText;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaPopupController : UIViewController
|
||||
@property(nullable, nonatomic) UIView *mainView;
|
||||
@property(nonatomic) float animeDuration;
|
||||
|
||||
- (BOOL)popupControllerWillCancel;
|
||||
- (void)popupControllerDidCanceled;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaPopupController.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface TUIMultimediaPopupController () {
|
||||
UIView *_popupView;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaPopupController
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_animeDuration = 0.15;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
_popupView = [[UIView alloc] init];
|
||||
[self.view addSubview:_popupView];
|
||||
|
||||
UIView *cancelView = [[UIView alloc] init];
|
||||
[self.view addSubview:cancelView];
|
||||
|
||||
[_popupView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.view);
|
||||
make.top.equalTo(self.view.mas_bottom);
|
||||
}];
|
||||
[cancelView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.left.right.equalTo(self.view);
|
||||
make.bottom.equalTo(_popupView.mas_top);
|
||||
}];
|
||||
|
||||
UITapGestureRecognizer *rec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onCancelViewTap:)];
|
||||
rec.cancelsTouchesInView = NO;
|
||||
[cancelView addGestureRecognizer:rec];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[UIView animateWithDuration:_animeDuration
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveLinear
|
||||
animations:^{
|
||||
[self->_popupView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.view);
|
||||
make.bottom.equalTo(self.view.mas_bottom);
|
||||
}];
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
- (BOOL)popupControllerWillCancel {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)popupControllerDidCanceled {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
- (void)onCancelViewTap:(UITapGestureRecognizer *)rec {
|
||||
BOOL b = [self popupControllerWillCancel];
|
||||
if (b) {
|
||||
[UIView animateWithDuration:_animeDuration
|
||||
delay:0
|
||||
options:UIViewAnimationOptionCurveLinear
|
||||
animations:^{
|
||||
[self->_popupView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.view);
|
||||
make.top.equalTo(self.view.mas_bottom);
|
||||
}];
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[self popupControllerDidCanceled];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMainView:(UIView *)mainView {
|
||||
if (_mainView != nil) {
|
||||
[_mainView removeFromSuperview];
|
||||
}
|
||||
_mainView = mainView;
|
||||
[_popupView addSubview:_mainView];
|
||||
[mainView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self->_popupView);
|
||||
}];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaSplitter : UIView
|
||||
@property(nonatomic) UILayoutConstraintAxis axis;
|
||||
@property(nonatomic) UIColor *color;
|
||||
@property(nonatomic) CGFloat lineWidth;
|
||||
@property(nonatomic) CGLineCap lineCap;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaSplitter.h"
|
||||
|
||||
@interface TUIMultimediaSplitter () {
|
||||
CAShapeLayer *_shapeLayer;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaSplitter
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
_color = UIColor.lightGrayColor;
|
||||
_lineWidth = 1;
|
||||
_lineCap = kCGLineCapRound;
|
||||
self.backgroundColor = UIColor.clearColor;
|
||||
|
||||
_shapeLayer = [[CAShapeLayer alloc] init];
|
||||
[self.layer addSublayer:_shapeLayer];
|
||||
|
||||
[self updateShapeLayer];
|
||||
return self;
|
||||
}
|
||||
- (void)updateShapeLayer {
|
||||
_shapeLayer.frame = self.bounds;
|
||||
CGPoint startPoint, endPoint;
|
||||
CGSize size = self.bounds.size;
|
||||
CGFloat inset = 0;
|
||||
if (_axis == UILayoutConstraintAxisVertical) {
|
||||
startPoint = CGPointMake(size.width / 2, inset);
|
||||
endPoint = CGPointMake(size.width / 2, size.height - inset);
|
||||
} else {
|
||||
startPoint = CGPointMake(inset, size.height / 2);
|
||||
endPoint = CGPointMake(size.width - inset, size.height / 2);
|
||||
}
|
||||
UIBezierPath *path = [UIBezierPath bezierPath];
|
||||
[path moveToPoint:startPoint];
|
||||
[path addLineToPoint:endPoint];
|
||||
_shapeLayer.path = path.CGPath;
|
||||
_shapeLayer.strokeColor = _color.CGColor;
|
||||
_shapeLayer.lineWidth = _lineWidth;
|
||||
switch (_lineCap) {
|
||||
default:
|
||||
case kCGLineCapButt:
|
||||
_shapeLayer.lineCap = kCALineCapButt;
|
||||
break;
|
||||
case kCGLineCapRound:
|
||||
_shapeLayer.lineCap = kCALineCapRound;
|
||||
break;
|
||||
case kCGLineCapSquare:
|
||||
_shapeLayer.lineCap = kCALineCapSquare;
|
||||
break;
|
||||
}
|
||||
}
|
||||
- (void)layoutSubviews {
|
||||
[self updateShapeLayer];
|
||||
}
|
||||
- (void)setAxis:(UILayoutConstraintAxis)axis {
|
||||
_axis = axis;
|
||||
[self updateShapeLayer];
|
||||
}
|
||||
- (void)setColor:(UIColor *)color {
|
||||
_color = color;
|
||||
[self updateShapeLayer];
|
||||
}
|
||||
- (void)setLineWidth:(CGFloat)lineWidth {
|
||||
_lineWidth = lineWidth;
|
||||
[self updateShapeLayer];
|
||||
}
|
||||
- (void)setLineCap:(CGLineCap)lineCap {
|
||||
_lineCap = lineCap;
|
||||
[self updateShapeLayer];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TUIMultimediaTabPanelTab;
|
||||
|
||||
@protocol TUIMultimediaTabPanelDelegate;
|
||||
|
||||
@interface TUIMultimediaTabPanel : UIView
|
||||
@property(nonatomic) NSArray<TUIMultimediaTabPanelTab *> *tabs;
|
||||
@property(nonatomic) NSInteger selectedIndex;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaTabPanelDelegate> delegate;
|
||||
- (id)initWithFrame:(CGRect)frame;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaTabPanelDelegate <NSObject>
|
||||
- (void)tabPanel:(TUIMultimediaTabPanel *)panel selectedIndexChanged:(NSInteger)selectedIndex;
|
||||
@end
|
||||
|
||||
@interface TUIMultimediaTabPanelTab : NSObject
|
||||
@property(nonatomic) UIView *view;
|
||||
@property(nullable, nonatomic) NSString *name;
|
||||
@property(nullable, nonatomic) UIImage *icon;
|
||||
- (instancetype)initWithName:(nullable NSString *)name icon:(nullable UIImage *)icon view:(UIView *)view;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaTabBarDelegate;
|
||||
|
||||
@interface TUIMultimediaTabBar : UIView
|
||||
@property(nonatomic) NSArray<id> *tabs;
|
||||
@property(nonatomic) NSInteger selectedIndex;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaTabBarDelegate> delegate;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaTabBarDelegate <NSObject>
|
||||
- (void)tabBar:(TUIMultimediaTabBar *)bar selectedIndexChanged:(NSInteger)index;
|
||||
@end
|
||||
|
||||
@interface TUIMultimediaTabBarCell : UICollectionViewCell
|
||||
@property(nullable, nonatomic) NSAttributedString *attributedText;
|
||||
@property(nullable, nonatomic) UIImage *icon;
|
||||
@property(nonatomic) CGFloat padding;
|
||||
@property(nonatomic) BOOL barCellSelected;
|
||||
+ (NSString *)reuseIdentifier;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
286
TUIKit/TUIMultimediaPlugin/Common/View/TUIMultimediaTabPanel.m
Normal file
286
TUIKit/TUIMultimediaPlugin/Common/View/TUIMultimediaTabPanel.m
Normal file
@@ -0,0 +1,286 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaTabPanel.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/NSArray+Functional.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaImageUtil.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaSplitter.h"
|
||||
|
||||
@interface TUIMultimediaTabPanel () <TUIMultimediaTabBarDelegate> {
|
||||
TUIMultimediaTabBar *_bar;
|
||||
TUIMultimediaSplitter *_splitter;
|
||||
NSArray<TUIMultimediaTabPanelTab *> *_tabs;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaTabPanel
|
||||
|
||||
@dynamic tabs;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
[self initUI];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
_bar = [[TUIMultimediaTabBar alloc] init];
|
||||
[self addSubview:_bar];
|
||||
_bar.delegate = self;
|
||||
|
||||
_splitter = [[TUIMultimediaSplitter alloc] init];
|
||||
[self addSubview:_splitter];
|
||||
_splitter.lineWidth = 1;
|
||||
_splitter.color = TUIMultimediaPluginDynamicColor(@"tabpanel_splitter_color", @"#FFFFFF1A");
|
||||
|
||||
[_bar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.top.equalTo(self).inset(5);
|
||||
make.height.mas_equalTo(42);
|
||||
}];
|
||||
[_splitter mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self);
|
||||
make.top.equalTo(_bar.mas_bottom);
|
||||
make.height.mas_equalTo(5);
|
||||
}];
|
||||
}
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
- (NSInteger)selectedIndex {
|
||||
return _bar.selectedIndex;
|
||||
}
|
||||
|
||||
- (void)setSelectedIndex:(NSInteger)selectedIndex {
|
||||
_bar.selectedIndex = selectedIndex;
|
||||
for (int i = 0; i < _tabs.count; i++) {
|
||||
TUIMultimediaTabPanelTab *t = _tabs[i];
|
||||
t.view.hidden = i != selectedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<TUIMultimediaTabPanelTab *> *)tabs {
|
||||
return _tabs;
|
||||
}
|
||||
|
||||
- (void)setTabs:(NSArray<TUIMultimediaTabPanelTab *> *)value {
|
||||
if (_tabs != nil) {
|
||||
for (TUIMultimediaTabPanelTab *t in _tabs) {
|
||||
[t.view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
_tabs = value;
|
||||
_bar.selectedIndex = -1;
|
||||
_bar.tabs = [value tui_multimedia_map:^id(TUIMultimediaTabPanelTab *t) {
|
||||
return t.icon == nil ? t.name : t.icon;
|
||||
}];
|
||||
for (int i = 0; i < _tabs.count; i++) {
|
||||
TUIMultimediaTabPanelTab *t = _tabs[i];
|
||||
t.view.hidden = YES;
|
||||
[self addSubview:t.view];
|
||||
[t.view mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.bottom.equalTo(self);
|
||||
make.top.equalTo(_splitter.mas_bottom);
|
||||
}];
|
||||
}
|
||||
if (_tabs.count > 0) {
|
||||
TUIMultimediaTabPanelTab *t = _tabs[0];
|
||||
_bar.selectedIndex = 0;
|
||||
t.view.hidden = NO;
|
||||
}
|
||||
}
|
||||
#pragma mark - TUIMultimediaTabBarDelegate protocol
|
||||
- (void)tabBar:(TUIMultimediaTabBar *)bar selectedIndexChanged:(NSInteger)index {
|
||||
for (int i = 0; i < _tabs.count; i++) {
|
||||
TUIMultimediaTabPanelTab *t = _tabs[i];
|
||||
t.view.hidden = i != index;
|
||||
}
|
||||
[_delegate tabPanel:self selectedIndexChanged:index];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - TUIMultimediaTabPanelTab
|
||||
@implementation TUIMultimediaTabPanelTab {
|
||||
}
|
||||
- (instancetype)initWithName:(NSString *)name icon:(UIImage *)icon view:(UIView *)view {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_name = name;
|
||||
_icon = icon;
|
||||
_view = view;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
#pragma mark - TUIMultimediaTabBar
|
||||
|
||||
@interface TUIMultimediaTabBar () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> {
|
||||
UICollectionView *_collectionView;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaTabBar
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
[self initUI];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
layout.minimumInteritemSpacing = 10;
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
_collectionView.backgroundColor = UIColor.clearColor;
|
||||
_collectionView.showsHorizontalScrollIndicator = NO;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.dataSource = self;
|
||||
[_collectionView registerClass:TUIMultimediaTabBarCell.class forCellWithReuseIdentifier:TUIMultimediaTabBarCell.reuseIdentifier];
|
||||
[self addSubview:_collectionView];
|
||||
|
||||
[_collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setSelectedIndex:(NSInteger)selectedIndex {
|
||||
_selectedIndex = selectedIndex;
|
||||
[_collectionView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDelegateFlowLayout protocol
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[collectionView deselectItemAtIndexPath:indexPath animated:NO];
|
||||
_selectedIndex = indexPath.item;
|
||||
[collectionView reloadData];
|
||||
|
||||
[_delegate tabBar:self selectedIndexChanged:_selectedIndex];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDataSource protocol
|
||||
- (NSAttributedString *)getAttributedString:(NSString *)str selected:(BOOL)selected {
|
||||
UIColor *color;
|
||||
UIFont *font;
|
||||
if (selected) {
|
||||
color = UIColor.whiteColor;
|
||||
font = [UIFont systemFontOfSize:16 weight:UIFontWeightBold];
|
||||
} else {
|
||||
color = [UIColor colorWithWhite:1 alpha:0.6];
|
||||
font = [UIFont systemFontOfSize:16 weight:UIFontWeightRegular];
|
||||
}
|
||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:str
|
||||
attributes:@{
|
||||
NSForegroundColorAttributeName : color,
|
||||
NSFontAttributeName : font,
|
||||
}];
|
||||
return text;
|
||||
}
|
||||
- (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
TUIMultimediaTabBarCell *cell = (TUIMultimediaTabBarCell *)[collectionView dequeueReusableCellWithReuseIdentifier:TUIMultimediaTabBarCell.reuseIdentifier
|
||||
forIndexPath:indexPath];
|
||||
cell.barCellSelected = indexPath.item == _selectedIndex;
|
||||
BOOL cellSelected = indexPath.item == _selectedIndex;
|
||||
id item = _tabs[indexPath.item];
|
||||
if ([item isKindOfClass:NSString.class]) {
|
||||
cell.attributedText = [self getAttributedString:item selected:cellSelected];
|
||||
} else if ([item isKindOfClass:UIImage.class]) {
|
||||
cell.contentView.backgroundColor = cellSelected ? [UIColor colorWithWhite:1 alpha:0.1] : UIColor.clearColor;
|
||||
cell.icon = item;
|
||||
cell.padding = 8;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView
|
||||
layout:(UICollectionViewLayout *)collectionViewLayout
|
||||
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
CGFloat h = _collectionView.bounds.size.height;
|
||||
CGSize size = CGSizeMake(h, h);
|
||||
id item = _tabs[indexPath.item];
|
||||
if ([item isKindOfClass:NSString.class]) {
|
||||
NSAttributedString *text = [self getAttributedString:_tabs[indexPath.item] selected:YES];
|
||||
CGSize textSize = [text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, _collectionView.bounds.size.height)
|
||||
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
|
||||
context:nil]
|
||||
.size;
|
||||
size.width = MAX(size.width, textSize.width + 10);
|
||||
size.height = MAX(size.height, textSize.height + 8);
|
||||
return size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
- (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return _tabs.count;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - TUIMultimediaTabBarCell
|
||||
@interface TUIMultimediaTabBarCell () {
|
||||
UILabel *_label;
|
||||
UIImageView *_imgView;
|
||||
}
|
||||
@end
|
||||
@implementation TUIMultimediaTabBarCell
|
||||
+ (NSString *)reuseIdentifier {
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
self.contentView.layer.cornerRadius = 5;
|
||||
self.contentView.clipsToBounds = YES;
|
||||
|
||||
_label = [[UILabel alloc] init];
|
||||
[self.contentView addSubview:_label];
|
||||
_label.textAlignment = NSTextAlignmentCenter;
|
||||
[_label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
|
||||
_imgView = [[UIImageView alloc] init];
|
||||
[self.contentView addSubview:_imgView];
|
||||
_imgView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[_imgView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
self.attributedText = nil;
|
||||
self.icon = nil;
|
||||
self.barCellSelected = NO;
|
||||
self.padding = 0;
|
||||
}
|
||||
- (NSAttributedString *)attributedText {
|
||||
return _label.attributedText;
|
||||
}
|
||||
- (UIImage *)icon {
|
||||
return _imgView.image;
|
||||
}
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText {
|
||||
_label.attributedText = attributedText;
|
||||
}
|
||||
- (void)setIcon:(UIImage *)icon {
|
||||
_imgView.image = icon;
|
||||
}
|
||||
- (void)setPadding:(CGFloat)padding {
|
||||
_padding = padding;
|
||||
[_label mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self).inset(_padding);
|
||||
}];
|
||||
[_imgView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self).inset(_padding);
|
||||
}];
|
||||
}
|
||||
@end
|
||||
Reference in New Issue
Block a user