这篇文章将为大家详细讲解有关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如何实现搭建聊天页面”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。