增加换肤功能

This commit is contained in:
启星
2025-08-14 10:07:49 +08:00
parent f6964c1e89
commit 4f9318d98e
8789 changed files with 978530 additions and 2 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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