由于工作需要,需要用到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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# iOS聊天页面
# iOS聊天页面实现
# iOS搭建聊天页面
# iOS输出手机系统版本号
# iOS利用UIScrollView实现图片的缩放实例代码
# React-Native实现ListView组件之上拉刷新实例(iOS和Android通用)
# iOS实现按钮点击选中与被选中切换功能
# IOS中多手势之间的冲突和解决办法
# IOS 开发之UILabel 或者 UIButton加下划线链接
# 输入框
# 设为
# 半天
# 弹出
# 发个
# 大家多多
# 时再
# 在网上
# userInfo
# keyboardFrame
# CGRectValue
# UIKeyboardFrameEndUserInfoKey
# doubleValue
# CGRect
# UIKeyboardAnimationDurationUserInfoKey
# indexPathForRow
# indexPath
# inSection
# atScrollPosition
# scrollToRowAtIndexPath
相关文章:
如何在橙子建站上传落地页?操作指南详解
如何高效配置香港服务器实现快速建站?
建站之星价格显示格式升级,你的预算足够吗?
阿里云网站制作公司,阿里云快速搭建网站好用吗?
建站之星安装失败:服务器环境不兼容?
长沙企业网站制作哪家好,长沙水业集团官方网站?
建站之星如何助力网站排名飙升?揭秘高效技巧
北京网站制作公司哪家好一点,北京租房网站有哪些?
宝塔建站助手安装配置与建站模板使用全流程解析
宁波自助建站系统如何快速打造专业企业网站?
儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?
南宁网站建设制作定制,南宁网站建设可以定制吗?
高端建站如何打造兼具美学与转化的品牌官网?
武汉网站制作费用多少,在武汉武昌,建面100平方左右的房子,想装暖气片,费用大概是多少啊?
寿县云建站:智能SEO优化与多行业模板快速上线指南
公司网站制作费用多少,为公司建立一个网站需要哪些费用?
,巨量百应是干嘛的?
制作表格网站有哪些,线上表格怎么弄?
网站制作壁纸教程视频,电脑壁纸网站?
网站制作员失业,怎样查看自己网站的注册者?
招贴海报怎么做,什么是海报招贴?
ppt在线制作免费网站推荐,有什么下载免费的ppt模板网站?
网页制作模板网站推荐,网页设计海报之类的素材哪里好?
孙琪峥织梦建站教程如何优化数据库安全?
大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?
建站之星安装路径如何正确选择及配置?
如何选择适配移动端的WAP自助建站平台?
TestNG的testng.xml配置文件怎么写
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
建站之星2.7模板:企业网站建设与h5定制设计专题
购物网站制作公司有哪些,哪个购物网站比较好?
如何在自有机房高效搭建专业网站?
建站之星体验版:智能建站系统+响应式设计,多端适配快速建站
商务网站制作工程师,从哪几个方面把握电子商务网站主页和页面的特色设计?
建站中国必看指南:CMS建站系统+手机网站搭建核心技巧解析
建站之星如何快速生成多端适配网站?
Android自定义listview布局实现上拉加载下拉刷新功能
h5网站制作工具有哪些,h5页面制作工具有哪些?
盘锦网站制作公司,盘锦大洼有多少5G网站?
官网建站费用明细查询_企业建站套餐价格及收费标准指南
装修招标网站设计制作流程,装修招标流程?
宝塔新建站点为何无法访问?如何排查?
如何配置支付宝与微信支付功能?
建站之星导航如何优化提升用户体验?
常州自助建站费用包含哪些项目?
音响网站制作视频教程,隆霸音响官方网站?
网站制作免费,什么网站能看正片电影?
如何通过IIS搭建网站并配置访问权限?
*请认真填写需求信息,我们会在24小时内与您取得联系。