iOS实现搭建聊天页面的实例代码

2020-01-20 23:56:01王旭

由于工作需要,需要用到ios聊天页面,在网上搜了半天没有想要的,果断自己写一个,发个笔记

功能分析,模仿QQ聊天页面

输入框失去第一响应的情况:

1:点击页面

2:下滑页面

输入框成为第一响应的情况:

1:开始输入

2:上滑页面最底部

iOS聊天页面,iOS聊天页面实现,iOS搭建聊天页面

控制器


//
// 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