接著即時通訊整個頁面的搭建(一)繼續(xù)寫
上一篇說完了控制器, 這一篇說說Cell, 底部ToolBar以及錄音的顯示的View, 約束嘛, 用的Masonry
IMBaseCell
這個屬性是一個枚舉值
typedef enum{
IMBaseCellDefaultStyle = -1, // 默認狀態(tài) 左邊
IMBaseCellLeftStyle, //左邊的狀態(tài)
IMBaseCellRightStyle // 右邊的狀態(tài)
} IMBaseCellStyle;
一開始是沒有這個屬性的, 直接就是一個isLeft判斷, (當然正常的一個即時通訊的判斷是不會放在外面的), 后來運行發(fā)現(xiàn), Masony每一次reMake約束, 很耗時, 所以采用一個屬性保存之前的樣式, 和當前樣式一樣, 就不用再去改變約束了, 相對而言, 要提升一點流暢度
@property(nonatomic, assign)IMBaseCellStyle style;
這是一個Block屬性, 是為了cell的Item變量改變,讓控制器的數(shù)組的也同時更新,(一切顯示靠模型去控制), 會有下載的進度的改變等等
@property(nonatomic, copy)void(^reloadCell)(IMBaseItem *messageItem);
查看圖片, 以及播放視頻的需要跳轉(zhuǎn), 很喜歡用block, 但是像這種情況還是選擇了代理(具體哪里該使用代理, 哪里使用block, 不是很清楚, 有的人說block跟蹤代碼不方便, 代理點一下就跳過去了, 說的也是, 不過自己寫的代碼, 跟蹤肯定是沒問題的, 各有優(yōu)劣吧, 全憑自己選擇)
@property(nonatomic, weak)id<IMBaseCellDelegate> delegate;
看看讓代理干了啥
/**
* 點擊視頻,圖片的代理方法
*
* @param cell 點擊的cell
* @param viewController 實例化好的控制器
* @param animated 是否動畫過去
* @param completion 動畫完成后要執(zhí)行的代碼
*/
- (void)IMBaseCell:(IMBaseCell *)cell presentViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void(^)())completion;
/**
* 重新發(fā)送(發(fā)送失敗的情況下)
*/
- (void)IMBaseCell:(IMBaseCell *)cell reSendMessage:(IMBaseItem *)message;
/**
* 點擊了語音播放(上一篇已經(jīng)講過連續(xù)播放)
*/
- (void)IMBaseCellClickAudioCell:(IMBaseCell *)cell;
cell里沒有太多的邏輯代碼, 其他的就是賦值了
其他就沒有了(子弟動手,豐衣足食??), 因為是demo, 并沒有聯(lián)網(wǎng)的, 所以上傳,下載的進度, 運行時沒有顯示的, 代碼已經(jīng)注釋了, 具體這個進度怎么監(jiān)聽的, 放到下一篇, 上傳,下載的方法里(因為用的AFN2.x的版本, 并沒有3.0里的block回調(diào), 所以只能用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;
/**
* 發(fā)送多條
*
* @param schoolChatToolBar
* @param messageItem 消息體
*/
- (void)IMChatToolBar:(IMChatToolBar *)chatToolBar sendIMBaseItem:(IMBaseItem *)messageItem;
/*
* 鍵盤彈出 更新外部高度
*/
- (void)IMChatToolBar:(IMChatToolBar *)schoolChatToolBar ReloadHeight:(CGFloat)reloadHeight;
主要說一下更新高度, 這個給一個代理方法, 這個高度, 是toolBar的頂部具體屏幕底部的距離, 控制器的View拿到這個高度做響應(yīng)的增減即可
具體看看實現(xiàn)吧 ↘???↙?
demo更多里值有拍照, 選擇照片, 還有視頻, 這里偷懶了, 因為選擇照片這些都需要一個控制器的, 也是因為我自己知道, 我的toolBar代理是控制器才敢這么寫的??
應(yīng)該加一層isKindIfClass判斷
@property(nonatomic, weak)UIViewController *viewController;
- (void)setDelegate:(id<IMChatToolBarDelegate>)delegate{
_delegate = delegate;
self.viewController = (UIViewController *)delegate;
}
這么寫的好處就是選擇照片上傳的代碼就不用放在控制器里了, toolBar就能搞定,當然不一定合理(好用的就是合理的??, 開玩笑啦), 所以基本上, 發(fā)送消息的活都讓toolBar干了 控制器只要刷新數(shù)據(jù)就好了
說一個監(jiān)聽textView發(fā)送鍵的時間
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ([text isEqualToString:@"\n"]) {
[self sendText:[self emotionText]];
return NO;
}
return YES;
}
說到發(fā)送消息, 想到了自定義鍵盤時候(這里的鍵盤用的袁崢),表情編碼與解碼, 也是一個坑, 寫好的正則表達式,在網(wǎng)站里測試是正常的到代碼里就不行了, 只能采用暴力的方法解決(性能不好, 有好的方法可以討論),例如匹配[1]這樣的中括號中間是數(shù)字的字符串的正則表達式該怎么寫(會的請評論一下喔,感激不盡)
既然要要自定義鍵盤, 那肯定要切換鍵盤了, 改變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];
}
上傳下載發(fā)送消息的到下一篇再說吧
再看一下錄音時候的提示,完全模仿微信的提示寫的
IMVoiceVolumeView
/**
* 根據(jù)聲音大小顯示不同view
*
* @param volumn 聲音的分貝大小
*/
- (void)volunmeWithSound:(CGFloat)volumn;
/**
* 取消錄音的樣式
*/
- (void)volunmeViewCancel;
/**
* 正常的樣式
*/
- (void)volunmeViewNormal;
/**
* 倒計時時間樣式
*/
- (void)volunmeViewCountDown;
/**
* 開始倒計時的方法
*/
- (void)volunmeViewCountDownBegin;
/**
* 顯示錄音太短
*/
- (void)showShortTimeModel;
/**
* 說話過長
*/
- (void)showLongTimeModel;
試了一下微信(平時經(jīng)常用,但還真沒留意過),大概就這個幾種模式吧
看模式前,看一下按鈕的監(jiān)聽方法
// 開始錄音
- (void)recordButtonTouchDown
{
self.volumeView.hidden = NO;
[self.volumeView volunmeViewNormal];
}
// 取消錄音
- (void)recordButtonTouchUpOutside// 滑出 并不在按鈕上
{
self.volumeView.hidden = YES;
}
// 完成錄音
- (void)recordButtonTouchUpInside
{
self.volumeView.hidden = YES;
[[IMAudioTool shareAudioTool] startEncode];
}
// 滑出按鈕 但按鈕還在響應(yīng)點擊事件
- (void)recordDragOutside
{
[self.volumeView volunmeViewCancel];
}
// 滑入按鈕范圍
- (void)recordDragInside
{
[self.volumeView volunmeViewNormal];
}
-
正常的樣式
就是會顯示錄音波動的效果, 大小聲測試了一下, -30 - 0的取值范圍, 5個音量級別(音量的分貝傳過來是用IMAudioTool單例傳值的, 具體就下一篇說說, ??)
-
錄音過短模式
低于最短限制(避免誤點)
-
取消錄音的模式
向上滑動松開取消,(超出按鈕的范圍顯示這個模式, 沒有松開滑入按鈕的范圍), 恢復正常的顯示模式
-
倒計時模式
當即將要超出錄音最大時長的時候, 有倒計時的提示 , demo限制的60, 剩余10秒開始提示
-
說話過長模式
超出最大限制的時間, 提示這個
具體的實現(xiàn)思路, 因為很多種樣式, 每一種提示的控件基本上都不能復用, 所以我就用了好幾個View, 顯示隱藏來回切換, 控件數(shù)量確實挺多的, 暫時沒想到其他的好辦法, 如果說移除, 需要的時候再去實例化, 會有卡頓, 用內(nèi)存換速度吧, 這也是最直接的辦法了
要注意一點, 當滑動超出范圍的時候, 給的是一個松開取消的時候, 當滑入按鈕范圍的時候, 要判斷錄音的時長, 看是需要顯示哪一種模式, 是正常的模式, 還是倒計時的模式
具體的看代碼吧, 比我說的清晰, 我是這么覺得的??
下一篇說一下, 整個消息的發(fā)送過程, 以及本地緩存等等