增加换肤功能
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBeautifySettings.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPopupController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIMultimediaBeautifyControllerDelegate;
|
||||
|
||||
@interface TUIMultimediaBeautifyController : TUIMultimediaPopupController
|
||||
@property(readonly, nonatomic) TUIMultimediaBeautifySettings *settings;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaBeautifyControllerDelegate> delegate;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaBeautifyControllerDelegate <NSObject>
|
||||
- (void)beautifyControllerOnExit:(TUIMultimediaBeautifyController *)controller;
|
||||
- (void)beautifyController:(TUIMultimediaBeautifyController *)controller onSettingsChange:(TUIMultimediaBeautifySettings *)settings;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaBeautifyController.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBeautifyView.h"
|
||||
|
||||
@interface TUIMultimediaBeautifyController () <TUIMultimediaBeautifyViewDelegate> {
|
||||
TUIMultimediaBeautifyView *_beautifyView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaBeautifyController
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_settings = [[TUIMultimediaBeautifySettings alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
_beautifyView = [[TUIMultimediaBeautifyView alloc] init];
|
||||
_beautifyView.settings = _settings;
|
||||
_beautifyView.delegate = self;
|
||||
|
||||
self.mainView = _beautifyView;
|
||||
}
|
||||
|
||||
- (void)popupControllerDidCanceled {
|
||||
[_delegate beautifyControllerOnExit:self];
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaBeautifyViewDelegate protocol
|
||||
- (void)beautifyView:(TUIMultimediaBeautifyView *)beautifyView onSettingsChange:(TUIMultimediaBeautifySettings *)settings {
|
||||
[_delegate beautifyController:self onSettingsChange:settings];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <TXLiteAVSDK_Professional/TXUGCRecord.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaEncodeConfig.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^TUIMultimediaRecordResultCallback)(NSString *_Nullable videoPath, UIImage *_Nullable image);
|
||||
|
||||
@interface TUIMultimediaRecordController : UIViewController
|
||||
@property(nullable, nonatomic) TUIMultimediaRecordResultCallback resultCallback;
|
||||
@property(nonatomic) BOOL isOnlySupportTakePhoto;
|
||||
- (instancetype)init;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,301 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaRecordController.h"
|
||||
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TUICore/NSDictionary+TUISafe.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import <TXLiteAVSDK_Professional/TXUGCRecord.h>
|
||||
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBeautifyController.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaRecordControlView.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaAuthorizationPrompter.h"
|
||||
|
||||
@interface TUIMultimediaRecordController () <TUIMultimediaRecordControlViewDelegate, TXUGCRecordListener, TUIMultimediaBeautifyControllerDelegate> {
|
||||
TUIMultimediaBeautifyController *_beautifyController;
|
||||
TUIMultimediaRecordControlView *_ctrlView;
|
||||
BOOL _originNavgationBarHidden;
|
||||
float recordDuration;
|
||||
CGFloat _zoom;
|
||||
TUIMultimediaEncodeConfig* _encodeConfig;
|
||||
BOOL _recordForEdit;
|
||||
float _minDurationSeconds;
|
||||
float _maxDurationSeconds;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaRecordController
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_minDurationSeconds = [[TUIMultimediaConfig sharedInstance] getMinRecordDurationMs] / 1000.0f;
|
||||
_maxDurationSeconds = [[TUIMultimediaConfig sharedInstance] getMaxRecordDurationMs] / 1000.0f;
|
||||
_encodeConfig = [[TUIMultimediaEncodeConfig alloc] initWithVideoQuality:[[TUIMultimediaConfig sharedInstance] getVideoQuality]];
|
||||
_zoom = 1.0;
|
||||
_recordForEdit = YES;
|
||||
_isOnlySupportTakePhoto = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[self initUI];
|
||||
}
|
||||
|
||||
#pragma mark - UI Init
|
||||
|
||||
- (void)initUI {
|
||||
self.view.backgroundColor = UIColor.blackColor;
|
||||
|
||||
_ctrlView = [[TUIMultimediaRecordControlView alloc] initWithFrame:self.view.bounds isOnlySupportTakePhoto:_isOnlySupportTakePhoto];
|
||||
_ctrlView.delegate = self;
|
||||
[self.view addSubview:_ctrlView];
|
||||
|
||||
_beautifyController = [[TUIMultimediaBeautifyController alloc] init];
|
||||
_beautifyController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
_beautifyController.delegate = self;
|
||||
|
||||
[self startPreview];
|
||||
|
||||
UIPinchGestureRecognizer *pinchRec = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinch:)];
|
||||
[_ctrlView addGestureRecognizer:pinchRec];
|
||||
}
|
||||
|
||||
- (void)startPreview {
|
||||
TXUGCCustomConfig *param = [[TXUGCCustomConfig alloc] init];
|
||||
param.minDuration = _minDurationSeconds;
|
||||
param.maxDuration = _maxDurationSeconds;
|
||||
param.videoResolution = [_encodeConfig getVideoRecordResolution];
|
||||
param.videoBitratePIN = _encodeConfig.bitrate;
|
||||
param.videoFPS = _encodeConfig.fps;
|
||||
if (_recordForEdit) {
|
||||
param.videoBitratePIN = TUIMultimediaEditBitrate;
|
||||
}
|
||||
[self recordControlViewOnAspectChange:_ctrlView.aspectRatio];
|
||||
[TXUGCRecord.shareInstance startCameraCustom:param preview:_ctrlView.previewView];
|
||||
[TXUGCRecord.shareInstance switchCamera:_ctrlView.isUsingFrontCamera];
|
||||
[self beautifyController:_beautifyController onSettingsChange:_beautifyController.settings];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[self startPreview];
|
||||
if (self.navigationController != nil) {
|
||||
_originNavgationBarHidden = self.navigationController.navigationBarHidden;
|
||||
self.navigationController.navigationBarHidden = YES;
|
||||
}
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(appDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(appWillResignActive) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[TXUGCRecord.shareInstance stopCameraPreview];
|
||||
if (self.navigationController != nil) {
|
||||
self.navigationController.navigationBarHidden = _originNavgationBarHidden;
|
||||
}
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)appDidBecomeActive {
|
||||
[self startPreview];
|
||||
}
|
||||
- (void)appWillResignActive {
|
||||
[TXUGCRecord.shareInstance stopCameraPreview];
|
||||
}
|
||||
|
||||
- (void)presentSimpleAlertWithTitle:(NSString *)title message:(NSString *)message onOk:(void (^)(void))onOk {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
if (onOk != nil) {
|
||||
onOk();
|
||||
}
|
||||
}];
|
||||
[alert addAction:defaultAction];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)takePhoto {
|
||||
int code = [TXUGCRecord.shareInstance snapshot:^(UIImage *image) {
|
||||
if (self->_resultCallback != nil) {
|
||||
self->_resultCallback(nil, image);
|
||||
}
|
||||
}];
|
||||
if (code != 0) {
|
||||
[self presentSimpleAlertWithTitle:[TUIMultimediaCommon localizedStringForKey:@"photo_failed"] message:@"" onOk:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIPinchGestureRecognizer
|
||||
- (void)onPinch:(UIPinchGestureRecognizer *)rec {
|
||||
_zoom = MIN(MAX(_zoom * rec.scale, 1.0), 5.0);
|
||||
rec.scale = 1;
|
||||
[TXUGCRecord.shareInstance setZoom:_zoom];
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaRecordControlViewDelegate protocol
|
||||
- (void)recordControlViewOnAspectChange:(TUIMultimediaRecordAspectRatio)aspect {
|
||||
switch (_ctrlView.aspectRatio) {
|
||||
case TUIMultimediaRecordAspectRatio3_4: {
|
||||
[TXUGCRecord.shareInstance setAspectRatio:VIDEO_ASPECT_RATIO_3_4];
|
||||
break;
|
||||
}
|
||||
case TUIMultimediaRecordAspectRatio9_16: {
|
||||
[TXUGCRecord.shareInstance setAspectRatio:VIDEO_ASPECT_RATIO_9_16];
|
||||
break;
|
||||
}
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)recordControlViewOnCameraSwicth:(BOOL)isUsingFrontCamera {
|
||||
[TXUGCRecord.shareInstance switchCamera:isUsingFrontCamera];
|
||||
}
|
||||
|
||||
- (void)recordControlViewOnExit {
|
||||
[TXUGCRecord.shareInstance stopCameraPreview];
|
||||
if (_resultCallback != nil) {
|
||||
_resultCallback(nil, nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)recordControlViewOnFlashStateChange:(BOOL)flashState {
|
||||
[TXUGCRecord.shareInstance toggleTorch:flashState];
|
||||
}
|
||||
|
||||
- (void)recordControlViewOnRecordStart {
|
||||
TXUGCRecord.shareInstance.recordDelegate = self;
|
||||
int res = [TXUGCRecord.shareInstance startRecord];
|
||||
if (res == 0) {
|
||||
return;
|
||||
}
|
||||
NSString *title = [TUIMultimediaCommon localizedStringForKey:@"record_failed"];
|
||||
NSString *msg;
|
||||
switch (res) {
|
||||
case -3:
|
||||
msg = [TUIMultimediaCommon localizedStringForKey:@"record_failed_reason_camera"];
|
||||
break;
|
||||
case -4:
|
||||
msg = [TUIMultimediaCommon localizedStringForKey:@"record_failed_reason_microphone"];
|
||||
break;
|
||||
case -5:
|
||||
msg = [TUIMultimediaCommon localizedStringForKey:@"record_failed_reason_license"];
|
||||
break;
|
||||
default:
|
||||
msg = [TUIMultimediaCommon localizedStringForKey:@"record_failed_reason_unknown"];
|
||||
break;
|
||||
}
|
||||
[self presentSimpleAlertWithTitle:title message:msg onOk:nil];
|
||||
}
|
||||
|
||||
- (void)recordControlViewOnRecordFinish {
|
||||
[TXUGCRecord.shareInstance stopRecord];
|
||||
[TXUGCRecord.shareInstance stopCameraPreview];
|
||||
}
|
||||
|
||||
- (void)recordControlViewPhoto {
|
||||
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:self]) {
|
||||
return;
|
||||
}
|
||||
[self takePhoto];
|
||||
}
|
||||
|
||||
- (void)recordControlViewOnBeautify {
|
||||
_ctrlView.recordTipHidden = YES;
|
||||
[self.navigationController presentViewController:_beautifyController animated:NO completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - TXUGCRecordListener protocol
|
||||
- (void)onRecordProgress:(NSInteger)milliSecond {
|
||||
recordDuration = milliSecond / 1000.0;
|
||||
[_ctrlView setProgress:milliSecond / 1000.0 / _maxDurationSeconds duration:recordDuration];
|
||||
if (milliSecond / 1000.0 >= _maxDurationSeconds) {
|
||||
[TXUGCRecord.shareInstance stopRecord];
|
||||
[TXUGCRecord.shareInstance stopCameraPreview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRecordComplete:(TXUGCRecordResult *)result {
|
||||
[TXUGCRecord.shareInstance.partsManager deleteAllParts];
|
||||
if (result.retCode == UGC_RECORD_RESULT_FAILED) {
|
||||
NSString *title = [TUIMultimediaCommon localizedStringForKey:@"record_failed"];
|
||||
[self presentSimpleAlertWithTitle:title
|
||||
message:@""
|
||||
onOk:^{
|
||||
[self startPreview];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
// 未达到最小录制时长
|
||||
if (recordDuration < _minDurationSeconds) {
|
||||
[self takePhoto];
|
||||
return;
|
||||
}
|
||||
if (self->_resultCallback != nil) {
|
||||
self->_resultCallback(result.videoPath, nil);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaBeautifyControllerDelegate protocol
|
||||
- (void)beautifyControllerOnExit:(TUIMultimediaBeautifyController *)controller {
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
_ctrlView.recordTipHidden = NO;
|
||||
}
|
||||
|
||||
- (void)beautifyController:(TUIMultimediaBeautifyController *)controller onSettingsChange:(TUIMultimediaBeautifySettings *)settings {
|
||||
TXBeautyManager *manager = [TXUGCRecord.shareInstance getBeautyManager];
|
||||
[manager setBeautyStyle:TXBeautyStyleSmooth];
|
||||
__auto_type convertStrength = ^(int strength, float min, float max) {
|
||||
float ratio = ((float)strength - TUIMultimediaEffectSliderMin) / (TUIMultimediaEffectSliderMax - TUIMultimediaEffectSliderMin);
|
||||
return ratio * (max - min) + min;
|
||||
};
|
||||
__auto_type convertBeautifyStrength = ^(int strength) {
|
||||
return convertStrength(strength, TUIMultimediaBeautifyStrengthMin, TUIMultimediaBeautifyStrengthMax);
|
||||
};
|
||||
__auto_type convertFilterStrength = ^(int strength) {
|
||||
return convertStrength(strength, TUIMultimediaFilterStrengthMin, TUIMultimediaFilterStrengthMax);
|
||||
};
|
||||
if (settings.activeBeautifyTag == TUIMultimediaEffectItemTagNone) {
|
||||
[manager setBeautyLevel:0];
|
||||
}
|
||||
for (TUIMultimediaEffectItem *item in settings.beautifyItems) {
|
||||
float strength = convertBeautifyStrength(item.strength);
|
||||
if (settings.activeBeautifyTag == item.tag) {
|
||||
switch (item.tag) {
|
||||
case TUIMultimediaEffectItemTagSmooth:
|
||||
[manager setBeautyStyle:TXBeautyStyleSmooth];
|
||||
break;
|
||||
case TUIMultimediaEffectItemTagNatural:
|
||||
[manager setBeautyStyle:TXBeautyStyleNature];
|
||||
break;
|
||||
case TUIMultimediaEffectItemTagPitu:
|
||||
[manager setBeautyStyle:TXBeautyStylePitu];
|
||||
break;
|
||||
}
|
||||
[manager setBeautyLevel:strength];
|
||||
}
|
||||
switch (item.tag) {
|
||||
case TUIMultimediaEffectItemTagWhiteness:
|
||||
[manager setWhitenessLevel:strength];
|
||||
break;
|
||||
case TUIMultimediaEffectItemTagRuddy:
|
||||
[manager setRuddyLevel:strength];
|
||||
break;
|
||||
}
|
||||
}
|
||||
NSInteger idx = settings.activeFilterIndex;
|
||||
if (idx >= 0 && idx < settings.filterItems.count) {
|
||||
float strength = convertFilterStrength(settings.filterItems[idx].strength);
|
||||
[manager setFilter:settings.filterItems[idx].filterMapImage];
|
||||
[manager setFilterStrength:strength];
|
||||
} else {
|
||||
[manager setFilter:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMultimediaEffectCell : UICollectionViewCell
|
||||
@property(nonatomic) UIImage *image;
|
||||
@property(nonatomic) NSString *text;
|
||||
@property(nonatomic) BOOL effectSelected;
|
||||
@property(nonatomic) CGSize iconSize;
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
|
||||
+ (NSString *)reuseIdentifier;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaEffectCell.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
|
||||
const CGFloat MaskBorderWidth = 2;
|
||||
const CGFloat EffectCellRadius = 10;
|
||||
const CGFloat EffectCellWidth = 60;
|
||||
|
||||
const CGFloat LabelFontSize = 12;
|
||||
const CGFloat LabelHeight = 18;
|
||||
const CGFloat LabelInsectToBottom = 5;
|
||||
|
||||
@interface TUIMultimediaEffectCell () {
|
||||
UIView *_imgContainerView;
|
||||
UIView *_highlightView;
|
||||
UIImageView *_imgView;
|
||||
UILabel *_label;
|
||||
NSString *_text;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaEffectCell
|
||||
|
||||
@dynamic image;
|
||||
@dynamic text;
|
||||
|
||||
+ (NSString *)reuseIdentifier {
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
_imgContainerView = [[UIView alloc] init];
|
||||
[self.contentView addSubview:_imgContainerView];
|
||||
_imgContainerView.layer.cornerRadius = EffectCellRadius;
|
||||
_imgContainerView.clipsToBounds = YES;
|
||||
_imgView = [[UIImageView alloc] init];
|
||||
[_imgContainerView addSubview:_imgView];
|
||||
_imgView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[_imgView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.size.mas_equalTo(_iconSize);
|
||||
make.center.equalTo(_imgContainerView);
|
||||
}];
|
||||
|
||||
_highlightView = [[UIView alloc] init];
|
||||
[self.contentView addSubview:_highlightView];
|
||||
_highlightView.hidden = YES;
|
||||
_highlightView.backgroundColor = [[TUIMultimediaConfig sharedInstance] getThemeColor];
|
||||
|
||||
_label = [[UILabel alloc] init];
|
||||
_label.font = [UIFont systemFontOfSize:LabelFontSize];
|
||||
_label.textColor = UIColor.whiteColor;
|
||||
_label.textAlignment = NSTextAlignmentCenter;
|
||||
[self.contentView addSubview:_label];
|
||||
|
||||
[_highlightView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.contentView);
|
||||
}];
|
||||
[_imgContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.top.equalTo(self.contentView).inset(MaskBorderWidth);
|
||||
make.height.equalTo(_imgContainerView.mas_width);
|
||||
}];
|
||||
[_label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.contentView);
|
||||
make.bottom.equalTo(self.contentView);
|
||||
make.top.equalTo(_imgContainerView.mas_bottom);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
[self addMaskToHighlightView];
|
||||
}
|
||||
|
||||
- (void)addMaskToHighlightView {
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_highlightView.bounds cornerRadius:EffectCellRadius];
|
||||
[maskPath appendPath:[UIBezierPath bezierPathWithRoundedRect:_imgContainerView.frame cornerRadius:EffectCellRadius].bezierPathByReversingPath];
|
||||
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
|
||||
layer.path = maskPath.CGPath;
|
||||
_highlightView.layer.mask = layer;
|
||||
}
|
||||
|
||||
- (void)setIconSize:(CGSize)iconSize {
|
||||
_iconSize = iconSize;
|
||||
[_imgView mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.size.mas_equalTo(_iconSize);
|
||||
}];
|
||||
}
|
||||
|
||||
- (UIImage *)image {
|
||||
return _imgView.image;
|
||||
}
|
||||
- (void)setImage:(UIImage *)image {
|
||||
_imgView.image = image;
|
||||
}
|
||||
- (NSString *)text {
|
||||
return _text;
|
||||
}
|
||||
- (void)setText:(NSString *)text {
|
||||
_text = text;
|
||||
_label.text = text;
|
||||
}
|
||||
- (void)setEffectSelected:(BOOL)effectSelected {
|
||||
_effectSelected = effectSelected;
|
||||
_highlightView.hidden = !effectSelected;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaEffectCell.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaEffectItem.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIMultimediaEffectPanelDelegate;
|
||||
|
||||
/**
|
||||
美颜和滤镜设置界面
|
||||
*/
|
||||
@interface TUIMultimediaEffectPanel : UIView
|
||||
@property(nonatomic) NSArray<TUIMultimediaEffectItem *> *items;
|
||||
@property(nonatomic) NSInteger selectedIndex;
|
||||
@property(nonatomic) CGSize iconSize;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaEffectPanelDelegate> delegate;
|
||||
- (id)initWithFrame:(CGRect)frame;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaEffectPanelDelegate <NSObject>
|
||||
- (void)effectPanelSelectionChanged:(TUIMultimediaEffectPanel *)panel;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaEffectPanel.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "TUIMultimediaEffectCell.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBeautifySettings.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
|
||||
static const CGSize EffectItemSize = TUIMultimediaConstCGSize(60, 86);
|
||||
|
||||
@interface TUIMultimediaEffectPanel () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> {
|
||||
NSArray<TUIMultimediaEffectItem *> *_items;
|
||||
UICollectionView *_collectionView;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaEffectPanel
|
||||
@dynamic items;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
_selectedIndex = -1;
|
||||
_items = @[];
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
layout.minimumInteritemSpacing = 10;
|
||||
layout.itemSize = EffectItemSize;
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
_collectionView.backgroundColor = UIColor.clearColor;
|
||||
_collectionView.showsHorizontalScrollIndicator = NO;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.dataSource = self;
|
||||
[_collectionView registerClass:TUIMultimediaEffectCell.class forCellWithReuseIdentifier:TUIMultimediaEffectCell.reuseIdentifier];
|
||||
[self addSubview:_collectionView];
|
||||
|
||||
[_collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize {
|
||||
return CGSizeMake(UIViewNoIntrinsicMetric, EffectItemSize.height /* + self.safeAreaInsets.bottom*/);
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
- (NSArray<TUIMultimediaEffectItem *> *)items {
|
||||
return _items;
|
||||
}
|
||||
- (void)setItems:(NSArray<TUIMultimediaEffectItem *> *)value {
|
||||
_items = value;
|
||||
_selectedIndex = -1;
|
||||
[_collectionView reloadData];
|
||||
}
|
||||
- (void)setSelectedIndex:(NSInteger)selectedIndex {
|
||||
_selectedIndex = selectedIndex;
|
||||
[_collectionView reloadData];
|
||||
}
|
||||
- (void)setIconSize:(CGSize)iconSize {
|
||||
_iconSize = iconSize;
|
||||
[_collectionView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDelegateFlowLayout protocol
|
||||
|
||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIMultimediaEffectCell *cell = (TUIMultimediaEffectCell *)[collectionView dequeueReusableCellWithReuseIdentifier:TUIMultimediaEffectCell.reuseIdentifier
|
||||
forIndexPath:indexPath];
|
||||
cell.image = _items[indexPath.item].iconImage;
|
||||
cell.text = _items[indexPath.item].name;
|
||||
cell.effectSelected = (indexPath.item == _selectedIndex);
|
||||
cell.iconSize = _iconSize;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return _items.count;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// 立刻反选此cell,不使用CollectionView自带的选中逻辑
|
||||
[collectionView deselectItemAtIndexPath:indexPath animated:NO];
|
||||
_selectedIndex = indexPath.item;
|
||||
[collectionView reloadData];
|
||||
|
||||
[_delegate effectPanelSelectionChanged:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBeautifySettings.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIMultimediaBeautifyViewDelegate;
|
||||
|
||||
@interface TUIMultimediaBeautifyView : UIView
|
||||
@property(nonatomic) TUIMultimediaBeautifySettings *settings;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaBeautifyViewDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
- (instancetype)initWithFrame:(CGRect)frame settings:(nullable TUIMultimediaBeautifySettings *)settings;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaBeautifyViewDelegate <NSObject>
|
||||
- (void)beautifyView:(TUIMultimediaBeautifyView *)beautifyView onSettingsChange:(TUIMultimediaBeautifySettings *)settings;
|
||||
@end
|
||||
|
||||
@interface TUIMultimediaMarker : UIView
|
||||
@property(nonatomic) NSString *text;
|
||||
- (void)showForDuration:(CGFloat)showSeconds withHideAnimeDuration:(CGFloat)hideAnimeSeconds;
|
||||
- (void)hide;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,259 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaBeautifyView.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <ReactiveObjC/RACEXTScope.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaEffectCell.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaEffectPanel.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaImageUtil.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaTabPanel.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
|
||||
#pragma mark - TUIMultimediaBeautifyView
|
||||
@interface TUIMultimediaBeautifyView () <TUIMultimediaEffectPanelDelegate, TUIMultimediaTabPanelDelegate> {
|
||||
UIView *_topPanel;
|
||||
UILabel *_lbStrength;
|
||||
UIImageView *_imgViewCompare;
|
||||
UISlider *_slider;
|
||||
TUIMultimediaTabPanel *_tabPanel;
|
||||
TUIMultimediaEffectPanel *_effectPanelBeauty;
|
||||
TUIMultimediaEffectPanel *_effectPanelFilter;
|
||||
TUIMultimediaEffectItem *_selectedItem;
|
||||
TUIMultimediaMarker *_marker;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaBeautifyView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
return [self initWithFrame:frame settings:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame settings:(TUIMultimediaBeautifySettings *)settings {
|
||||
self = [super initWithFrame:frame];
|
||||
_settings = settings;
|
||||
if (_settings == nil) {
|
||||
_settings = [[TUIMultimediaBeautifySettings alloc] init];
|
||||
}
|
||||
[self initUI];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
self.opaque = NO;
|
||||
self.backgroundColor = UIColor.clearColor;
|
||||
|
||||
_topPanel = [[UIView alloc] init];
|
||||
_topPanel.hidden = YES;
|
||||
[self addSubview:_topPanel];
|
||||
[_topPanel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.top.equalTo(self);
|
||||
make.height.mas_equalTo(52);
|
||||
}];
|
||||
|
||||
_marker = [[TUIMultimediaMarker alloc] init];
|
||||
[_topPanel addSubview:_marker];
|
||||
|
||||
_lbStrength = [[UILabel alloc] init];
|
||||
[_topPanel addSubview:_lbStrength];
|
||||
_lbStrength.text = [TUIMultimediaCommon localizedStringForKey:@"strength"];
|
||||
_lbStrength.textColor = UIColor.whiteColor;
|
||||
_lbStrength.font = [UIFont systemFontOfSize:16];
|
||||
|
||||
_imgViewCompare = [[UIImageView alloc] init];
|
||||
[_topPanel addSubview:_imgViewCompare];
|
||||
_imgViewCompare.image = TUIMultimediaPluginBundleThemeImage(@"beautify_effect_compare_img", @"effect_compare");
|
||||
|
||||
UIColor* themeColor = [[TUIMultimediaConfig sharedInstance] getThemeColor];
|
||||
_slider = [[UISlider alloc] init];
|
||||
[_topPanel addSubview:_slider];
|
||||
//_slider.thumbTintColor = TUIMultimediaPluginDynamicColor(@"theme_accent_dark_color", @"#006CFF");
|
||||
_slider.minimumValue = TUIMultimediaEffectSliderMin;
|
||||
_slider.maximumValue = TUIMultimediaEffectSliderMax;
|
||||
_slider.minimumTrackTintColor = themeColor;//TUIMultimediaPluginDynamicColor(@"theme_accent_dark_color", @"#006CFF");
|
||||
_slider.maximumTrackTintColor = TUIMultimediaPluginDynamicColor(@"beautify_slider_track_bg_color", @"#F4F5F9");
|
||||
UIImage *thumbImg = [TUIMultimediaImageUtil createBlueCircleWithWhiteBorder:CGSizeMake(24, 24) withColor:themeColor];
|
||||
[_slider setThumbImage:thumbImg forState:UIControlStateNormal];
|
||||
[_slider setThumbImage:thumbImg forState:UIControlStateHighlighted];
|
||||
[_slider addTarget:self action:@selector(onSliderValueChange) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[_lbStrength mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_topPanel).inset(16);
|
||||
make.centerY.equalTo(_topPanel);
|
||||
}];
|
||||
[_imgViewCompare mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(_topPanel).inset(16);
|
||||
make.centerY.equalTo(_topPanel);
|
||||
make.width.height.mas_equalTo(30);
|
||||
}];
|
||||
[_slider mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_lbStrength.mas_right).inset(8);
|
||||
make.right.equalTo(_imgViewCompare.mas_left).inset(8);
|
||||
make.centerY.equalTo(_topPanel);
|
||||
make.height.mas_equalTo(30);
|
||||
}];
|
||||
|
||||
_effectPanelBeauty = [[TUIMultimediaEffectPanel alloc] init];
|
||||
_effectPanelBeauty.iconSize = CGSizeMake(32, 32);
|
||||
_effectPanelBeauty.delegate = self;
|
||||
_effectPanelBeauty.items = _settings.beautifyItems;
|
||||
|
||||
_effectPanelFilter = [[TUIMultimediaEffectPanel alloc] init];
|
||||
_effectPanelFilter.iconSize = CGSizeMake(56, 56);
|
||||
_effectPanelFilter.delegate = self;
|
||||
_effectPanelFilter.items = _settings.filterItems;
|
||||
_effectPanelFilter.selectedIndex = 0;
|
||||
|
||||
UIView *bottomPanel = [[UIView alloc] init];
|
||||
[self addSubview:bottomPanel];
|
||||
bottomPanel.backgroundColor = UIColor.blackColor;
|
||||
|
||||
_tabPanel = [[TUIMultimediaTabPanel alloc] initWithFrame:self.bounds];
|
||||
_tabPanel.delegate = self;
|
||||
_tabPanel.backgroundColor = UIColor.blackColor;
|
||||
_tabPanel.tabs = @[
|
||||
[[TUIMultimediaTabPanelTab alloc] initWithName:[TUIMultimediaCommon localizedStringForKey:@"beautify"] icon:nil view:_effectPanelBeauty],
|
||||
[[TUIMultimediaTabPanelTab alloc] initWithName:[TUIMultimediaCommon localizedStringForKey:@"filter"] icon:nil view:_effectPanelFilter],
|
||||
];
|
||||
[bottomPanel addSubview:_tabPanel];
|
||||
|
||||
[bottomPanel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(_topPanel.mas_bottom);
|
||||
make.left.right.bottom.equalTo(self);
|
||||
}];
|
||||
[_tabPanel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.top.equalTo(bottomPanel);
|
||||
make.bottom.equalTo(bottomPanel.mas_safeAreaLayoutGuideBottom);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)effectPanelSelectionChanged:(TUIMultimediaEffectPanel *)panel {
|
||||
[_marker hide];
|
||||
if (panel.selectedIndex == -1 || panel.items[panel.selectedIndex].tag == TUIMultimediaEffectItemTagNone) {
|
||||
panel.selectedIndex = -1;
|
||||
_selectedItem = nil;
|
||||
_topPanel.hidden = YES;
|
||||
if (panel == _effectPanelFilter) {
|
||||
_settings.activeFilterIndex = -1;
|
||||
} else {
|
||||
for (TUIMultimediaEffectItem *item in _settings.beautifyItems) {
|
||||
item.strength = 0;
|
||||
}
|
||||
}
|
||||
[_delegate beautifyView:self onSettingsChange:_settings];
|
||||
return;
|
||||
}
|
||||
_selectedItem = panel.items[panel.selectedIndex];
|
||||
if (panel == _effectPanelFilter) {
|
||||
_settings.activeFilterIndex = panel.selectedIndex;
|
||||
_topPanel.hidden = NO;
|
||||
_slider.value = _selectedItem.strength;
|
||||
[_delegate beautifyView:self onSettingsChange:_settings];
|
||||
} else {
|
||||
_topPanel.hidden = NO;
|
||||
_slider.value = _selectedItem.strength;
|
||||
if ([@[ @(TUIMultimediaEffectItemTagSmooth), @(TUIMultimediaEffectItemTagNatural), @(TUIMultimediaEffectItemTagPitu) ] containsObject:@(_selectedItem.tag)]) {
|
||||
_settings.activeBeautifyTag = _selectedItem.tag;
|
||||
[_delegate beautifyView:self onSettingsChange:_settings];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tabPanel:(TUIMultimediaTabPanel *)panel selectedIndexChanged:(NSInteger)selectedIndex {
|
||||
TUIMultimediaEffectPanel *effectPanel = (TUIMultimediaEffectPanel *)panel.tabs[selectedIndex].view;
|
||||
[self effectPanelSelectionChanged:effectPanel];
|
||||
}
|
||||
|
||||
- (void)onSliderValueChange {
|
||||
_selectedItem.strength = _slider.value;
|
||||
|
||||
_marker.text = [NSString stringWithFormat:@"%d", (int)_slider.value];
|
||||
CGRect trackRect = [_slider trackRectForBounds:_slider.bounds];
|
||||
CGRect thumbRect = [_slider thumbRectForBounds:_slider.bounds trackRect:trackRect value:_slider.value];
|
||||
CGRect rect = [_topPanel convertRect:thumbRect fromView:_slider];
|
||||
[_marker mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.height.mas_equalTo(34);
|
||||
make.centerX.equalTo(_topPanel.mas_left).offset(CGRectGetMidX(rect));
|
||||
make.bottom.equalTo(_topPanel.mas_top).offset(rect.origin.y - 4);
|
||||
}];
|
||||
[_marker showForDuration:0.5 withHideAnimeDuration:0.5];
|
||||
|
||||
[_delegate beautifyView:self onSettingsChange:_settings];
|
||||
}
|
||||
|
||||
- (void)setSettings:(TUIMultimediaBeautifySettings *)settings {
|
||||
_effectPanelBeauty.items = _settings.beautifyItems;
|
||||
_effectPanelFilter.items = _settings.filterItems;
|
||||
_effectPanelFilter.selectedIndex = 0;
|
||||
}
|
||||
@end
|
||||
|
||||
#pragma mark - TUIMultimediaMarker
|
||||
@interface TUIMultimediaMarker () {
|
||||
UIImageView *_imgView;
|
||||
UILabel *_label;
|
||||
dispatch_block_t hideBlock;
|
||||
}
|
||||
@end
|
||||
@implementation TUIMultimediaMarker
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
[self initUI];
|
||||
return self;
|
||||
}
|
||||
- (void)initUI {
|
||||
_imgView = [[UIImageView alloc] init];
|
||||
[self addSubview:_imgView];
|
||||
_imgView.image = TUIMultimediaPluginBundleThemeImage(@"beautify_marker_img", @"marker");
|
||||
[_imgView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
}];
|
||||
|
||||
_label = [[UILabel alloc] init];
|
||||
[self addSubview:_label];
|
||||
_label.textAlignment = NSTextAlignmentCenter;
|
||||
_label.textColor = UIColor.blackColor;
|
||||
_label.font = [UIFont systemFontOfSize:12];
|
||||
[_label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self);
|
||||
make.centerY.equalTo(self);
|
||||
}];
|
||||
}
|
||||
- (NSString *)text {
|
||||
return _label.text;
|
||||
}
|
||||
- (void)setText:(NSString *)text {
|
||||
_label.text = text;
|
||||
}
|
||||
- (void)showForDuration:(CGFloat)showSeconds withHideAnimeDuration:(CGFloat)hideAnimeSeconds {
|
||||
self.alpha = 1;
|
||||
self.hidden = NO;
|
||||
if (hideBlock != nil) {
|
||||
dispatch_block_cancel(hideBlock);
|
||||
}
|
||||
@weakify(self);
|
||||
hideBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{
|
||||
@strongify(self);
|
||||
[UIView animateWithDuration:hideAnimeSeconds
|
||||
animations:^{
|
||||
self.alpha = 0;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
self.alpha = 1;
|
||||
self.hidden = YES;
|
||||
}];
|
||||
});
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(showSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), hideBlock);
|
||||
}
|
||||
- (void)hide {
|
||||
if (hideBlock != nil) {
|
||||
dispatch_block_cancel(hideBlock);
|
||||
}
|
||||
self.alpha = 1;
|
||||
self.hidden = YES;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIMultimediaRecordButtonDelegate;
|
||||
|
||||
/**
|
||||
视频录制按钮
|
||||
*/
|
||||
@interface TUIMultimediaRecordButton : UIView
|
||||
@property(nonatomic) float progress;
|
||||
@property(nonatomic) float dotSizeNormal;
|
||||
@property(nonatomic) float progressSizeNormal;
|
||||
@property(nonatomic) float dotSizePressed;
|
||||
@property(nonatomic) float progressSizePressed;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaRecordButtonDelegate> delegate;
|
||||
@property(nonatomic) BOOL isOnlySupportTakePhoto;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaRecordButtonDelegate <NSObject>
|
||||
- (void)onRecordButtonTap:(TUIMultimediaRecordButton *)btn;
|
||||
- (void)onRecordButtonLongPressBegan:(TUIMultimediaRecordButton *)btn;
|
||||
- (void)onRecordButtonLongPressEnded:(TUIMultimediaRecordButton *)btn;
|
||||
- (void)onRecordButtonLongPressCancelled:(TUIMultimediaRecordButton *)btn;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaRecordButton.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCircleProgressView.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
|
||||
static const CGFloat RecordAnimeDuration = 0.3;
|
||||
|
||||
#pragma mark - TUIMultimediaRecordButton
|
||||
@interface TUIMultimediaRecordButton () {
|
||||
TUIMultimediaCircleProgressView *_progressView;
|
||||
UIView *_dotView;
|
||||
BOOL _pressed;
|
||||
UILongPressGestureRecognizer *_longPressRec;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaRecordButton
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
_progressView = [[TUIMultimediaCircleProgressView alloc] init];
|
||||
_progressView.lineCap = kCALineCapButt;
|
||||
_progressView.progressBgColor = TUIMultimediaPluginDynamicColor(@"record_btn_progress_bg_color", @"#FFFFFF");
|
||||
_progressView.progressColor = [[TUIMultimediaConfig sharedInstance] getThemeColor];
|
||||
_progressView.width = 2;
|
||||
[self addSubview:_progressView];
|
||||
|
||||
_dotView = [[UIView alloc] init];
|
||||
_dotView.backgroundColor = TUIMultimediaPluginDynamicColor(@"record_btn_dot_color", @"#FFFFFF");
|
||||
[self addSubview:_dotView];
|
||||
|
||||
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
|
||||
tapRec.cancelsTouchesInView = NO;
|
||||
[self addGestureRecognizer:tapRec];
|
||||
|
||||
_longPressRec = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
|
||||
_longPressRec.cancelsTouchesInView = NO;
|
||||
_longPressRec.minimumPressDuration = RecordAnimeDuration;
|
||||
[self addGestureRecognizer:_longPressRec];
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
CGFloat dotSize = _pressed ? _dotSizePressed : _dotSizeNormal;
|
||||
|
||||
CGFloat progressSize = _pressed ? _progressSizePressed : _progressSizeNormal;
|
||||
if (_isOnlySupportTakePhoto) {
|
||||
progressSize = _progressSizeNormal;
|
||||
}
|
||||
|
||||
CGPoint center = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2);
|
||||
CGFloat len = MIN(CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
|
||||
_progressView.center = center;
|
||||
_progressView.bounds = CGRectMake(0, 0, len, len);
|
||||
_progressView.transform = CGAffineTransformMakeScale(progressSize / len, progressSize / len);
|
||||
|
||||
_dotView.center = center;
|
||||
_dotView.bounds = CGRectMake(0, 0, dotSize, dotSize);
|
||||
_dotView.layer.cornerRadius = dotSize / 2;
|
||||
}
|
||||
|
||||
- (void)animeStartRecord {
|
||||
_progressView.width = 4;
|
||||
[UIView animateWithDuration:RecordAnimeDuration
|
||||
animations:^{
|
||||
self->_pressed = YES;
|
||||
[self layoutSubviews];
|
||||
}];
|
||||
}
|
||||
- (void)animeStopRecord {
|
||||
_progressView.width = 2;
|
||||
[UIView animateWithDuration:RecordAnimeDuration
|
||||
animations:^{
|
||||
self->_pressed = NO;
|
||||
[self layoutSubviews];
|
||||
}];
|
||||
}
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
[self animeStartRecord];
|
||||
}
|
||||
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
[self animeStopRecord];
|
||||
}
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
[self animeStopRecord];
|
||||
}
|
||||
- (void)onTap:(UITapGestureRecognizer *)rec {
|
||||
[_delegate onRecordButtonTap:self];
|
||||
}
|
||||
- (void)onLongPress:(UILongPressGestureRecognizer *)rec {
|
||||
switch (rec.state) {
|
||||
case UIGestureRecognizerStateBegan: {
|
||||
[_delegate onRecordButtonLongPressBegan:self];
|
||||
break;
|
||||
}
|
||||
case UIGestureRecognizerStateEnded: {
|
||||
[_delegate onRecordButtonLongPressEnded:self];
|
||||
break;
|
||||
}
|
||||
case UIGestureRecognizerStateCancelled: {
|
||||
[self animeStopRecord];
|
||||
[_delegate onRecordButtonLongPressCancelled:self];
|
||||
}
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgress:(float)progress {
|
||||
[_progressView setProgress:progress animated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBeautifySettings.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TUIMultimediaRecordControlView;
|
||||
|
||||
typedef NS_ENUM(NSInteger, TUIMultimediaRecordAspectRatio) {
|
||||
TUIMultimediaRecordAspectRatio1_1,
|
||||
TUIMultimediaRecordAspectRatio3_4,
|
||||
TUIMultimediaRecordAspectRatio4_3,
|
||||
TUIMultimediaRecordAspectRatio9_16,
|
||||
TUIMultimediaRecordAspectRatio16_9,
|
||||
};
|
||||
typedef void (^TUIMultimediaRecordControlCallback)(TUIMultimediaRecordControlView *);
|
||||
|
||||
@protocol TUIMultimediaRecordControlViewDelegate <NSObject>
|
||||
- (void)recordControlViewOnRecordStart;
|
||||
- (void)recordControlViewOnRecordFinish;
|
||||
- (void)recordControlViewPhoto;
|
||||
- (void)recordControlViewOnFlashStateChange:(BOOL)flashState;
|
||||
- (void)recordControlViewOnAspectChange:(TUIMultimediaRecordAspectRatio)aspect;
|
||||
- (void)recordControlViewOnExit;
|
||||
- (void)recordControlViewOnCameraSwicth:(BOOL)isUsingFrontCamera;
|
||||
- (void)recordControlViewOnBeautify;
|
||||
@end
|
||||
|
||||
/**
|
||||
包含视频录制页面的各种控制按钮
|
||||
*/
|
||||
@interface TUIMultimediaRecordControlView : UIView
|
||||
@property(nonatomic) BOOL flashState;
|
||||
@property(nonatomic) TUIMultimediaRecordAspectRatio aspectRatio;
|
||||
@property(nonatomic) BOOL isUsingFrontCamera;
|
||||
@property(readonly, nonatomic) UIView *previewView;
|
||||
@property(nonatomic) TUIMultimediaBeautifySettings *beautifySettings;
|
||||
@property(nonatomic) BOOL recordTipHidden;
|
||||
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaRecordControlViewDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame isOnlySupportTakePhoto:(BOOL)isOnlySupportTakePhoto;
|
||||
- (instancetype)initWithFrame:(CGRect)frame isOnlySupportTakePhoto:(BOOL)isOnlySupportTakePhoto beautifySettings:(TUIMultimediaBeautifySettings *)beautifySettings;
|
||||
|
||||
- (void)setProgress:(float)progress duration:(float)duration;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,408 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaRecordControlView.h"
|
||||
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TUICore/NSDictionary+TUISafe.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaIconLabelButton.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaRecordButton.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaAuthorizationPrompter.h"
|
||||
|
||||
#pragma mark - UI relative constants
|
||||
const static CGFloat BtnStartRecordSize = 72;
|
||||
const static CGFloat BtnStartRecordGapBottom = 15;
|
||||
const static CGFloat BtnStartRecordDotSizeNormal = 56;
|
||||
const static CGFloat BtnStartRecordDotSizePressed = 20;
|
||||
const static CGFloat BtnStartRecordProgressSizeNormal = 72;
|
||||
const static CGFloat BtnStartRecordProgressSizePressed = 80;
|
||||
|
||||
const static CGSize BtnExitRecordSize = TUIMultimediaConstCGSize(28, 28);
|
||||
const static CGFloat BtnExitRecordGapToStartRecord = 55;
|
||||
|
||||
const static CGSize BtnCameraSwitch = TUIMultimediaConstCGSize(28, 28);
|
||||
const static CGFloat BtnCameraSwitchGapToStartRecord = 55;
|
||||
|
||||
const static CGFloat FunctionBtnToToTop = 64;
|
||||
const static CGFloat FunctionBtnToRight = 16;
|
||||
|
||||
const static CGSize BtnExtendFunctionIconSize = TUIMultimediaConstCGSize(28, 28);
|
||||
const static CGFloat BtnExtendFunctionIconTextGap = 4;
|
||||
const static CGFloat BtnExtendFunctionGap = 24;
|
||||
|
||||
const static BOOL ShowDurationLabel = YES;
|
||||
|
||||
#pragma mark - TUIMultimediaRecordControlView
|
||||
|
||||
@interface TUIMultimediaRecordControlView () <TUIMultimediaRecordButtonDelegate> {
|
||||
TUIMultimediaRecordButton *_btnRecord;
|
||||
UIButton *_btnExitRecord;
|
||||
UIButton *_btnCameraSwitch;
|
||||
UIButton *_btnFlash;
|
||||
UIButton *_btnBeautify;
|
||||
UIButton *_btnAspect;
|
||||
UIButton *_lastFuncitonBtn;
|
||||
UILabel *_lbDuration;
|
||||
UILabel *_lbTip;
|
||||
BOOL _isOnlySupportTakePhoto;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaRecordControlView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame isOnlySupportTakePhoto:(BOOL)isOnlySupportTakePhoto {
|
||||
return [self initWithFrame:frame isOnlySupportTakePhoto:isOnlySupportTakePhoto beautifySettings:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame isOnlySupportTakePhoto:(BOOL) isOnlySupportTakePhoto beautifySettings:(TUIMultimediaBeautifySettings *)beautifySettings {
|
||||
self = [super initWithFrame:frame];
|
||||
_isUsingFrontCamera = NO;
|
||||
_flashState = NO;
|
||||
_aspectRatio = TUIMultimediaRecordAspectRatio9_16;
|
||||
_beautifySettings = beautifySettings;
|
||||
_isOnlySupportTakePhoto = isOnlySupportTakePhoto;
|
||||
if (_beautifySettings == nil) {
|
||||
_beautifySettings = [[TUIMultimediaBeautifySettings alloc] init];
|
||||
}
|
||||
[self initUI];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setProgress:(float)progress duration:(float)duration {
|
||||
[_btnRecord setProgress:progress];
|
||||
int m = floor(duration / 60);
|
||||
int s = floor(duration - m * 60);
|
||||
_lbDuration.text = [NSString stringWithFormat:@"%02d:%02d", m, s];
|
||||
}
|
||||
#pragma mark - UI Init
|
||||
|
||||
- (void)initUI {
|
||||
_previewView = [[UIView alloc] init];
|
||||
[self addSubview:_previewView];
|
||||
[_previewView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self);
|
||||
make.width.equalTo(self);
|
||||
make.height.equalTo(_previewView.mas_width).multipliedBy(16.0 / 9.0);
|
||||
}];
|
||||
|
||||
// 防止 _lbDuration 直接与 _previewView 部分重叠
|
||||
UIView *_bottomMaskView = [[UIView alloc] init];
|
||||
[self addSubview:_bottomMaskView];
|
||||
_bottomMaskView.backgroundColor = UIColor.blackColor;
|
||||
|
||||
[self initControlButtons];
|
||||
[self initFunctionButtons];
|
||||
|
||||
_lbDuration = [[UILabel alloc] init];
|
||||
[self addSubview:_lbDuration];
|
||||
_lbDuration.hidden = YES;
|
||||
_lbDuration.text = @"00:00";
|
||||
_lbDuration.font = [UIFont monospacedSystemFontOfSize:18 weight:UIFontWeightMedium];
|
||||
_lbDuration.textColor = UIColor.whiteColor;
|
||||
[_lbDuration mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.bottom.equalTo(_btnRecord.mas_top).inset(8);
|
||||
make.centerX.equalTo(_btnRecord);
|
||||
}];
|
||||
|
||||
[_bottomMaskView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.bottom.equalTo(self);
|
||||
make.top.equalTo(_lbDuration.mas_top).offset(-8);
|
||||
}];
|
||||
|
||||
_lbTip = [[UILabel alloc] init];
|
||||
[self addSubview:_lbTip];
|
||||
if (_isOnlySupportTakePhoto) {
|
||||
_lbTip.text = [TUIMultimediaCommon localizedStringForKey:@"record_photo_tip"];
|
||||
} else {
|
||||
_lbTip.text = [TUIMultimediaCommon localizedStringForKey:@"record_tip"];
|
||||
}
|
||||
|
||||
_lbTip.font = [UIFont systemFontOfSize:16];
|
||||
_lbTip.textColor = UIColor.whiteColor;
|
||||
[_lbTip mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.bottom.lessThanOrEqualTo(_previewView).inset(16);
|
||||
make.bottom.lessThanOrEqualTo(_bottomMaskView.mas_top).offset(-16);
|
||||
make.centerX.equalTo(self);
|
||||
}];
|
||||
}
|
||||
|
||||
// 下方的控制按钮
|
||||
- (void)initControlButtons {
|
||||
_btnRecord = [[TUIMultimediaRecordButton alloc] init];
|
||||
[self addSubview:_btnRecord];
|
||||
_btnRecord.dotSizeNormal = BtnStartRecordDotSizeNormal;
|
||||
_btnRecord.dotSizePressed = BtnStartRecordDotSizePressed;
|
||||
_btnRecord.progressSizeNormal = BtnStartRecordProgressSizeNormal;
|
||||
_btnRecord.progressSizePressed = BtnStartRecordProgressSizePressed;
|
||||
_btnRecord.isOnlySupportTakePhoto = _isOnlySupportTakePhoto;
|
||||
_btnRecord.delegate = self;
|
||||
_btnExitRecord = [self newCustomButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"record_exit_img", @"cross") onTouchUpInside:@selector(onBtnExitClick)];
|
||||
_btnCameraSwitch = [self newCustomButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"record_camera_switch_img", @"camera_switch")
|
||||
onTouchUpInside:@selector(onBtnCameraSwitchClick)];
|
||||
|
||||
[_btnRecord mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.width.height.mas_equalTo(BtnStartRecordSize);
|
||||
make.centerX.equalTo(self);
|
||||
make.bottom.equalTo(self.mas_safeAreaLayoutGuideBottom).inset(BtnStartRecordGapBottom);
|
||||
}];
|
||||
[_btnExitRecord mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.size.mas_equalTo(BtnExitRecordSize);
|
||||
make.centerY.equalTo(_btnRecord);
|
||||
make.right.equalTo(_btnRecord.mas_left).inset(BtnExitRecordGapToStartRecord);
|
||||
}];
|
||||
[_btnCameraSwitch mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.size.mas_equalTo(BtnCameraSwitch);
|
||||
make.centerY.equalTo(_btnRecord);
|
||||
make.left.equalTo(_btnRecord.mas_right).inset(BtnCameraSwitchGapToStartRecord);
|
||||
}];
|
||||
}
|
||||
|
||||
// 右上角的功能按钮
|
||||
- (void)initFunctionButtons {
|
||||
[self initTorchView];
|
||||
[self initBeautyView];
|
||||
[self initAspectView];
|
||||
}
|
||||
|
||||
- (void) initTorchView {
|
||||
if (![[TUIMultimediaConfig sharedInstance] isSupportRecordTorch]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_btnFlash = [self newFunctionButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"record_flash_close_img", @"flash_close")
|
||||
title:[TUIMultimediaCommon localizedStringForKey:@"flash"]
|
||||
onTouchUpInside:@selector(onBtnFlashClick)];
|
||||
|
||||
[_btnFlash mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self).inset(FunctionBtnToRight);
|
||||
make.top.equalTo(self).inset(FunctionBtnToToTop);
|
||||
}];
|
||||
|
||||
_lastFuncitonBtn = _btnFlash;
|
||||
}
|
||||
|
||||
- (void) initBeautyView {
|
||||
if (![[TUIMultimediaConfig sharedInstance] isSupportRecordBeauty]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_btnBeautify = [self newFunctionButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"record_beautify_img", @"beauty_record")
|
||||
title:[TUIMultimediaCommon localizedStringForKey:@"beautify"]
|
||||
onTouchUpInside:@selector(onBtnBeautifyClick)];
|
||||
|
||||
[_btnBeautify mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
if (_lastFuncitonBtn == nil) {
|
||||
make.top.equalTo(self).inset(FunctionBtnToToTop);
|
||||
make.right.equalTo(self).inset(FunctionBtnToRight);
|
||||
} else {
|
||||
make.centerX.equalTo(_lastFuncitonBtn);
|
||||
make.top.equalTo(_lastFuncitonBtn.mas_bottom).inset(BtnExtendFunctionGap);
|
||||
}
|
||||
_lastFuncitonBtn = _btnBeautify;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) initAspectView {
|
||||
if (![[TUIMultimediaConfig sharedInstance] isSupportRecordAspect]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_btnAspect = [self newFunctionButtonWithImage:TUIMultimediaPluginBundleThemeImage(@"record_aspect_9_16_img", @"record_aspect_9_16")
|
||||
title:[TUIMultimediaCommon localizedStringForKey:@"aspect"]
|
||||
onTouchUpInside:@selector(onBtnAspectClick)];
|
||||
|
||||
[_btnAspect mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
if (_lastFuncitonBtn == nil) {
|
||||
make.top.equalTo(self).inset(FunctionBtnToToTop);
|
||||
make.right.equalTo(self).inset(FunctionBtnToRight);
|
||||
} else {
|
||||
make.centerX.equalTo(_lastFuncitonBtn);
|
||||
make.top.equalTo(_lastFuncitonBtn.mas_bottom).inset(BtnExtendFunctionGap);
|
||||
}
|
||||
_lastFuncitonBtn = _btnBeautify;
|
||||
}];
|
||||
}
|
||||
|
||||
- (UIButton *)newFunctionButtonWithImage:(UIImage *)img title:(NSString *)title onTouchUpInside:(SEL)sel {
|
||||
TUIMultimediaIconLabelButton *btn = [TUIMultimediaIconLabelButton buttonWithType:UIButtonTypeCustom];
|
||||
[btn setImage:img forState:UIControlStateNormal];
|
||||
[btn setAttributedTitle:[[NSAttributedString alloc]
|
||||
initWithString:title
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:12],
|
||||
NSForegroundColorAttributeName : TUIMultimediaPluginDynamicColor(@"record_func_btn_text_color", @"#FFFFFF"),
|
||||
}]
|
||||
forState:UIControlStateNormal];
|
||||
[btn addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];
|
||||
btn.iconSize = BtnExtendFunctionIconSize;
|
||||
btn.iconLabelGap = BtnExtendFunctionIconTextGap;
|
||||
[self addSubview:btn];
|
||||
return btn;
|
||||
}
|
||||
- (UIButton *)newCustomButtonWithImage:(nullable UIImage *)image onTouchUpInside:(nullable SEL)sel {
|
||||
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[btn setImage:image forState:UIControlStateNormal];
|
||||
if (sel != nil) {
|
||||
[btn addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
[self addSubview:btn];
|
||||
return btn;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
- (void)onBtnExitClick {
|
||||
[_delegate recordControlViewOnExit];
|
||||
}
|
||||
|
||||
- (void)onBtnCameraSwitchClick {
|
||||
if (_isUsingFrontCamera) {
|
||||
_isUsingFrontCamera = NO;
|
||||
_btnFlash.enabled = YES;
|
||||
} else {
|
||||
_isUsingFrontCamera = YES;
|
||||
_btnFlash.enabled = NO;
|
||||
[self setFlashState:NO];
|
||||
}
|
||||
[_delegate recordControlViewOnCameraSwicth:_isUsingFrontCamera];
|
||||
}
|
||||
|
||||
- (void)onBtnFlashClick {
|
||||
if (_isUsingFrontCamera) {
|
||||
return;
|
||||
}
|
||||
[self setFlashState:!_flashState];
|
||||
[_delegate recordControlViewOnFlashStateChange:_flashState];
|
||||
}
|
||||
|
||||
- (void)onBtnAspectClick {
|
||||
if (_aspectRatio == TUIMultimediaRecordAspectRatio9_16) {
|
||||
_aspectRatio = TUIMultimediaRecordAspectRatio3_4;
|
||||
[_btnAspect setImage:TUIMultimediaPluginBundleThemeImage(@"record_aspect_3_4_img", @"record_aspect_3_4") forState:UIControlStateNormal];
|
||||
[_previewView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.center.equalTo(self);
|
||||
make.width.equalTo(self);
|
||||
make.height.equalTo(_previewView.mas_width).multipliedBy(4.0 / 3.0);
|
||||
}];
|
||||
} else {
|
||||
_aspectRatio = TUIMultimediaRecordAspectRatio9_16;
|
||||
[_btnAspect setImage:TUIMultimediaPluginBundleThemeImage(@"record_aspect_9_16_img", @"record_aspect_9_16") forState:UIControlStateNormal];
|
||||
[_previewView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self);
|
||||
make.width.equalTo(self);
|
||||
make.height.equalTo(_previewView.mas_width).multipliedBy(16.0 / 9.0);
|
||||
}];
|
||||
}
|
||||
[_delegate recordControlViewOnAspectChange:_aspectRatio];
|
||||
}
|
||||
|
||||
- (void)onBtnBeautifyClick {
|
||||
[_delegate recordControlViewOnBeautify];
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaRecordButtonDelegate protocol
|
||||
- (void)onRecordButtonLongPressBegan:(TUIMultimediaRecordButton *)btn {
|
||||
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:self.delegate]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isOnlySupportTakePhoto) {
|
||||
[_delegate recordControlViewOnRecordStart];
|
||||
_lbDuration.hidden = !ShowDurationLabel;
|
||||
} else {
|
||||
_lbDuration.hidden = YES;
|
||||
}
|
||||
|
||||
_lbTip.hidden = YES;
|
||||
_btnExitRecord.hidden = YES;
|
||||
_btnCameraSwitch.hidden = YES;
|
||||
|
||||
if (_btnFlash != nil) {
|
||||
_btnFlash.hidden = YES;
|
||||
}
|
||||
if (_btnBeautify != nil) {
|
||||
_btnBeautify.hidden = YES;
|
||||
}
|
||||
if (_btnAspect != nil) {
|
||||
_btnAspect.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRecordButtonLongPressEnded:(TUIMultimediaRecordButton *)btn {
|
||||
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:nil]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!_isOnlySupportTakePhoto) {
|
||||
[_delegate recordControlViewOnRecordFinish];
|
||||
} else {
|
||||
[_delegate recordControlViewPhoto];
|
||||
}
|
||||
|
||||
_lbDuration.hidden = YES;
|
||||
|
||||
_lbTip.hidden = NO;
|
||||
_btnExitRecord.hidden = NO;
|
||||
_btnCameraSwitch.hidden = NO;
|
||||
if (_btnFlash != nil) {
|
||||
_btnFlash.hidden = NO;
|
||||
}
|
||||
if (_btnBeautify != nil) {
|
||||
_btnBeautify.hidden = NO;
|
||||
}
|
||||
if (_btnAspect != nil) {
|
||||
_btnAspect.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRecordButtonLongPressCancelled:(TUIMultimediaRecordButton *)btn {
|
||||
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:nil]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isOnlySupportTakePhoto) {
|
||||
[_delegate recordControlViewOnRecordFinish];
|
||||
}
|
||||
|
||||
_lbDuration.hidden = YES;
|
||||
|
||||
_lbTip.hidden = NO;
|
||||
_btnExitRecord.hidden = NO;
|
||||
_btnCameraSwitch.hidden = NO;
|
||||
if (_btnFlash != nil) {
|
||||
_btnFlash.hidden = NO;
|
||||
}
|
||||
if (_btnBeautify != nil) {
|
||||
_btnBeautify.hidden = NO;
|
||||
}
|
||||
if (_btnAspect != nil) {
|
||||
_btnAspect.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRecordButtonTap:(TUIMultimediaRecordButton *)btn {
|
||||
[_delegate recordControlViewPhoto];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
- (void)setFlashState:(BOOL)flashState {
|
||||
_flashState = flashState;
|
||||
if (_flashState) {
|
||||
[_btnFlash setImage:TUIMultimediaPluginBundleThemeImage(@"record_flash_open_img", @"flash_open") forState:UIControlStateNormal];
|
||||
} else {
|
||||
[_btnFlash setImage:TUIMultimediaPluginBundleThemeImage(@"record_flash_close_img", @"flash_close") forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)recordTipHidden {
|
||||
return _lbTip.hidden;
|
||||
}
|
||||
|
||||
- (void)setRecordTipHidden:(BOOL)recordTipHidden {
|
||||
_lbTip.hidden = recordTipHidden;
|
||||
}
|
||||
@end
|
||||
Reference in New Issue
Block a user