由于工作需要,需要用到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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。
新闻热点
疑难解答