接着即时通讯整个页面的搭建(一)继续写
上一篇说完了控制器, 这一篇说说Cell, 底部ToolBar以及录音的显示的View, 约束嘛, 用的Masonry
IMBaseCell
这个属性是一个枚举值
typedef enum{
IMBaseCellDefaultStyle = -1, // 默认状态 左边
IMBaseCellLeftStyle, //左边的状态
IMBaseCellRightStyle // 右边的状态
} IMBaseCellStyle;
一开始是没有这个属性的, 直接就是一个isLeft判断, (当然正常的一个即时通讯的判断是不会放在外面的), 后来运行发现, Masony每一次reMake约束, 很耗时, 所以采用一个属性保存之前的样式, 和当前样式一样, 就不用再去改变约束了, 相对而言, 要提升一点流畅度
@property(nonatomic, assign)IMBaseCellStyle style;
这是一个Block属性, 是为了cell的Item变量改变,让控制器的数组的也同时更新,(一切显示靠模型去控制), 会有下载的进度的改变等等
@property(nonatomic, copy)void(^reloadCell)(IMBaseItem *messageItem);
查看图片, 以及播放视频的需要跳转, 很喜欢用block, 但是像这种情况还是选择了代理(具体哪里该使用代理, 哪里使用block, 不是很清楚, 有的人说block跟踪代码不方便, 代理点一下就跳过去了, 说的也是, 不过自己写的代码, 跟踪肯定是没问题的, 各有优劣吧, 全凭自己选择)
@property(nonatomic, weak)id<IMBaseCellDelegate> delegate;
看看让代理干了啥
/**
* 点击视频,图片的代理方法
*
* @param cell 点击的cell
* @param viewController 实例化好的控制器
* @param animated 是否动画过去
* @param completion 动画完成后要执行的代码
*/
- (void)IMBaseCell:(IMBaseCell *)cell presentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void(^)())completion;
/**
* 重新发送(发送失败的情况下)
*/
- (void)IMBaseCell:(IMBaseCell *)cell reSendMessage:(IMBaseItem *)message;
/**
* 点击了语音播放(上一篇已经讲过连续播放)
*/
- (void)IMBaseCellClickAudioCell:(IMBaseCell *)cell;
cell里没有太多的逻辑代码, 其他的就是赋值了
其他就没有了(子弟动手,丰衣足食😀), 因为是demo, 并没有联网的, 所以上传,下载的进度, 运行时没有显示的, 代码已经注释了, 具体这个进度怎么监听的, 放到下一篇, 上传,下载的方法里(因为用的AFN2.x的版本, 并没有3.0里的block回调, 所以只能用kvo了),也是懵了一会,最后也想了这个办法解决, 有更好的方法,可以告诉我,不慎感激
下面说说底部的工具栏吧
IMChatToolBar
直接上代码
@property(nonatomic, weak)id<IMChatToolBarDelegate> delegate;
@property(nonatomic, weak)PlacehodeTextView *inputTextView;
/**
* 实例化一个toolBar
*
* @param frame 尺寸
* @param toolBarHeight toolBar的高度
* @param moreViewHeight 更多的View的高度
*
* @return 实例
*/
- (instancetype)initWithFrame:(CGRect)frame toolBarHeight:(CGFloat)toolBarHeight moreViewHeight:(CGFloat)moreViewHeight;
/**
* 回到最底部
*/
- (void)backOriginalHeight;
/**
* 隐藏
*/
- (void)hide;
/**
* 显示
*/
- (void)show;
代理的方法
/**
* 刷新消息
*/
- (void)IMChatToolBar:(IMChatToolBar *)chatToolBar imBaseItem:(IMBaseItem *)messageItem;
/**
* 发送多条
*
* @param schoolChatToolBar
* @param messageItem 消息体
*/
- (void)IMChatToolBar:(IMChatToolBar *)chatToolBar sendIMBaseItem:(IMBaseItem *)messageItem;
/*
* 键盘弹出 更新外部高度
*/
- (void)IMChatToolBar:(IMChatToolBar *)schoolChatToolBar ReloadHeight:(CGFloat)reloadHeight;
主要说一下更新高度, 这个给一个代理方法, 这个高度, 是toolBar的顶部具体屏幕底部的距离, 控制器的View拿到这个高度做响应的增减即可
具体看看实现吧 ↘️⤵️↙️
demo更多里值有拍照, 选择照片, 还有视频, 这里偷懒了, 因为选择照片这些都需要一个控制器的, 也是因为我自己知道, 我的toolBar代理是控制器才敢这么写的😋
应该加一层isKindIfClass判断
@property(nonatomic, weak)UIViewController *viewController;
- (void)setDelegate:(id<IMChatToolBarDelegate>)delegate{
_delegate = delegate;
self.viewController = (UIViewController *)delegate;
}
这么写的好处就是选择照片上传的代码就不用放在控制器里了, toolBar就能搞定,当然不一定合理(好用的就是合理的😀, 开玩笑啦), 所以基本上, 发送消息的活都让toolBar干了 控制器只要刷新数据就好了
说一个监听textView发送键的时间
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ([text isEqualToString:@"\n"]) {
[self sendText:[self emotionText]];
return NO;
}
return YES;
}
说到发送消息, 想到了自定义键盘时候(这里的键盘用的袁崢),表情编码与解码, 也是一个坑, 写好的正则表达式,在网站里测试是正常的到代码里就不行了, 只能采用暴力的方法解决(性能不好, 有好的方法可以讨论),例如匹配[1]这样的中括号中间是数字的字符串的正则表达式该怎么写(会的请评论一下喔,感激不尽)
既然要要自定义键盘, 那肯定要切换键盘了, 改变inputView即可
感兴趣的可以研究一下袁峥的表情键盘
self.emoticonButton.selected = !self.emoticonButton.isSelected;
if (self.emoticonButton.selected) {
self.audioButton.hidden = YES;
self.recordButton.selected = NO;
self.inputTextView.yz_emotionKeyboard = self.emotionKeyboard;
[self.inputTextView becomeFirstResponder];
} else {
self.inputTextView.yz_emotionKeyboard = nil;
[self reloadInputViews];
}
上传下载发送消息的到下一篇再说吧
再看一下录音时候的提示,完全模仿微信的提示写的
IMVoiceVolumeView
/**
* 根据声音大小显示不同view
*
* @param volumn 声音的分贝大小
*/
- (void)volunmeWithSound:(CGFloat)volumn;
/**
* 取消录音的样式
*/
- (void)volunmeViewCancel;
/**
* 正常的样式
*/
- (void)volunmeViewNormal;
/**
* 倒计时时间样式
*/
- (void)volunmeViewCountDown;
/**
* 开始倒计时的方法
*/
- (void)volunmeViewCountDownBegin;
/**
* 显示录音太短
*/
- (void)showShortTimeModel;
/**
* 说话过长
*/
- (void)showLongTimeModel;
试了一下微信(平时经常用,但还真没留意过),大概就这个几种模式吧
看模式前,看一下按钮的监听方法
// 开始录音
- (void)recordButtonTouchDown
{
self.volumeView.hidden = NO;
[self.volumeView volunmeViewNormal];
}
// 取消录音
- (void)recordButtonTouchUpOutside// 滑出 并不在按钮上
{
self.volumeView.hidden = YES;
}
// 完成录音
- (void)recordButtonTouchUpInside
{
self.volumeView.hidden = YES;
[[IMAudioTool shareAudioTool] startEncode];
}
// 滑出按钮 但按钮还在响应点击事件
- (void)recordDragOutside
{
[self.volumeView volunmeViewCancel];
}
// 滑入按钮范围
- (void)recordDragInside
{
[self.volumeView volunmeViewNormal];
}
-
正常的样式
就是会显示录音波动的效果, 大小声测试了一下, -30 - 0的取值范围, 5个音量级别(音量的分贝传过来是用IMAudioTool单例传值的, 具体就下一篇说说, 😋)
-
录音过短模式
低于最短限制(避免误点)
-
取消录音的模式
向上滑动松开取消,(超出按钮的范围显示这个模式, 没有松开滑入按钮的范围), 恢复正常的显示模式
-
倒计时模式
当即将要超出录音最大时长的时候, 有倒计时的提示 , demo限制的60, 剩余10秒开始提示
-
说话过长模式
超出最大限制的时间, 提示这个
具体的实现思路, 因为很多种样式, 每一种提示的控件基本上都不能复用, 所以我就用了好几个View, 显示隐藏来回切换, 控件数量确实挺多的, 暂时没想到其他的好办法, 如果说移除, 需要的时候再去实例化, 会有卡顿, 用内存换速度吧, 这也是最直接的办法了
要注意一点, 当滑动超出范围的时候, 给的是一个松开取消的时候, 当滑入按钮范围的时候, 要判断录音的时长, 看是需要显示哪一种模式, 是正常的模式, 还是倒计时的模式
具体的看代码吧, 比我说的清晰, 我是这么觉得的😀
下一篇说一下, 整个消息的发送过程, 以及本地缓存等等
网友评论