iOS實(shí)現(xiàn)搭建聊天頁面的實(shí)例代碼
由于工作需要,需要用到ios聊天頁面,在網(wǎng)上搜了半天沒有想要的,果斷自己寫一個(gè),發(fā)個(gè)筆記
功能分析,模仿QQ聊天頁面
輸入框失去第一響應(yīng)的情況:
1:點(diǎn)擊頁面
2:下滑頁面
輸入框成為第一響應(yīng)的情況:
1:開始輸入
2:上滑頁面最底部
控制器
// // WDPersonMessageDetailVC.m // WestDevelopment // // Created by wangtao on 2017/6/23. // Copyright © 2017年 xikaijinfu. All rights reserved. // #import "WDPersonMessageDetailVC.h" #import "WDPersonMessageDetailCell.h" #import "WDPersonMessageFooterCell.h" #import "WDPersonMessageDetailModel.h" #import <IQKeyboardManager.h> @interface WDPersonMessageDetailVC () @property (nonatomic, weak) WDPersonMessageFooterCell *textfieldView; @end @implementation WDPersonMessageDetailVC - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat contentOffsetY = scrollView.contentOffset.y; // 頁面下滑,并且輸入框還是第一響應(yīng)的時(shí)候,控制器要失去第一響應(yīng) if (contentOffsetY > 10) { if (self.textfieldView.isFirst) { [self clickSelf]; } } // 頁面上滑,控制器成為第一響應(yīng) if (contentOffsetY < - 10) { self.textfieldView.isFirst = YES; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // 關(guān)閉IQ鍵盤 [IQKeyboardManager sharedManager].enable = NO; [IQKeyboardManager sharedManager].enableAutoToolbar = NO; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.view endEditing:YES]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [IQKeyboardManager sharedManager].enableAutoToolbar = YES; [IQKeyboardManager sharedManager].enable = YES; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)loadView { UIScrollView *view = [[UIScrollView alloc] init]; view.frame = CGRectMake(0, 0, kMainScreenWidth, kMainScreenHeight); self.view = view; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [IQKeyboardManager sharedManager].enable = NO; [IQKeyboardManager sharedManager].enableAutoToolbar = NO; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; // 旋轉(zhuǎn)tableView self.tableView.transform = CGAffineTransformMakeScale (1, -1); self.tableView.tableHeaderView.transform = CGAffineTransformMakeScale (1, -1); self.tableView.tableFooterView.transform = CGAffineTransformMakeScale (1, -1); self.view.backgroundColor = WTHexColor(0xeaeaea); self.tableView.backgroundColor = WTHexColor(0xeaeaea); self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(50, 0, 0, 0); [self.tableView registerClass:[WDPersonMessageDetailCell class] forCellReuseIdentifier:WDPersonMessageDetailCellID]; [self.tableView registerClass:[WDPersonMessageFooterCell class] forHeaderFooterViewReuseIdentifier:WDPersonMessageFooterCellID]; [self.tableView wt_addTapTarget:self action:@selector(clickSelf)]; [self addFooter]; } //鍵盤彈出時(shí)把消息列表tableView的高度設(shè)為(屏幕高度 - 輸入框高度 - 鍵盤高度),同時(shí)輸入框上移; //鍵盤消失時(shí)再把tableView的高度設(shè)為(屏幕高度 - 輸入框的高度),同時(shí)輸入框下移。 //這樣可以完美解決聊天列表的上面的消息無法顯示問題和鍵盤遮擋問題。 - (void)keyboardWillShow:(NSNotification*)notification { // 0.取出鍵盤動(dòng)畫的時(shí)間 CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 1.取得鍵盤最后的frame CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; // 2.計(jì)算控制器的view需要平移的距離 CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height; // 3.執(zhí)行動(dòng)畫 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO]; WTWS(weakSelf); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView animateWithDuration:duration animations:^{ weakSelf.tableView.frame = CGRectMake(0, 0, kMainScreenWidth, kMainScreenHeight - keyboardFrame.size.height - 64); weakSelf.inputView.transform = CGAffineTransformMakeTranslation(0, transformY); }]; }); } - (void)keyboardWillHide:(NSNotification*)notification { CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; [UIView animateWithDuration:duration animations:^{ self.tableView.frame = CGRectMake(0, 0, kMainScreenWidth, kMainScreenHeight); self.view.transform = CGAffineTransformIdentity; }]; } //失去第一響應(yīng) - (void)clickSelf { [[NSNotificationCenter defaultCenter] postNotificationName:kMessageState object:@(YES)]; } - (void)addHeader { __unsafe_unretained __typeof(self) weakSelf = self; self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ [weakSelf loadData]; }]; [self.tableView.mj_header beginRefreshing]; } //關(guān)閉下拉和上拉控件的文字展示 - (void)addFooter { // [self addHeader]; [self loadData]; MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; [footer setTitle:@"" forState:MJRefreshStateIdle]; [footer setTitle:@"" forState:MJRefreshStatePulling]; [footer setTitle:@"" forState:MJRefreshStateRefreshing]; [footer setTitle:@"" forState:MJRefreshStateWillRefresh]; [footer setTitle:@"" forState:MJRefreshStateNoMoreData]; self.tableView.mj_footer = footer; } - (void)loadData { self.page = 1; NSDictionary *par = @{ kToken : [WTAccount shareAccount].token, kUserId : [WTAccount shareAccount].uid, kCurrentPage : @(self.page), kFriendId : self.friendId, }; [WDNetwork postkMyMessageDetailPhoneWithParameters:par modelClass:[WDPersonMessageDetailModel class] responseBlock:^(id dataObject, NSError *error) { if (!error && [[dataObject class] isSubclassOfClass:[NSArray class]]) { NSArray* reversedArray = [[dataObject reverseObjectEnumerator] allObjects]; self.dataArray = [NSMutableArray arrayWithArray:reversedArray]; [self.tableView reloadData]; self.page ++; if ([dataObject count] < 20) { [self.tableView.mj_header endRefreshing]; [self.tableView.mj_footer endRefreshingWithNoMoreData]; } else { [self.tableView.mj_header endRefreshing]; [self.tableView.mj_footer endRefreshing]; } } else { [self.tableView.mj_header endRefreshing]; [self.tableView.mj_footer endRefreshingWithNoMoreData]; } }]; } - (void)loadMoreData { NSDictionary *par = @{ kToken : [WTAccount shareAccount].token, kUserId : [WTAccount shareAccount].uid, kCurrentPage : @(self.page), kFriendId : self.friendId, }; [WDNetwork postkMyMessageDetailPhoneWithParameters:par modelClass:[WDPersonMessageDetailModel class] responseBlock:^(id dataObject, NSError *error) { if (!error && [[dataObject class] isSubclassOfClass:[NSArray class]]) { NSArray* reversedArray = [[dataObject reverseObjectEnumerator] allObjects]; [self.dataArray addObjectsFromArray:reversedArray]; [self.tableView reloadData]; self.page ++; if ([dataObject count] < 20) { [self.tableView.mj_footer endRefreshingWithNoMoreData]; } else { [self.tableView.mj_footer endRefreshing]; } } else { [self.tableView.mj_footer endRefreshingWithNoMoreData]; } }]; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 50; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return CGFLOAT_MIN; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { WDPersonMessageFooterCell *footer = [tableView dequeueReusableHeaderFooterViewWithIdentifier:WDPersonMessageFooterCellID]; self.textfieldView = footer; footer.contentView.transform = CGAffineTransformMakeScale (1, -1); WTWS(weakSelf); footer.clickSenderText = ^(NSString *text) { NSDictionary *par = @{ kToken : [WTAccount shareAccount].token, kUserId : [WTAccount shareAccount].uid, kComment : text, kFlag : @(11), kFriendId : weakSelf.friendId, }; [WDNetwork postkAddCommentPhoneWithParameters:par modelClass:[NSNull class] responseBlock:^(id dataObject, NSError *error) { if (!error && ([[dataObject objectForKey:kCode] integerValue] == 200)) { [weakSelf loadData]; weakSelf.textfieldView.sendSucceed = YES; } else if (!error && [dataObject objectForKey:kMsg]) { } }]; }; footer.resignFirstRes = ^{ weakSelf.tableView.frame = CGRectMake(0, 0, kMainScreenWidth, kMainScreenHeight - 64); weakSelf.view.transform = CGAffineTransformIdentity; }; return footer; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.dataArray.count; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { WDPersonMessageDetailModel *model = self.dataArray[indexPath.row]; return model.height; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { WDPersonMessageDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:WDPersonMessageDetailCellID forIndexPath:indexPath]; cell.model = self.dataArray[indexPath.row]; cell.contentView.transform = CGAffineTransformMakeScale (1, -1); return cell; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ @end
輸入框 UITableViewHeaderFooterView
// // WDPersonMessageFooterCell.h // WestDevelopment // // Created by wangtao on 2017/6/26. // Copyright © 2017年 xikaijinfu. All rights reserved. // #import "WDBaseTVHeaderFooterView.h" typedef void(^ClickSender_t)(NSString *text); typedef void(^ResignFirstResponder)(); @interface WDPersonMessageFooterCell : WDBaseTVHeaderFooterView @property (nonatomic, copy) ClickSender_t clickSenderText; @property (nonatomic, copy) ResignFirstResponder resignFirstRes; @property (nonatomic, assign) BOOL isFirst; @property (nonatomic, assign) BOOL sendSucceed; @end
// // WDPersonMessageFooterCell.m // WestDevelopment // // Created by wangtao on 2017/6/26. // Copyright © 2017年 xikaijinfu. All rights reserved. // #import "WDPersonMessageFooterCell.h" @interface WDPersonMessageFooterCell () <UITextFieldDelegate> @property (nonatomic, weak) UITextField *textField; @property (nonatomic, weak) UIView *line; @end @implementation WDPersonMessageFooterCell @synthesize isFirst = _isFirst; - (void)setupAll { self.contentView.backgroundColor = WTHexColor(0xf2f2f2); UITextField *textField = [[UITextField alloc] init]; textField.backgroundColor = kWhiteColor; [self.contentView addSubview:textField]; textField.delegate = self; self.textField = textField; textField.layer.cornerRadius = 3; textField.layer.masksToBounds = YES; textField.returnKeyType = UIReturnKeySend; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(messageState:) name:kMessageState object:nil]; UIView *line = [[UIView alloc] init]; line.backgroundColor = WTHexColor(0xdddddd); [self.contentView addSubview:line]; self.line = line; } - (void)messageState:(NSNotification *)noti { NSInteger state = [[noti object] boolValue]; if (state) { [self.textField resignFirstResponder]; if (self.resignFirstRes) { self.resignFirstRes(); } } } - (BOOL)textFieldShouldReturn:(UITextField *)textField { if (self.clickSenderText) { self.clickSenderText(self.textField.text); } return YES; } - (void)setIsFirst:(BOOL)isFirst { _isFirst = isFirst; if (isFirst) { [self.textField becomeFirstResponder]; } else { [self.textField resignFirstResponder]; } } - (BOOL)isFirst { if ([self.textField isFirstResponder]) { return YES; } return NO; } - (void)setSendSucceed:(BOOL)sendSucceed { self.textField.text = @""; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat padding = 10; self.textField.frame = CGRectMake(padding, padding, self.wt_width - padding * 2, self.wt_height - padding * 2); self.line.frame = CGRectMake(0, 0, self.wt_width, .5); } @end
消息cell
// // WDPersonMessageDetailCell.m // WestDevelopment // // Created by wangtao on 2017/6/23. // Copyright © 2017年 xikaijinfu. All rights reserved. // #import "WDPersonMessageDetailCell.h" #import "WDPersonMessageDetailModel.h" @interface WDPersonMessageDetailCell () @property (nonatomic, weak) UILabel *time; @property (nonatomic, weak) UIImageView *icon; @property (nonatomic, weak) UILabel *detail; @property (nonatomic, weak) UIView *baseView; @end @implementation WDPersonMessageDetailCell - (void)setupAll { self.selectionStyle = UITableViewCellSelectionStyleNone; self.backgroundColor = WTHexColor(0xeaeaea); self.contentView.backgroundColor = WTHexColor(0xeaeaea); UILabel *time = [UILabel labelWithText:@"" textColor:WTHexColor(0xaaaaaa) textAlignment:NSTextAlignmentCenter font:12 backgroundColor:kClearColor]; [self.contentView addSubview:time]; self.time = time; UIImageView *icon = [[UIImageView alloc] init]; [self.contentView addSubview:icon]; icon.image = [UIImage imageNamed:kDefault]; self.icon = icon; self.icon.layer.cornerRadius = 35 / 2; self.icon.layer.masksToBounds = YES; UIView *baseView = [[UIView alloc] init]; [self.contentView addSubview:baseView]; self.baseView = baseView; baseView.layer.masksToBounds = YES; baseView.layer.cornerRadius = 4; UILabel *detail = [UILabel labelWithText:@"" textColor:kBlackColor textAlignment:NSTextAlignmentLeft font:13 backgroundColor:kClearColor]; [baseView addSubview:detail]; self.detail = detail; detail.numberOfLines = 0; } - (void)setModel:(WDPersonMessageDetailModel *)model { _model = model; if ([model.isShow isEqualToString:@"1"]) { self.time.text = model.addTime; self.time.hidden = NO; self.time.frame = CGRectMake(0, 0, kMainScreenWidth, 20); } else { self.time.text = @""; self.time.hidden = YES; self.time.frame = CGRectZero; } self.time.text = model.addTime; [self.icon wt_setImageWithUrlString:model.headImg placeholderString:@"me_icon"]; self.detail.text = model.comment; if ([model.userId isEqualToString:[WTAccount shareAccount].uid]) { self.detail.textColor = kBlackColor; self.baseView.backgroundColor = kWhiteColor; self.icon.frame = CGRectMake(kPadding, self.time.wt_bottom + kPadding, 35, 35); self.baseView.frame = CGRectMake(self.icon.wt_right + kPadding, self.icon.wt_top, model.commentW, model.commentH); self.detail.frame = CGRectMake(kPadding, kPadding, model.commentW - kPadding * 2, model.commentH - kPadding * 2); } else { self.detail.textColor = kWhiteColor; self.baseView.backgroundColor = kHomeColor; self.icon.frame = CGRectMake(kMainScreenWidth - 35 - kPadding, self.time.wt_bottom + kPadding, 35, 35); self.baseView.frame = CGRectMake(self.icon.wt_left - kPadding - model.commentW, self.icon.wt_top, model.commentW, model.commentH); self.detail.frame = CGRectMake(kPadding, kPadding, model.commentW - kPadding * 2, model.commentH - kPadding * 2); } } @end
模型
// // WDPersonMessageDetailModel.m // WestDevelopment // // Created by wangtao on 2017/6/23. // Copyright © 2017年 xikaijinfu. All rights reserved. // #import "WDPersonMessageDetailModel.h" @implementation WDPersonMessageDetailModel - (CGFloat)commentW { if (_commentW == 0) { _commentW = [self.comment wt_calculateStringSizeWithFontOfSize:13 maxWidth:kMainScreenWidth / 2].width + 20; } return _commentW; } - (CGFloat)commentH { if (_commentH == 0) { CGFloat textH = [self.comment wt_calculateStringSizeWithFontOfSize:13 maxWidth:kMainScreenWidth / 2].height; // 一行字體是15高,一行的情況就和頭像一樣高 _commentH = (textH < 20) ? 35 : (textH + 20); } return _commentH; } - (CGFloat)height { if (_height == 0) { _height = self.commentH + 20; if ([self.isShow isEqualToString:@"1"]) { _height += 20; } } return _height; } @end
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS開源一個(gè)簡(jiǎn)單的訂餐app UI框架
這篇文章主要介紹了iOS開源一個(gè)簡(jiǎn)單的訂餐app UI框架,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10iOS開發(fā)中實(shí)現(xiàn)新聞圖片的無限循環(huán)展示的方法
這篇文章主要介紹了iOS開發(fā)中實(shí)現(xiàn)新聞圖片的無限循環(huán)展示的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12iOS數(shù)據(jù)持久化KeyChain數(shù)據(jù)操作詳解
這篇文章主要為大家介紹了iOS數(shù)據(jù)持久化KeyChain,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02詳解IOS開發(fā)中圖片上傳時(shí)兩種圖片壓縮方式的比較
這篇文章主要介紹了IOS開發(fā)中圖片上傳時(shí)兩種圖片壓縮方式的比較,需要的朋友可以參考下2017-03-03iOS多線程應(yīng)用開發(fā)中自定義NSOperation類的實(shí)例解析
這篇文章主要介紹了iOS多線程應(yīng)用開發(fā)中自定義NSOperation類的實(shí)例解析,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-01-01剖析iOS開發(fā)中Cocos2d-x的內(nèi)存管理相關(guān)操作
這篇文章主要介紹了剖析iOS開發(fā)中Cocos2d-x的內(nèi)存管理相關(guān)操作,Cocos2d-x是開發(fā)游戲的利器,需要的朋友可以參考下2015-10-10IOS 基礎(chǔ)之nil,NULL,NSNULL區(qū)別詳解
這篇文章主要介紹了IOS 基礎(chǔ)之nil,NULL,NSNULL區(qū)別詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04iOS應(yīng)用開發(fā)中對(duì)UIImage進(jìn)行截取和縮放的方法詳解
這篇文章主要介紹了iOS應(yīng)用開發(fā)中對(duì)UIImage進(jìn)行截取和縮放的方法,分別講解了如何截取指定區(qū)域大小的UIImage以及縮放到指定大小和等比縮放的具體操作過程,需要的朋友可以參考下2016-04-04