486 lines
17 KiB
Objective-C
486 lines
17 KiB
Objective-C
//
|
||
// MessageToolbarView.m
|
||
// xplan-ios
|
||
//
|
||
// Created by 冯硕 on 2022/4/20.
|
||
//
|
||
|
||
#import "SessionToolbarView.h"
|
||
///Third
|
||
#import <Masonry/Masonry.h>
|
||
#import <NIMSDK/NIMSDK.h>
|
||
///Tool
|
||
#import "ThemeColor.h"
|
||
#import "XPMacro.h"
|
||
#import "NSArray+Safe.h"
|
||
///Model
|
||
#import "MessageMenuModel.h"
|
||
///View
|
||
#import "MessageToolMenuCollectionViewCell.h"
|
||
#import "SessionAudioRecordView.h"
|
||
|
||
#define TOOL_HEIGHT 60
|
||
#define MENU_HEIGHT 130
|
||
|
||
@interface SessionToolbarView ()<UICollectionViewDelegate, UICollectionViewDataSource, UITextFieldDelegate, NIMMediaManagerDelegate>
|
||
///最大的那个容器
|
||
@property (nonatomic,strong) UIStackView *stackView;
|
||
///上部分的功能的容器
|
||
@property (nonatomic,strong) UIStackView *toolStackView;
|
||
///切换语音和文字的按钮
|
||
@property (nonatomic,strong) UIButton *changeButton;
|
||
///更多按钮
|
||
@property (nonatomic,strong) UIButton *moreButton;
|
||
///语音和输入框的
|
||
@property (nonatomic,strong) UIStackView *audioStackView;
|
||
///输入框背景
|
||
@property (nonatomic,strong) UIView * inputBackView;
|
||
///输入框
|
||
@property (nonatomic,strong) UITextField *inputView;
|
||
///语音
|
||
@property (nonatomic,strong) UIButton *audioButton;
|
||
///列表
|
||
@property (nonatomic,strong) UICollectionView *collectionView;
|
||
///底部的view
|
||
@property (nonatomic,strong) UIView * placeView;
|
||
///菜单数据
|
||
@property (nonatomic,strong) NSArray<MessageMenuModel *> *menuList;
|
||
///录音的view
|
||
@property (nonatomic,strong) SessionAudioRecordView *audioRecordView;
|
||
@end
|
||
|
||
@implementation SessionToolbarView
|
||
|
||
- (void)dealloc {
|
||
[[NIMSDK sharedSDK].mediaManager removeDelegate:self];
|
||
}
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame {
|
||
self = [super initWithFrame:frame];
|
||
if (self) {
|
||
[[NIMSDK sharedSDK].mediaManager addDelegate:self];
|
||
[self addNotification];
|
||
[self addAudioButtonAction];
|
||
[self initSubViews];
|
||
[self initSubViewConstraints];
|
||
}
|
||
return self;
|
||
}
|
||
#pragma mark - Public Method
|
||
- (void)foldMenuView {
|
||
[self.inputView resignFirstResponder];
|
||
if (self.collectionView.hidden == NO) {
|
||
self.collectionView.hidden = YES;
|
||
self.moreButton.selected = NO;
|
||
}
|
||
}
|
||
#pragma mark - Private Method
|
||
- (void)addAudioButtonAction {
|
||
///button 周围有一个70px的范围 超出这个范围的话 就是out 在这个范围的话就是in 从范围外到范围内的话就是enter 从范围内到范围外的话exit
|
||
[self.audioButton addTarget:self action:@selector(audioTouchDownAction) forControlEvents:UIControlEventTouchDown];
|
||
[self.audioButton addTarget:self action:@selector(audioTouchUpOutsideAction) forControlEvents:UIControlEventTouchUpOutside];
|
||
[self.audioButton addTarget:self action:@selector(audioTouchUpInsideAction) forControlEvents:UIControlEventTouchUpInside];
|
||
[self.audioButton addTarget:self action:@selector(audioTouchDragEnterAction) forControlEvents:UIControlEventTouchDragEnter];
|
||
[self.audioButton addTarget:self action:@selector(audioTouchDragExitAction) forControlEvents:UIControlEventTouchDragExit];
|
||
}
|
||
|
||
- (void)addNotification {
|
||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHidden:) name:UIKeyboardWillHideNotification object:nil];
|
||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
|
||
}
|
||
|
||
- (void)initSubViews {
|
||
self.backgroundColor = [ThemeColor appCellBackgroundColor];
|
||
[self addSubview:self.stackView];
|
||
|
||
[self.stackView addArrangedSubview:self.toolStackView];
|
||
[self.stackView addArrangedSubview:self.collectionView];
|
||
[self.stackView addArrangedSubview:self.placeView];
|
||
|
||
[self.toolStackView addArrangedSubview:self.changeButton];
|
||
[self.toolStackView addArrangedSubview:self.audioStackView];
|
||
[self.toolStackView addArrangedSubview:self.moreButton];
|
||
|
||
[self.audioStackView addArrangedSubview:self.inputBackView];
|
||
[self.audioStackView addArrangedSubview:self.audioButton];
|
||
|
||
[self.inputBackView addSubview:self.inputView];
|
||
}
|
||
|
||
- (void)initSubViewConstraints {
|
||
[self mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.bottom.mas_equalTo(self.stackView);
|
||
}];
|
||
|
||
[self.stackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.left.right.mas_equalTo(self).inset(15);
|
||
make.top.mas_equalTo(self);
|
||
}];
|
||
|
||
[self.toolStackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.height.mas_equalTo(TOOL_HEIGHT);
|
||
}];
|
||
|
||
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.height.mas_equalTo(MENU_HEIGHT);
|
||
}];
|
||
|
||
[self.placeView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.height.mas_equalTo(kSafeAreaBottomHeight);
|
||
}];
|
||
|
||
[self.changeButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.size.mas_equalTo(CGSizeMake(40, 40));
|
||
}];
|
||
|
||
[self.moreButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.size.mas_equalTo(CGSizeMake(40, 40));
|
||
}];
|
||
|
||
[self.audioStackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.height.mas_equalTo(40);
|
||
}];
|
||
|
||
[self.inputView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.left.mas_equalTo(self.inputBackView.mas_left).offset(16);
|
||
make.top.bottom.mas_equalTo(self.inputBackView);
|
||
make.right.mas_equalTo(self.inputBackView).offset(-5);
|
||
}];
|
||
}
|
||
|
||
#pragma mark - UICollectionViewDelegate And UICollectionViewDatasource
|
||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||
return self.menuList.count;
|
||
}
|
||
|
||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||
MessageToolMenuCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([MessageToolMenuCollectionViewCell class]) forIndexPath:indexPath];
|
||
MessageMenuModel * model = [self.menuList safeObjectAtIndex1:indexPath.row];
|
||
cell.imageName = model.imageName;
|
||
return cell;
|
||
}
|
||
|
||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
||
if (self.menuList.count > 0) {
|
||
MessageMenuModel * model = [self.menuList safeObjectAtIndex1:indexPath.row];
|
||
if (self.delegate && [self.delegate respondsToSelector:@selector(didSelectMenuItem:)]) {
|
||
[self.delegate didSelectMenuItem:model];
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma mark - UITextFieldDelegate
|
||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||
if (textField.text.length >0) {
|
||
if (self.delegate && [self.delegate respondsToSelector:@selector(sendTextMessage:)]) {
|
||
[self.delegate sendTextMessage:textField.text];
|
||
textField.text = nil;
|
||
}
|
||
}
|
||
return YES;
|
||
}
|
||
#pragma mark - NIMMediaManagerDelegate
|
||
/**
|
||
* 开始录制音频的回调
|
||
*
|
||
* @param filePath 录制的音频的文件路径
|
||
* @param error 错误信息
|
||
* @discussion 如果录音失败,filePath 有可能为 nil
|
||
*/
|
||
- (void)recordAudio:(nullable NSString *)filePath didBeganWithError:(nullable NSError *)error {
|
||
NSLog(@"开始录制语音");
|
||
}
|
||
|
||
/**
|
||
* 录制音频完成后的回调
|
||
*
|
||
* @param filePath 录制完成的音频文件路径
|
||
* @param error 错误信息
|
||
*/
|
||
- (void)recordAudio:(nullable NSString *)filePath didCompletedWithError:(nullable NSError *)error {
|
||
if (filePath.length > 0 && error == nil) {
|
||
if (self.delegate && [self.delegate respondsToSelector:@selector(audioRecordCompletion:)]) {
|
||
[self.delegate audioRecordCompletion:filePath];
|
||
}
|
||
}
|
||
NSLog(@"录制音频完成后的回调 %@", filePath);
|
||
}
|
||
|
||
/**
|
||
* 录音被取消的回调
|
||
*/
|
||
- (void)recordAudioDidCancelled {
|
||
NSLog(@"录音被取消的回调");
|
||
}
|
||
|
||
/**
|
||
* 音频录制进度更新回调
|
||
*
|
||
* @param currentTime 当前录制的时间
|
||
*/
|
||
- (void)recordAudioProgress:(NSTimeInterval)currentTime {
|
||
[self.audioRecordView updateAudioRecordProgress:currentTime];
|
||
}
|
||
|
||
/**
|
||
* 录音开始被打断回调
|
||
*/
|
||
- (void)recordAudioInterruptionBegin {
|
||
[self.audioRecordView cancelAudioRecord];
|
||
}
|
||
|
||
#pragma mark - Event Response
|
||
- (void)keyboardWillShow:(NSNotification *)notification {
|
||
if (self.collectionView.hidden == NO) {
|
||
self.collectionView.hidden = YES;
|
||
self.moreButton.selected = NO;
|
||
}
|
||
NSDictionary *userInfo = [notification userInfo];
|
||
CGRect keyboardEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||
if (CGRectIsNull(keyboardEndFrame)) {
|
||
return;
|
||
}
|
||
UIViewAnimationCurve animationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
||
NSInteger animationCurveOption = (animationCurve << 16);
|
||
double animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
||
[UIView animateWithDuration:animationDuration delay:0.0 options:animationCurveOption animations:^{
|
||
if (self.delegate && [self.delegate respondsToSelector:@selector(keyboardWillChangeFrame:)]) {
|
||
[self.delegate keyboardWillChangeFrame:keyboardEndFrame];
|
||
}
|
||
} completion:^(BOOL finished) {
|
||
|
||
}];
|
||
}
|
||
|
||
- (void)keyboardWillHidden:(NSNotification*)notification {
|
||
CGRect keyboardEndFrame = CGRectMake(0, 0, 0, 0);
|
||
[UIView animateWithDuration:0.25 animations:^{
|
||
if (self.delegate && [self.delegate respondsToSelector:@selector(keyboardWillChangeFrame:)]) {
|
||
[self.delegate keyboardWillChangeFrame:keyboardEndFrame];
|
||
}
|
||
}];
|
||
}
|
||
|
||
- (void)changeButtonAction:(UIButton *)sender {
|
||
sender.selected = !sender.selected;
|
||
self.inputBackView.hidden = sender.selected;
|
||
self.audioButton.hidden = !sender.selected;
|
||
self.collectionView.hidden = YES;
|
||
self.moreButton.selected = NO;
|
||
if (sender.selected) {
|
||
[self.inputView resignFirstResponder];
|
||
} else {
|
||
[self.inputView becomeFirstResponder];
|
||
}
|
||
}
|
||
|
||
- (void)moreButtonAction:(UIButton *)sender {
|
||
sender.selected = !sender.selected;
|
||
self.collectionView.hidden = !sender.selected;
|
||
[self.inputView resignFirstResponder];
|
||
CGRect keyboardEndFrame = CGRectMake(0, 0, 0, 0);
|
||
[UIView animateWithDuration:0.25 animations:^{
|
||
if (self.delegate && [self.delegate respondsToSelector:@selector(keyboardWillChangeFrame:)]) {
|
||
[self.delegate keyboardWillChangeFrame:keyboardEndFrame];
|
||
}
|
||
}];
|
||
}
|
||
|
||
|
||
- (void)audioTouchDownAction {
|
||
self.audioButton.selected = YES;
|
||
///开始录音
|
||
if (!self.audioRecordView.superview) {
|
||
[[UIApplication sharedApplication].keyWindow addSubview:self.audioRecordView];
|
||
[self.audioRecordView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.center.mas_equalTo(self.audioRecordView.superview);
|
||
}];
|
||
[self.audioRecordView configAudioRecord:@"message_tool_audio_record_first" title:@"手指上滑,取消发送" isAnimation:YES];
|
||
[self.audioRecordView beginAudioRecord];
|
||
}
|
||
|
||
}
|
||
|
||
- (void)audioTouchUpOutsideAction {
|
||
self.audioButton.selected = NO;
|
||
///取消录音
|
||
[self.audioRecordView cancelAudioRecord];
|
||
[self.audioRecordView removeFromSuperview];
|
||
}
|
||
|
||
- (void)audioTouchUpInsideAction {
|
||
self.audioButton.selected = NO;
|
||
///手指抬起 完成录音
|
||
[self.audioRecordView finishAudioRecord];
|
||
[self.audioRecordView removeFromSuperview];
|
||
}
|
||
|
||
- (void)audioTouchDragEnterAction {
|
||
///手指上滑 取消发送
|
||
[self.audioRecordView configAudioRecord:@"message_tool_audio_record_first" title:@"手指上滑,取消发送" isAnimation:YES];
|
||
}
|
||
|
||
- (void)audioTouchDragExitAction {
|
||
///松开手指 取消发送
|
||
[self.audioRecordView configAudioRecord:@"message_tool_audio_record_cancel" title:@"松开手指 取消发送" isAnimation:NO];
|
||
}
|
||
|
||
#pragma mark - Getters And Setters
|
||
- (void)setSendDisabled:(BOOL)sendDisabled {
|
||
self.userInteractionEnabled = !sendDisabled;
|
||
if (sendDisabled) {
|
||
self.inputView.text = @"";
|
||
NSAttributedString * attribute = [[NSAttributedString alloc] initWithString:_model == 2 ? @"互相关注成为好友可发起聊天" : @"暂时无法私聊" attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15], NSForegroundColorAttributeName : [ThemeColor secondTextColor]}];
|
||
self.inputView.attributedPlaceholder = attribute;
|
||
} else {
|
||
NSAttributedString * attribute = [[NSAttributedString alloc] initWithString:@"请输入消息" attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15], NSForegroundColorAttributeName : [ThemeColor secondTextColor]}];
|
||
self.inputView.attributedPlaceholder = attribute;
|
||
}
|
||
}
|
||
|
||
- (UIStackView *)stackView {
|
||
if (!_stackView) {
|
||
_stackView = [[UIStackView alloc] init];
|
||
_stackView.axis = UILayoutConstraintAxisVertical;
|
||
_stackView.distribution = UIStackViewDistributionFill;
|
||
_stackView.alignment = UIStackViewAlignmentFill;
|
||
_stackView.spacing = 0;
|
||
}
|
||
return _stackView;
|
||
}
|
||
|
||
- (UIStackView *)toolStackView {
|
||
if (!_toolStackView) {
|
||
_toolStackView = [[UIStackView alloc] init];
|
||
_toolStackView.axis = UILayoutConstraintAxisHorizontal;
|
||
_toolStackView.distribution = UIStackViewDistributionFill;
|
||
_toolStackView.alignment = UIStackViewAlignmentCenter;
|
||
_toolStackView.spacing = 8;
|
||
}
|
||
return _toolStackView;
|
||
}
|
||
|
||
- (UIButton *)changeButton {
|
||
if (!_changeButton) {
|
||
_changeButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||
[_changeButton setImage:[UIImage imageNamed:@"message_session_tool_text"] forState:UIControlStateNormal];
|
||
[_changeButton setImage:[UIImage imageNamed:@"message_session_tool_audio"] forState:UIControlStateSelected];
|
||
[_changeButton addTarget:self action:@selector(changeButtonAction:) forControlEvents:UIControlEventTouchUpInside];
|
||
}
|
||
return _changeButton;
|
||
}
|
||
|
||
- (UIStackView *)audioStackView {
|
||
if (!_audioStackView) {
|
||
_audioStackView = [[UIStackView alloc] init];
|
||
_audioStackView.axis = UILayoutConstraintAxisHorizontal;
|
||
_audioStackView.distribution = UIStackViewDistributionFill;
|
||
_audioStackView.alignment = UIStackViewAlignmentFill;
|
||
_audioStackView.spacing = 0;
|
||
}
|
||
return _audioStackView;
|
||
}
|
||
|
||
- (UIView *)inputBackView {
|
||
if (!_inputBackView) {
|
||
_inputBackView = [[UIView alloc] init];
|
||
_inputBackView.backgroundColor = UIColorFromRGB(0xF1F1FA);
|
||
_inputBackView.layer.masksToBounds = YES;
|
||
_inputBackView.layer.cornerRadius = 4;
|
||
}
|
||
return _inputBackView;
|
||
}
|
||
|
||
- (UITextField *)inputView{
|
||
if (!_inputView) {
|
||
_inputView = [[UITextField alloc] init];
|
||
NSAttributedString * attribute = [[NSAttributedString alloc] initWithString:@"请输入消息" attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15], NSForegroundColorAttributeName : [ThemeColor secondTextColor]}];
|
||
_inputView.attributedPlaceholder = attribute;
|
||
_inputView.borderStyle = UITextBorderStyleNone;
|
||
_inputView.tintColor = [ThemeColor appMainColor];
|
||
_inputView.textColor = [ThemeColor mainTextColor];
|
||
_inputView.font = [UIFont systemFontOfSize:12.f];
|
||
_inputView.delegate = self;
|
||
_inputView.autocorrectionType = UITextAutocorrectionTypeNo;
|
||
_inputView.returnKeyType = UIReturnKeySend;
|
||
}
|
||
return _inputView;
|
||
}
|
||
|
||
- (UIButton *)audioButton {
|
||
if (!_audioButton) {
|
||
_audioButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||
[_audioButton setTitle:@"按住说话" forState:UIControlStateNormal];
|
||
[_audioButton setTitle:@"松开结束" forState:UIControlStateSelected];
|
||
[_audioButton setTitleColor:[ThemeColor mainTextColor] forState:UIControlStateNormal];
|
||
_audioButton.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
_audioButton.layer.masksToBounds = YES;
|
||
_audioButton.layer.cornerRadius = 4;
|
||
_audioButton.backgroundColor = UIColorFromRGB(0xF1F1FA);
|
||
_audioButton.hidden = YES;
|
||
}
|
||
return _audioButton;
|
||
}
|
||
|
||
- (UIButton *)moreButton {
|
||
if (!_moreButton) {
|
||
_moreButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||
[_moreButton setImage:[UIImage imageNamed:@"message_session_tool_more_normal"] forState:UIControlStateNormal];
|
||
[_moreButton setImage:[UIImage imageNamed:@"message_session_tool_more_select"] forState:UIControlStateSelected];
|
||
[_moreButton addTarget:self action:@selector(moreButtonAction:) forControlEvents:UIControlEventTouchUpInside];
|
||
}
|
||
return _moreButton;
|
||
}
|
||
|
||
- (UICollectionView *)collectionView{
|
||
if (!_collectionView) {
|
||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||
layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
|
||
layout.itemSize = CGSizeMake(48, 48);
|
||
layout.minimumLineSpacing = 24;
|
||
layout.minimumInteritemSpacing = 10;
|
||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||
_collectionView.dataSource = self;
|
||
_collectionView.delegate = self;
|
||
_collectionView.backgroundColor = [UIColor clearColor];
|
||
_collectionView.hidden = YES;
|
||
[_collectionView registerClass:[MessageToolMenuCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass([MessageToolMenuCollectionViewCell class])];
|
||
}
|
||
return _collectionView;
|
||
}
|
||
|
||
- (UIView *)placeView {
|
||
if (!_placeView) {
|
||
_placeView = [[UIView alloc] init];
|
||
_placeView.backgroundColor = [UIColor clearColor];
|
||
}
|
||
return _placeView;
|
||
}
|
||
|
||
- (NSArray<MessageMenuModel *> *)menuList {
|
||
if (!_menuList) {
|
||
MessageMenuModel * phototModel = [[MessageMenuModel alloc] init];
|
||
phototModel.imageName = @"message_session_menu_photo";
|
||
phototModel.type= MessageMenuType_Photo;
|
||
|
||
MessageMenuModel * giftModel = [[MessageMenuModel alloc] init];
|
||
giftModel.imageName = @"message_session_menu_gift";
|
||
giftModel.type= MessageMenuType_Gift;
|
||
|
||
_menuList = @[giftModel,phototModel];
|
||
}
|
||
return _menuList;
|
||
}
|
||
|
||
- (SessionAudioRecordView *)audioRecordView {
|
||
if (!_audioRecordView) {
|
||
_audioRecordView = [[SessionAudioRecordView alloc] init];
|
||
}
|
||
return _audioRecordView;
|
||
}
|
||
|
||
|
||
|
||
@end
|