由于工作需要,需要用到ios聊天页面,在网上搜了半天没有想要的,果断自己写一个,发个笔记
功能分析,模仿QQ聊天页面
输入框失去第一响应的情况:
1:点击页面
2:下滑页面
输入框成为第一响应的情况:
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; // 页面下滑,并且输入框还是第一响应的时候,控制器要失去第一响应 if (contentOffsetY > 10) { if (self.textfieldView.isFirst) { [self clickSelf]; } } // 页面上滑,控制器成为第一响应 if (contentOffsetY < - 10) { self.textfieldView.isFirst = YES; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // 关闭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]; // 旋转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]; } //键盘弹出时把消息列表tableView的高度设为(屏幕高度 - 输入框高度 - 键盘高度),同时输入框上移; //键盘消失时再把tableView的高度设为(屏幕高度 - 输入框的高度),同时输入框下移。 //这样可以完美解决聊天列表的上面的消息无法显示问题和键盘遮挡问题。 - (void)keyboardWillShow:(NSNotification*)notification { // 0.取出键盘动画的时间 CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; // 1.取得键盘最后的frame CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; // 2.计算控制器的view需要平移的距离 CGFloat transformY = keyboardFrame.origin.y - self.view.frame.size.height; // 3.执行动画 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; }]; } //失去第一响应 - (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]; } //关闭下拉和上拉控件的文字展示 - (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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍php网页版聊天软件实现代码,包括了php网页版聊天软件实现代码的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了php匿名聊天室的具体实现代码,供大家参考,具体内容如下 1. index.html 2. action.php 3. chat.sql 4. add.php 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
本文向大家介绍iOS实现聊天输入框功能,包括了iOS实现聊天输入框功能的使用技巧和注意事项,需要的朋友参考一下 经常使用微信聊天,没事儿就会想输入框的实现过程,所以抽空,也实现了一个输入框的功能; 经过封装,使用就非常的简单了,在需要的VC中,实现方法如下: 主要就是上面的添加,此时输入框就已经添加到当前的VC中;稍后会讲到里面的代理方法的作用; 工程结构如下图 主要是红色线标出的两个类,结构
本文向大家介绍go语言实现聊天服务器的示例代码,包括了go语言实现聊天服务器的示例代码的使用技巧和注意事项,需要的朋友参考一下 看了两天 go 语言,是时候练练手了。 go 的 routine(例程) 和 chan(通道) 简直是神器,实现多线程(在 go 里准确的来说是 多例程)简直不要太轻松。 于是动手码了一个傻瓜版的黑框聊天器。 server 端: 监听 TCP 连接;支持自定义客户端命令;
本文向大家介绍python 实现语音聊天机器人的示例代码,包括了python 实现语音聊天机器人的示例代码的使用技巧和注意事项,需要的朋友参考一下 前言 在不远的将来,实现一定程度上的语音支持将成为日常科技的基本要求,整合了语音识别的python程序提供了其他技术无法比拟的交互性和可访问性。最重要的是,在python程序中实现语音识别非常简单。整个代码实现下来还不到150行。 原理简介 许多现代语
本文向大家介绍php+webSoket实现聊天室示例代码(附源码),包括了php+webSoket实现聊天室示例代码(附源码)的使用技巧和注意事项,需要的朋友参考一下 最近在公司利用直播间搭建一个图文直播间时正好要用到chatsever,研究了一下html5的websocket 实现了双向通信,根据前人的经验折腾了几天弄了个聊天室,实现了发送图片,发送QQ表情,群聊私聊等功能,特地分享给各位新手参
本文向大家介绍python socket 聊天室实例代码详解,包括了python socket 聊天室实例代码详解的使用技巧和注意事项,需要的朋友参考一下 python socket 聊天室 简单的python socket聊天室就完成了 其中socket.AF_INET是固定的,好像是socket树,SOCK_DGRAM是udp协议。 这个核心是用bind绑定了一个端口,相当于自己在本地开启了一