iOS開(kāi)發(fā)之聊天模塊--內(nèi)容保存邏輯實(shí)現(xiàn)
需求詳解:
在實(shí)際開(kāi)發(fā)中,有可能是在后期優(yōu)化的時(shí)候,會(huì)有這么需要優(yōu)化的需求:聊天輸入框保存之前輸入的文本,提高用戶的良好體驗(yàn)。
在聊天模塊中,用戶可能會(huì)在輸入框輸入若干字符,但是沒(méi)有點(diǎn)擊發(fā)送就點(diǎn)擊退出聊天,或者要點(diǎn)擊用戶頭像確認(rèn)用戶的信息,或者比如需要向好友發(fā)送另一個(gè)好 友的ID不得不暫時(shí)退出當(dāng)前好友聊天界面跳轉(zhuǎn)找到別的界面找ID,然而當(dāng)前聊天輸入框也已經(jīng)輸入好了若干字符,用戶當(dāng)然不希望退出之后就刪除之前輸入好的 文字。所以這里就需要暫時(shí)保存用戶輸入好的但是沒(méi)有發(fā)送出去的字符串。
但是,還需要滿足1、完全殺掉或者完全退出應(yīng)用就需要清除這個(gè)暫時(shí)保存的字符串,2、發(fā)送出去之后,肯定就要delegate之前暫時(shí)保存的字符串嘍。
開(kāi)始:
這部分邏輯的實(shí)現(xiàn)一開(kāi)始我沒(méi)怎么比較好的頭緒,只想到本地序列化,但實(shí)際上這個(gè)還不算是***的思路,因?yàn)楸镜匦蛄谢玫竭@里有點(diǎn)小題大做了,其實(shí)只要用全局靜態(tài)變量的字典就可以了。
具體實(shí)現(xiàn)的邏輯,我也特意閱讀研究了Coding項(xiàng)目的實(shí)現(xiàn),畢竟這個(gè)項(xiàng)目是比較成熟的項(xiàng)目,聊天模塊也做的很不錯(cuò),所以學(xué)學(xué)別人的思想,正所謂站在巨人的肩膀上,也是很好的哦。
那么下面,我就直接解讀Coding源碼(學(xué)習(xí)Coding-iOS開(kāi)源項(xiàng)目日志(一))在這個(gè)聊天模塊內(nèi)容保存的邏輯吧,就不拿自己工作開(kāi)發(fā)的項(xiàng)目來(lái)講了。
1、首先聲明全局static的變量,Coding中用inputStrDict存儲(chǔ)輸入框的字符串,而inputMediaDict我暫時(shí)不知道它具體存什么的,應(yīng)該是media之類的元素:
2、然后將很多邏輯封裝在這個(gè)UIMessageInputView類中,方法都不用公開(kāi),完全利用UIMessageInputView活動(dòng)周期的邏輯就可以了。
- #pragma mark remember input
- - (NSMutableDictionary *)shareInputStrDict{
- if (!_inputStrDict) {
- _inputStrDict = [[NSMutableDictionary alloc] init];
- }
- return _inputStrDict;
- }
- - (NSMutableDictionary *)shareInputMediaDict{
- if (!_inputMediaDict) {
- _inputMediaDict = [[NSMutableDictionary alloc] init];
- }
- return _inputMediaDict;
- }
- - (NSString *)inputKey{
- NSString *inputKey = nil;
- if (_contentType == UIMessageInputViewContentTypePriMsg) {
- inputKey = [NSString stringWithFormat:@"privateMessage_%@", self.toUser.global_key];
- }else{
- if (_commentOfId) {
- switch (_contentType) {
- case UIMessageInputViewContentTypeTweet:
- inputKey = [NSString stringWithFormat:@"tweet_%@_%@", _commentOfId.stringValue, _toUser.global_key.length > 0? _toUser.global_key:@""];
- break;
- case UIMessageInputViewContentTypeTopic:
- inputKey = [NSString stringWithFormat:@"topic_%@_%@", _commentOfId.stringValue, _toUser.global_key.length > 0? _toUser.global_key:@""];
- break;
- case UIMessageInputViewContentTypeTask:
- inputKey = [NSString stringWithFormat:@"task_%@_%@", _commentOfId.stringValue, _toUser.global_key.length > 0? _toUser.global_key:@""];
- break;
- default:
- break;
- }
- }
- }
- return inputKey;
- }
- - (NSString *)inputStr{
- NSString *inputKey = [self inputKey];
- if (inputKey) {
- DebugLog(@"inputStr_get:%@",[[self shareInputStrDict] objectForKey:inputKey]);
- return [[self shareInputStrDict] objectForKey:inputKey];
- }
- return nil;
- }
- - (void)deleteInputData{
- NSString *inputKey = [self inputKey];
- DebugLog(@"inputKey_delegate:%@",inputKey);
- if (inputKey) {
- [[self shareInputStrDict] removeObjectForKey:inputKey];
- [[self shareInputMediaDict] removeObjectForKey:inputKey];
- }
- }
- - (void)saveInputStr{
- NSString *inputStr = _inputTextView.text;
- NSString *inputKey = [self inputKey];
- DebugLog(@"inputKey_save:%@",inputKey);
- if (inputKey && inputKey.length > 0) {
- if (inputStr && inputStr.length > 0) {
- [[self shareInputStrDict] setObject:inputStr forKey:inputKey];
- }else{
- [[self shareInputStrDict] removeObjectForKey:inputKey];
- }
- }
- }
- - (void)saveInputMedia{
- NSString *inputKey = [self inputKey];
- if (inputKey && inputKey.length > 0) {
- if (_mediaList.count > 0) {
- [[self shareInputMediaDict] setObject:_mediaList forKey:inputKey];
- }else{
- [[self shareInputMediaDict] removeObjectForKey:inputKey];
- }
- }
- }
- - (NSMutableArray *)inputMedia{
- NSString *inputKey = [self inputKey];
- if (inputKey) {
- return [[self shareInputMediaDict] objectForKey:inputKey];
- }
- return nil;
- }
- - (void)setToUser:(User *)toUser{
- _toUser = toUser;
- NSString *inputStr = [self inputStr];
- if (_inputTextView) {
- if (_contentType != UIMessageInputViewContentTypePriMsg) {
- self.placeHolder = _toUser? [NSString stringWithFormat:@"回復(fù) %@", _toUser.name]: @"撰寫評(píng)論";
- }else{
- self.placeHolder = @"請(qǐng)輸入私信內(nèi)容";
- }
- _inputTextView.selectedRange = NSMakeRange(0, _inputTextView.text.length);
- [_inputTextView insertText:inputStr? inputStr: @""];
- _mediaList = [self inputMedia];
- [self mediaListChenged];
- }
上面無(wú)非就是通過(guò)聊天對(duì)象的名字拼接成key值,然后對(duì)應(yīng)存儲(chǔ)當(dāng)前輸入框的字符串到全局static的字典中,然后是取出、刪除的幾個(gè)方法。
3、再看看那哪些地方調(diào)用了這些方法:
保 存的方法,放在frame重寫的方法里,因?yàn)檩斎肟驎?huì)隨著鍵盤的現(xiàn)實(shí)和隱藏而切換frame,不過(guò)我公司的項(xiàng)目一開(kāi)始聊天模塊是我同事開(kāi)發(fā)的,我發(fā)現(xiàn)他用 Masonry的布局代碼去變換輸入框的位置,選擇了布局約束也就意味著放棄了frame,所以何處調(diào)用save方法還是要根據(jù)實(shí)際需求和實(shí)際的編碼實(shí) 現(xiàn)。另外,其實(shí)在最開(kāi)始開(kāi)發(fā)這個(gè)輸入框的時(shí)候,可以考慮其運(yùn)作的周期:開(kāi)始編輯->正在編輯->結(jié)束編輯,這些運(yùn)作周期是可以實(shí)現(xiàn)出各自的方 法,就和一個(gè)控制器的生命周期一樣??傊悸泛芏?,做好是能實(shí)現(xiàn)出好管理好維護(hù)的邏輯。
然后找找刪除的方法,刪除的方法是放在將字符串發(fā)出去的最前面,因?yàn)橐呀?jīng)發(fā)送出去了,是可以將字典中存儲(chǔ)的元素刪除了去。
另外,在創(chuàng)建key的時(shí)候,這個(gè)key字符串是依賴當(dāng)前聊天對(duì)象的,因?yàn)楫?dāng)前輸入框的內(nèi)容要和當(dāng)前好友對(duì)象一一對(duì)應(yīng),不能我保存了當(dāng)前好友對(duì)應(yīng)的輸入框內(nèi)容,跳到別的好友卻又出現(xiàn)了一樣的內(nèi)容。所以key值需要依據(jù)當(dāng)前好友的字符串來(lái)決定,所以Coding源碼中重寫了ToUser屬性的set方法: