学习如何进阶使用UITableView,带给应用更高级的观感(look and feel)

●    学习如何开发自己定制的UITableView类,模仿iMessage应用的观感

●    为一个基于分组的UITableView实现下钻逻辑

在iOS应用中呈现数据时,UITableView可能是最经常使用的用户界面对象。在本章中,将学习到以超越标准实现的方式使用UITableView,并理解UITableView类的工作方式。你会创建一个聊天视图控制器,它支持定制的单元格和灵活的行高,以及下钻功能的实现,能够将多个对象的多个分类进行分组,从而生成一个高级的用户界面。最后,你会为表格视图的实现添加搜索功能。

2.1  理解UITableView

UITableView直接继承于UIScrollView类,从而给它带来直向(译者注:横向和纵向)滚动的能力。当想要使用UITableView时,必须首先创建UITableView类的实例,将它指向UIView控件而使其可见,并且建立一个datasource对象和一个负责与UITableView进行交互的delegate对象。

2.1.1  datasource和delegate

每一个UITableView都需要datasource和delegate这两个对象。datasource对象为UITableView提供数据。通常,datasource对象使用NSArray类或者NSDictionary类在内部存储数据,并且根据需要将数据提供给表视图。delegate对象必须实现UITableViewDelegate和UITableViewDataSource这两个协议。

UITableViewDelegate协议定义了几个方法,delegate对象需要实现其中至少三个方法。

delegate对象必须实现的方法有:

●     tableview:numberOfRowsInSection:

●     numberOfSectionsInTableView:

●     tableview:cellForRowAtIndexPath:

启动Xcode开发环境,使用Single View ApplicationProject模板创建新项目,并使用如图2-1中所示的配置将其命名为PlainTable。

图1

使用Interface Builder工具打开YDViewController.xib文件,并将一个UITableView控件添加到该窗口中。使用Assistant Editor工具为这个UITableView控件创建一个属性。也需要设置Referencing Outlets一栏中的datasource和delegate指向UITableView对象。确保YDViewController.xib文件看起来如图2-2中所示。

图2

打开YDViewController.h文件,创建名为rowData的NSMutableArray对象充当datasource,如代码清单2-1中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-1  Chapter2/PlainTable/YDViewController.h
  2. #import <UIKit/UIKit.h>
  3. @interface YDViewController : UIViewController
  4. @property (weak, nonatomic) IBOutlet UITableView *mTableView;
  5. @property(nonatomic,strong) NSMutableArray* rowData;
  6. @end
  7. </span>

打开YDViewController.m文件,实现如代码清单2-2中所示的代码,关于这段代码,会在代码清单后详细说明。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-2  Chapter2/PlainTable/YDViewController.m
  2. #import "YDViewController.h"
  3. @interface YDViewController ()
  4. @end
  5. @implementation YDViewController
  6. - (void)viewDidLoad
  7. {
  8. [super viewDidLoad];
  9. // Do any additional setup after loading the view, typically from a nib.
  10. [self loadData];
  11. }
  12. -(void)loadData
  13. {
  14. if (self.rowData!=nil)
  15. {
  16. [self.rowData removeAllObjects];
  17. self.rowData=nil;
  18. }
  19. self.rowData = [[NSMutableArray alloc] init];
  20. for (int i=0 ; i<100;i++)
  21. {
  22. [self.rowData addObject:[NSString stringWithFormat:@"Row: %i",i]];
  23. }
  24. //now my datasource if populated let's reload the tableview
  25. [self.mTableView reloadData];
  26. }
  27. #pragma mark UITableView delegate
  28. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  29. return 1;
  30. }
  31. - (NSInteger)tableView:(UITableView *)tableView
  32. numberOfRowsInSection:(NSInteger)section {
  33. return [self.rowData count];
  34. }
  35. - (UITableViewCell *)tableView:(UITableView *)tableView
  36. cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  37. static NSString *CellIdentifier = @"Cell";
  38. UITableViewCell *cell = (UITableViewCell *)[tableView
  39. dequeueReusableCellWithIdentifier:CellIdentifier];
  40. if (cell == nil) {
  41. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
  42. reuseIdentifier:CellIdentifier];
  43. }
  44. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  45. cell.textLabel.text = [self.rowData objectAtIndex:indexPath.row];
  46. return cell;
  47. }
  48. -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
  49. (NSIndexPath *)indexPath
  50. {
  51. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  52. }
  53. - (void)didReceiveMemoryWarning
  54. {
  55. [super didReceiveMemoryWarning];
  56. // Dispose of any resources that can be recreated.
  57. }
  58. @end
  59. </span>

下面对这段代码进行分解,向你解释代码中各方法的作用。

在viewDidLoad方法中,调用了本地方法loadData,该方法创建了一个带有100个记录的NSMutableArray对象,并将reloadData消息发送给self.mTableView对象。

reloadData方法迫使mTableView对象通过调用delegate方法重新加载数据,并更新用户界面。

在#pragma mark UITableView delegate标记语句之后,需要实现表视图运行所必须的delegate对象的最小方法集合。

调用numberOfSectionsInTableView: 这个delegate方法来决定UITableView控件的section的数量。如果使用UITableViewStylePlain风格,UITableView控件的section数通常是1。后面将会学习到带有下钻功能的例子,如果使用例子中那种风格的section,则需要返回实际的section的数量。

当渲染单元格时,会调用tableview:cellForRowAtIndexPath:这个delegate方法。这个方法恰好是布局UITableViewCell的地方(UITableView中的一行)。现在,先简单地创建一个UITableviewCell,如果单元格仍然可用的话,试着在内存中重用它。

为了显示rowData数组中的正确的行,需要将[rowData objectAtIndex :indexPath.row];方法的返回值赋给cell.textLabel.text属性。

当用户以单击某行的方式选择该行时,会调用tableview:didSelectRowAtIndexPath:这个delegate方法。deselectRowAtIndexPath:animated:的delegate方法会取消这一行的选择,因此单元格不会保持高亮的状态。

如果想要保持选择状态仍然可见,那么请省略这行代码。

当应用运行时,结果如图2-3中所示。

图3

2.1.2  滚动

由于UITableView对象继承于UIScrollView类,因此它本身拥有完全的滚动功能。然而,在某些情况下,例如在UITableView中添加一个新行,或者删除一行时,可能要直接滚动到UITableView中的某个位置。

可以通过调用UITableView的scrollToRowAtIndexPath:atScrollPosition:animated:方法,获得UITableView上基于代码的滚动效果。这个方法传入的第一个参数是NSIndexPath类型的对象。NSIndexPath对象表示到嵌套数组集合树上的某一特定节点的路径。这个路径称为索引路径。在iOS应用中,用NSIndexPath对象来确定到表格视图内的行和section的路径。调用NSIndexPath类的indexPathForRow:inSection:方法,传入行和section的索引数字,通过这种方式可以创建NSIndexPath的实例。

启动Xcode开发环境,使用SingleView Application Project模板创建一个新项目,并使用如图2-4中所示的配置,将其命名为ScrollingTable。

使用Interface Builder工具打开YDViewController.xib文件,创建一个用户界面,如图2-5中所示。

图4
图5

如代码清单2-3中所示,建立YDViewController.h文件。作为前一个例子的补充,为引入的两个UIButton添加两个动作。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-3  Chapter2/ScrollingTable/YDViewController.h
  2. #import <UIKit/UIKit.h>
  3. @interface YDViewController : UIViewController
  4. @property (weak, nonatomic) IBOutlet UITableView *mTableView;
  5. @property(nonatomic,strong) NSMutableArray* rowData;
  6. - (IBAction)scrollToTop:(UIButton *)sender;
  7. - (IBAction)scrollToBottom:(UIButton *)sender;
  8. @end
  9. </span>

YDViewController.m文件的实现与前面的代码清单2-2中的类似,唯一的区别在于此时scrollToTop:和scrollToBottom:这两个方法的实现如代码清单2-4中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-4  Chapter2/ScrollingTable/YDViewController.m
  2. #import "YDViewController.h"
  3. @interface YDViewController ()
  4. @end
  5. @implementation YDViewController
  6. - (void)viewDidLoad
  7. {
  8. [super viewDidLoad];
  9. // Do any additional setup after loading the view, typically from a nib.
  10. [self loadData];
  11. }
  12. -(void)loadData
  13. {
  14. if (self.rowData!=nil)
  15. {
  16. [self.rowData removeAllObjects];
  17. self.rowData=nil;
  18. }
  19. self.rowData = [[NSMutableArray alloc] init];
  20. for (int i=0 ; i<100;i++)
  21. {
  22. [self.rowData addObject:[NSString stringWithFormat:@"Row: %i",i]];
  23. }
  24. //now my datasource if populated let's reload the tableview
  25. [self.mTableView reloadData];
  26. }
  27. #pragma mark UITableView delegates
  28. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  29. return 1;
  30. }
  31. - (NSInteger)tableView:(UITableView *)tableView
  32. numberOfRowsInSection:(NSInteger)section {
  33. return [self.rowData count];
  34. }
  35. - (UITableViewCell *)tableView:(UITableView *)tableView
  36. cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  37. static NSString *CellIdentifier = @"Cell";
  38. UITableViewCell *cell = (UITableViewCell *)[tableView
  39. dequeueReusableCellWithIdentifier:CellIdentifier];
  40. if (cell == nil) {
  41. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
  42. reuseIdentifier:CellIdentifier];
  43. }
  44. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  45. cell.textLabel.text = [self.rowData objectAtIndex:indexPath.row];
  46. return cell;
  47. }
  48. -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
  49. (NSIndexPath *)indexPath
  50. {
  51. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  52. }
  53. - (void)didReceiveMemoryWarning
  54. {
  55. [super didReceiveMemoryWarning];
  56. // Dispose of any resources that can be recreated.
  57. }
  58. - (IBAction)scrollToTop:(UIButton *)sender
  59. {
  60. NSIndexPath *topRow = [NSIndexPath indexPathForRow:0 inSection:0];
  61. [self.mTableView scrollToRowAtIndexPath:topRow
  62. atScrollPosition:UITableViewScrollPositionTop animated:YES];
  63. }
  64. - (IBAction)scrollToBottom:(UIButton *)sender
  65. {
  66. NSIndexPath *bottomRow = [NSIndexPath indexPathForRow:
  67. [self.rowData count]-1 inSection:0];
  68. [self.mTableView scrollToRowAtIndexPath:bottomRow
  69. atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  70. }
  71. @end
  72. </span>

在scrollToTop:方法中,创建一个NSIndexPath对象的实例,把indexPathForRow的值置为0,可以将表视图滚动至顶部。在scrollToBottom:方法中,使用[self.rowData count]-1的值创建NSIndexPath实例,可以将表视图滚动至底部。

用所创建的NSIndexPath对象调用scrollToRowAtIndexPath:atScrollPosition:animated:方法时,mTableView控件既可以滚动到表格的顶部,也可以滚动到表格的底部。

这一实现的结果如图2-6和图2-7中所示。

图6                                                                                                                                                                                                            图7

2.2  构建聊天视图控制器

在本节中,将开发一个聊天视图控制器模拟iMessage以及其他即时通信应用的行为。为此,将学习如何使用灵活的单元格高度和定制的单元格创建一个定制的UITableView的实例。

最终的应用看起来如图2-8所示。

启动Xcode开发环境,用Single View ApplicationProject模板创建一个新项目,使用如图2-9所示的配置,将其命名为YDChatApp。

本例中所使用的图片,可以从本章的下载文件中获得。

YDViewController类会呈现即将开发的定制的UITableView,而且不使用Interface Builder工具开发。所有的UI代码是YDViewController.m文件的一个组成部分。

2.2.1  构建datasource

你将不会使用标准的UITableView,但是为了支持各种不同的聊天泡泡和section,会创建带有特定行为的定制的UITableView对象,并且使用定制的单元格。基于这个原因,开始时会编码实现一个定制的datasource对象,并被挂接到定制的UITableView上。创建一个继承于NSObject类的新协议,将其命名为YDChatTableViewDataSource。协议的源代码如代码清单2-5中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-5  Chapter2/YDChatApp/YDChatTableViewDataSource.h
  2. #import <Foundation/Foundation.h>
  3. @class YDChatData;
  4. @class YDChatTableView;
  5. @protocol YDChatTableViewDataSource <NSObject>
  6. - (NSInteger)rowsForChatTable:(YDChatTableView *)tableView;
  7. - (YDChatData *)chatTableView:(YDChatTableView *)tableView
  8. dataForRow:(NSInteger)row;
  9. @end
  10. </span>

这个协议直接继承于NSObject类,其中定义了必须在YDViewController类里实现的两个方法,定义定制的UITableView的地方就是这里。

2.2.2  构建聊天数据对象

为了使生活更轻松,定义一个名为YDChatData的对象,用来保存一条聊天消息的相关信息。可以用聊天的用户、时间戳、文字或者图片来初始化这个对象。枚举类型YDChatType有两种可能的值,ChatTypeMine和ChatTypeSomeone,用来负责聊天消息在UITableView上的位置。创建一个继承于NSObject的新的Objective-C类,将其命名为YDChatData。

YDChatData.h文件的源代码如代码清单2-6中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-6  Chapter2/YDChatApp/YDChatData.h
  2. #import <Foundation/Foundation.h>
  3. @class YDChatUser;
  4. //enumerator to identify the chattype
  5. typedef enum _YDChatType
  6. {
  7. ChatTypeMine = 0,
  8. ChatTypeSomeone = 1
  9. }   YDChatType;
  10. @interface YDChatData : NSObject
  11. @property (readonly, nonatomic) YDChatType type;
  12. @property (readonly, nonatomic, strong) NSDate *date;
  13. @property (readonly, nonatomic, strong) UIView *view;
  14. @property (readonly, nonatomic) UIEdgeInsets insets;
  15. @property (nonatomic,strong) YDChatUser *chatUser;
  16. //custom initializers
  17. + (id)dataWithText:(NSString *)text date:(NSDate *)date
  18. type:(YDChatType)type andUser:(YDChatUser *)_user;
  19. + (id)dataWithImage:(UIImage *)image date:(NSDate *)date
  20. type:(YDChatType)type andUser:(YDChatUser *)_user;
  21. + (id)dataWithView:(UIView *)view date:(NSDate *)date
  22. type:(YDChatType)type andUser:(YDChatUser *)_user
  23. insets:(UIEdgeInsets)insets;
  24. @end
  25. </span>

在类的实现中,实现几个不同的初始化方法,如代码清单2-7中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-7  Chapter2/YDChatApp/YDChatData.m
  2. import "YDChatData.h"
  3. #import <QuartzCore/QuartzCore.h>
  4. @implementation YDChatData
  5. //create some constant UIEdgeInsets to property align text and images
  6. const UIEdgeInsets textInsetsMine = {5, 10, 11, 17};
  7. const UIEdgeInsets textInsetsSomeone = {5, 15, 11, 10};
  8. const UIEdgeInsets imageInsetsMine = {11, 13, 16, 22};
  9. const UIEdgeInsets imageInsetsSomeone = {11, 18, 16, 14};
  10. #pragma initializers
  11. + (id)dataWithText:(NSString *)text date:(NSDate *)date type:(YDChatType)type
  12. andUser:(YDChatUser *)_user
  13. {
  14. return [[YDChatData alloc] initWithText:text date:date
  15. type:type andUser:_user];
  16. }
  17. •(id)initWithText:(NSString *)text date:(NSDate *)date type:(YDChatType)type andUser:(YDChatUser *)_user
  18. {
  19. UIFont* font = [UIFont boldSystemFontOfSize:12];
  20. int width = 225, height = 10000.0;
  21. NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
  22. [atts setObject:font forKey:NSFontAttributeName];
  23. CGRect size = [text boundingRectWithSize:CGSizeMake(width, height)
  24. options:NSStringDrawingUsesLineFragmentOrigin
  25. attributes:atts
  26. context:nil];
  27. UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.size.width, size.size.height)];
  28. label.numberOfLines = 0;
  29. label.lineBreakMode = NSLineBreakByWordWrapping;
  30. label.text = (text ? text : @"");
  31. label.font = font;
  32. label.backgroundColor = [UIColor clearColor];
  33. UIEdgeInsets insets = (type == ChatTypeMine ? textInsetsMine : textInsetsSomeone);
  34. return [self initWithView:label date:date type:type andUser:_user   insets:insets];
  35. }
  36. •(id)initWithImage:(UIImage *)image date:(NSDate *)date type:(YDChatType)type
  37. andUser:(YDChatUser *)_user
  38. {
  39. CGSize size = image.size;
  40. if (size.width > 220)
  41. {
  42. size.height /= (size.width / 220);
  43. size.width = 220;
  44. }
  45. UIImageView *imageView = [[UIImageView alloc] initWithFrame:
  46. CGRectMake(0, 0, size.width, size.height)];
  47. imageView.image = image;
  48. imageView.layer.cornerRadius = 5.0;
  49. imageView.layer.masksToBounds = YES;
  50. UIEdgeInsets insets =
  51. (type == ChatTypeMine ? imageInsetsMine : imageInsetsSomeone);
  52. return [self initWithView:imageView date:date type:type andUser:_user
  53. insets:insets];
  54. }
  55. + (id)dataWithView:(UIView *)view date:(NSDate *)date type:(YDChatType)type
  56. andUser:(YDChatUser *)_user insets:(UIEdgeInsets)insets
  57. {
  58. return [[YDChatData alloc] initWithView:view date:date type:type
  59. andUser:_user   insets:insets];
  60. }
  61. •(id)initWithView:(UIView *)view date:(NSDate *)date type:(YDChatType)type
  62. andUser:(YDChatUser *)_user insets:(UIEdgeInsets)insets
  63. {
  64. self = [super init];
  65. if (self)
  66. {
  67. _chatUser = _user;
  68. _view = view;
  69. _date = date;
  70. _type = type;
  71. _insets = insets;
  72. }
  73. return self;
  74. }
  75. @end
  76. </span>

2.2.3  构建定制的UITableView控件

创建一个名为YDChatTableView的新的Objective-C类,继承于UITableView类,并且实现了名为ChatBubbleTypingType的枚举类型和需要的属性,如代码清单2-8中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-8  Chapter2/YDChatApp/YDChatTableView.h
  2. #import <UIKit/UIKit.h>
  3. #import "YDChatTableViewDataSource.h"
  4. #import "YDChatTableViewCell.h"
  5. //enumerator to identify the bubble type
  6. typedef enum _ChatBubbleTypingType
  7. {
  8. ChatBubbleTypingTypeNobody = 0,
  9. ChatBubbleTypingTypeMe = 1,
  10. ChatBubbleTypingTypeSomebody = 2
  11. } ChatBubbleTypingType;
  12. @interface YDChatTableView : UITableView
  13. @property (nonatomic, assign) id<YDChatTableViewDataSource> chatDataSource;
  14. @property (nonatomic) NSTimeInterval snapInterval;
  15. @property (nonatomic) ChatBubbleTypingType typingBubble;
  16. @end
  17. </span>

在YDChatTableView类的实现中,私有接口遵从于UITableViewDelegate和UITable- ViewDataSource这两个协议,在这里还定义一个名为bubbleSection的属性。

初始化方法为UITableView设置了默认属性,例如背景颜色、delegate和datasource属性等。重写reloadData方法,并编写你自己的代码,从而在YDChatTableView中加载数据。

另外,必须重写numberOfSectionsInTableView、tableview:numberOfRowsInSection:、tableview:heightForRowAtIndexPath:和tableview:cellForRowAtIndexPath:这几个方法。tableview:cellForRowAtIndexPath:方法创建并返回一个YDChatHeaderTableViewCell对象,或者是一个YDChatTableViewCell对象。

如果正在显示的单元格是首行,那么tableview:heightForRowAtIndexPath:方法就会返回YDChatHeaderTableViewCell控件的高度,或者根据这一特定的数据行与之相关的YDChatData对象,计算出高度并返回。

完整的实现如代码清单2-9中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-9  Chapter2/YDChatApp/YDChatTableView.m
  2. #import "YDChatTableView.h"
  3. #import "YDChatData.h"
  4. #import "YDChatHeaderTableViewCell.h"
  5. @interface YDChatTableView ()<UITableViewDelegate, UITableViewDataSource>
  6. @property (nonatomic, retain) NSMutableArray *bubbleSection;
  7. @end
  8. @implementation YDChatTableView
  9. - (void)initializer
  10. {
  11. self.backgroundColor = [UIColor clearColor];
  12. self.separatorStyle = UITableViewCellSeparatorStyleNone;
  13. self.delegate = self;
  14. self.dataSource = self;
  15. //the snap interval in seconds implements a headerview to seperate chats
  16. self.snapInterval = 60 * 60 * 24; //one day
  17. self.typingBubble = ChatBubbleTypingTypeNobody;
  18. }
  19. - (id)init
  20. {
  21. self = [super init];
  22. if (self) [self initializer];
  23. return self;
  24. }
  25. - (id)initWithFrame:(CGRect)frame
  26. {
  27. self = [super initWithFrame:frame];
  28. if (self) [self initializer];
  29. return self;
  30. }
  31. - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
  32. {
  33. self = [super initWithFrame:frame style:UITableViewStylePlain];
  34. if (self) [self initializer];
  35. return self;
  36. }
  37. #pragma mark - Override
  38. - (void)reloadData
  39. {
  40. self.showsVerticalScrollIndicator = NO;
  41. self.showsHorizontalScrollIndicator = NO;
  42. self.bubbleSection = nil;
  43. int count = 0;
  44. self.bubbleSection = [[NSMutableArray alloc] init];
  45. if (self.chatDataSource && (count = [self.chatDataSource
  46. rowsForChatTable:self]) > 0)
  47. {
  48. NSMutableArray *bubbleData = [[NSMutableArray alloc]
  49. initWithCapacity:count];
  50. for (int i = 0; i < count; i++)
  51. {
  52. NSObject *object = [self.chatDataSource
  53. chatTableView:self dataForRow:i];
  54. assert([object isKindOfClass:[YDChatData class]]);
  55. [bubbleData addObject:object];
  56. }
  57. [bubbleData sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
  58. {
  59. YDChatData *bubbleData1 = (YDChatData *)obj1;
  60. YDChatData *bubbleData2 = (YDChatData *)obj2;
  61. return [bubbleData1.date compare:bubbleData2.date];
  62. }];
  63. NSDate *last = [NSDate dateWithTimeIntervalSince1970:0];
  64. NSMutableArray *currentSection = nil;
  65. for (int i = 0; i < count; i++)
  66. {
  67. YDChatData *data = (YDChatData *)[bubbleData objectAtIndex:i];
  68. if ([data.date timeIntervalSinceDate:last] > self.snapInterval)
  69. {
  70. currentSection = [[NSMutableArray alloc] init];
  71. [self.bubbleSection addObject:currentSection];
  72. }
  73. [currentSection addObject:data];
  74. last = data.date;
  75. }
  76. }
  77. [super reloadData];
  78. }
  79. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  80. {
  81. int result = [self.bubbleSection count];
  82. if (self.typingBubble != ChatBubbleTypingTypeNobody) result++;
  83. return result;
  84. }
  85. •(NSInteger)tableView:(UITableView *)tableView
  86. numberOfRowsInSection:(NSInteger)section
  87. {
  88. if (section >= [self.bubbleSection count]) return 1;
  89. return [[self.bubbleSection objectAtIndex:section] count] + 1;
  90. }
  91. •(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:
  92. (NSIndexPath *)indexPath
  93. {
  94. // Header
  95. if (indexPath.row == 0)
  96. {
  97. return [YDChatHeaderTableViewCell height];
  98. }
  99. YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
  100. objectAtIndex:indexPath.row - 1];
  101. return MAX(data.insets.top + data.view.frame.size.height +
  102. data.insets.bottom, 52);
  103. }
  104. •(UITableViewCell *)tableView:(UITableView *)tableView
  105. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  106. {
  107. // Header based on snapInterval
  108. if (indexPath.row == 0)
  109. {
  110. static NSString *cellId = @"HeaderCell";
  111. YDChatHeaderTableViewCell *cell = [tableView
  112. dequeueReusableCellWithIdentifier:cellId];
  113. YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
  114. objectAtIndex:0];
  115. if (cell == nil) cell = [[YDChatHeaderTableViewCell alloc] init];
  116. cell.date = data.date;
  117. return cell;
  118. }
  119. // Standard
  120. static NSString *cellId = @"ChatCell";
  121. YDChatTableViewCell *cell = [tableView
  122. dequeueReusableCellWithIdentifier:cellId];
  123. YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
  124. objectAtIndex:indexPath.row - 1];
  125. if (cell == nil) cell = [[YDChatTableViewCell alloc] init];
  126. cell.data = data;
  127. return cell;
  128. }
  129. @end
  130. </span>

2.2.4  灵活的单元格高度

由于聊天消息具有图片和文本,这会使每个单元格的宽度和高度发生变化,因此表格可以是不相同的,并且需要根据单元格的内容进行计算。在重写的tableview:heightForRow- AtIndexPath:方法中,存在一个判断条件,如果当前的单元格是标题单元格,那么就返回标题单元格的高度;如果这个单元格是一个普通的聊天单元格,那么就获取与该单元格相连的YDChatData对象,并计算用作显示单元格的相关UIEdgeInset变量的最大值。下面的代码片段在YDChatTableView类中实现,负责灵活地返回单元格的高度。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">(float)tableView:(UITableView *)tableView
  2. heightForRowAtIndexPath:(NSIndexPath *)indexPath
  3. {
  4. // Header
  5. if (indexPath.row == 0)
  6. {
  7. return [YDChatHeaderTableViewCell height];
  8. }
  9. YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
  10. objectAtIndex:indexPath.row - 1];
  11. return MAX(data.insets.top + data.view.frame.size.height +
  12. data.insets.bottom, 52);
  13. }
  14. </span>

2.2.5  开发定制的单元格

为了正确显示聊天数据,需要两种不同的定制的UITableViewCell对象,它们都继承于UITableViewCell类。

一种用以显示标题头,在这种情况下,就是显示与snapInterval属性相关的日期和时间编组。另外一种用以显示YDChatData对象中保存的聊天消息。前一种表格对象有一个名为height返回值类型为CGFloat的静态方法,返回这个UITableViewCell的高度,还有一个日期类型的属性,因而日期和时间可以从snapInterval属性中获得。创建一个名为YDChatTableViewHeaderCell的新的Objective-C类,打开YDChatTableViewHeaderCell.h文件,应用如代码清单2-10中所示的代码。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-10  Chapter2/YDChatApp/YDChatTableViewHeaderCell.h
  2. #import <UIKit/UIKit.h>
  3. @interface YDChatHeaderTableViewCell : UITableViewCell
  4. + (CGFloat)height;
  5. @property (nonatomic, strong) NSDate *date;
  6. @end
  7. </span>
[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">
  2. </span>

YDChatTableViewHeaderCell类的实现简单地返回30.0作为height方法的返回值。setDate方法接收一个日期对象,并创建UILabel控件,将其添加到视图上,用以显示section的日期-时间戳。实现如代码清单2-11中所示的代码。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-11  Chapter2/YDChatApp/YDChatTableViewHeaderCell.m
  2. #import "YDChatHeaderTableViewCell.h"
  3. @interface YDChatHeaderTableViewCell ()
  4. @property (nonatomic, retain) UILabel *label;
  5. @end
  6. @implementation YDChatHeaderTableViewCell
  7. + (CGFloat)height
  8. {
  9. return 30.0;
  10. }
  11. - (void)setDate:(NSDate *)value
  12. {
  13. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  14. [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
  15. [dateFormatter setTimeStyle:NSDateFormatterShortStyle];
  16. NSString *text = [dateFormatter stringFromDate:value];
  17. if (self.label)
  18. {
  19. self.label.text = text;
  20. return;
  21. }
  22. self.selectionStyle = UITableViewCellSelectionStyleNone;
  23. self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0,
  24. self.frame.size.width, [YDChatHeaderTableViewCell height])];
  25. self.label.text = text;
  26. self.label.font = [UIFont boldSystemFontOfSize:12];
  27. self.label.textAlignment = NSTextAlignmentCenter;
  28. self.label.shadowOffset = CGSizeMake(0, 1);
  29. self.label.shadowColor = [UIColor whiteColor];
  30. self.label.textColor = [UIColor darkGrayColor];
  31. self.label.backgroundColor = [UIColor clearColor];
  32. [self addSubview:self.label];
  33. }
  34. @end
  35. </span>

既然已经为HeaderCell创建了类,那么也需要为ChatCell创建一个定制的类,用来显示真实的聊天消息。创建一个继承于UITableViewCell的新的Objective-C类,将其命名为YDChatTableViewCell。为这个类添加YDChatData类型的唯一的一个属性,用以显示真实的聊天消息,并将单元格作为定制的UITableViewCell对象返回。

在YDChatTableViewCell.h文件中实现如代码清单2-12中所示的代码。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-12  Chapter2/YDChatApp/YDChatTableViewCell.h
  2. #import <UIKit/UIKit.h>
  3. #import "YDChatData.h"
  4. @interface YDChatTableViewCell : UITableViewCell
  5. @property (nonatomic, strong) YDChatData *data;
  6. -(void)setData(YDChatData*)data;
  7. @end</span>

setData:方法接受YDChatData对象,将它赋值给data属性。下一步,它会调用rebuild- UserInterface方法,如果该方法之前没有创建过bubbleImage,那么就会创建这个对象。如果YDChatData对象有代表一个用户的值,那么就会使用该聊天用户的头像,作为子视图添加到界面上。

YDChatTableViewCell.m文件的实现代码如代码清单2-13中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-13  Chapter2/YDChatApp/YDChatTableViewCell.m
  2. #import <QuartzCore/QuartzCore.h>
  3. #import "YDChatTableViewCell.h"
  4. #import "YDChatData.h"
  5. #import "YDChatUser.h"
  6. @interface YDChatTableViewCell ()
  7. //declare properties
  8. @property (nonatomic, retain) UIView *customView;
  9. @property (nonatomic, retain) UIImageView *bubbleImage;
  10. @property (nonatomic, retain) UIImageView *avatarImage;
  11. - (void) setupInternalData;
  12. @end
  13. @implementation YDChatTableViewCell
  14. @synthesize data=_data;
  15. - (void)setData:(YDChatData *)data
  16. {
  17. _data = data;
  18. [self rebuildUserInterface];
  19. }
  20. - (void) rebuildUserInterface
  21. {
  22. self.selectionStyle = UITableViewCellSelectionStyleNone;
  23. if (!self.bubbleImage)
  24. {
  25. self.bubbleImage = [[UIImageView alloc] init];
  26. [self addSubview:self.bubbleImage];
  27. }
  28. YDChatType type = self.data.type;
  29. CGFloat width = self.data.view.frame.size.width;
  30. CGFloat height = self.data.view.frame.size.height;
  31. CGFloat x = (type == ChatTypeSomeone) ? 0 :
  32. self.frame.size.width -
  33. width -
  34. self.data.insets.left -
  35. self.data.insets.right;
  36. CGFloat y = 0;
  37. //if we have a chatUser show the avatar of the YDChatUser property
  38. if (self.data.chatUser)
  39. {
  40. YDChatUser *thisUser = self.data.chatUser;
  41. [self.avatarImage removeFromSuperview];
  42. self.avatarImage = [[UIImageView alloc] initWithImage:(thisUser.avatar ?
  43. thisUser.avatar : [UIImage imageNamed:@"noAvatar.png"])];
  44. self.avatarImage.layer.cornerRadius = 9.0;
  45. self.avatarImage.layer.masksToBounds = YES;
  46. self.avatarImage.layer.borderColor =
  47. [UIColor colorWithWhite:0.0 alpha:0.2].CGColor;
  48. self.avatarImage.layer.borderWidth = 1.0;
  49. //calculate the x position
  50. CGFloat avatarX = (type == ChatTypeSomeone) ? 2 :
  51. self.frame.size.width - 52;
  52. CGFloat avatarY = self.frame.size.height - 50;
  53. //set the frame correctly
  54. self.avatarImage.frame = CGRectMake(avatarX, avatarY, 50, 50);
  55. [self addSubview:self.avatarImage];
  56. CGFloat delta = self.frame.size.height -
  57. (self.data.insets.top + self.data.insets.bottom +
  58. self.data.view.frame.size.height);
  59. if (delta > 0) y = delta;
  60. if (type == ChatTypeSomeone) x += 54;
  61. if (type == ChatTypeMine) x -= 54;
  62. }
  63. [self.customView removeFromSuperview];
  64. self.customView = self.data.view;
  65. self.customView.frame =
  66. CGRectMake(x + self.data.insets.left,
  67. y + self.data.insets.top, width, height);
  68. [self.contentView addSubview:self.customView];
  69. //depending on the ChatType a bubble image on the left or right
  70. if (type == ChatTypeSomeone)
  71. {
  72. self.bubbleImage.image = [[UIImage imageNamed:@"yoububble.png"]
  73. stretchableImageWithLeftCapWidth:21 topCapHeight:14];
  74. }
  75. else {
  76. self.bubbleImage.image = [[UIImage imageNamed:@"mebubble.png"]
  77. stretchableImageWithLeftCapWidth:15 topCapHeight:14];
  78. }
  79. self.bubbleImage.frame =
  80. CGRectMake(x, y, width + self.data.insets.left +
  81. self.data.insets.right, height +
  82. self.data.insets.top + self.data.insets.bottom);
  83. }
  84. - (void)setFrame:(CGRect)frame
  85. {
  86. [super setFrame:frame];
  87. [self rebuildUserInterface];
  88. }
  89. @end
  90. </span>

2.2.6  创建聊天用户对象

创建一个新的名为YDChatUser的类,具有两个属性:用户名和头像,它们会被显示在刚刚创建的YDChatTableViewCell中。设计YDChatUser类用来设置用户对象的用户名和头像图片,这样可以关联到YDChatData对象上。

创建一个继承于NSObject的新的Objective-C类,将其命名为YDChatUser。YDChatUser.h文件如代码清单2-14中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-14  Chapter2/YDChatApp/YDChatUser.h
  2. #import <Foundation/Foundation.h>
  3. @interface YDChatUser : NSObject
  4. @property (nonatomic, strong) NSString *username;
  5. @property (nonatomic, strong) UIImage *avatar;
  6. - (id)initWithUsername:(NSString *)user avatarImage:(UIImage *)image;
  7. @end
  8. </span>

实现定制的构造方法,并且将传入的参数值赋给属性,如代码清单2-15中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-15  Chapter2/YDChatApp/YDChatUser.m
  2. #import "YDChatUser.h"
  3. @implementation YDChatUser
  4. @synthesize avatar = _avatar;
  5. @synthesize username = _username;
  6. - (id)initWithUsername:(NSString *)user avatarImage:(UIImage *)image
  7. {
  8. self = [super init];
  9. if (self)
  10. {
  11. self.avatar = [image copy];
  12. self.username = [user copy];
  13. }
  14. return self;
  15. }
  16. @end</span>

2.2.7  融会贯通

既然已经开发了所有独立的组件,那么就可以编写YDViewController类,使用聊天消息来显示YDChatTableView了。

YDViewController.h文件不需要任何编码工作。

YDViewController.m文件导入所需的头文件,以此为开始并遵从YDChatTableViewDataSource和UITextViewDelegate协议。在viewDidLoad方法的开头,以编程方式创建了用户界面元素。在这个方法的结尾,创建了YDChatUser类型的两个对象,如下面这段代码示例所示:

me =[[YDChatUser alloc] initWithUsername:@"Peter"

avatarImage:[UIImage imageNamed:@"me.png"]];

you =[[YDChatUser alloc] initWithUsername:@"You"

avatarImage:[UIImageimageNamed:@"noavatar.png"]];

最终,在viewDidLoad方法中,一些YDChatData记录被创建并添加到Chats数组中,作为YDChatTableView控件的datasource对象。

YDChatData *first = [YDChatData dataWithText:

@"Hey, how are you doing? I'm inParis take a look at this picture."

date:[NSDatedateWithTimeIntervalSinceNow:-600]

type:ChatTypeMine andUser:me];

YDChatData *second = [YDChatDatadataWithImage:

[UIImage imageNamed:@"eiffeltower.jpg"]

date:[NSDatedateWithTimeIntervalSinceNow:-290]

type:ChatTypeMine andUser:me];

YDChatData *third = [YDChatDatadataWithText:

@"Wow.. Really cool pictureout there. Wish I could be with you"

date:[NSDatedateWithTimeIntervalSinceNow:-5]

type:ChatTypeSomeone andUser:you];

YDChatData *forth = [YDChatDatadataWithText:

@"Maybe next time you can comewith me."

date:[NSDatedateWithTimeIntervalSinceNow:+0]

type:ChatTypeMine andUser:me];

//Initialize the Chats array with thecreated YDChatData objects

Chats = [[NSMutableArray alloc]

initWithObjects:first, second,third,forth, nil];

sendMessage方法创建了YDChatData对象,使用从msgText控件中得到的文本来初始化这个对象,将其加入到Chats数组中,并调用chatTable对象的reloadData方法。

当选中UITextView,开始在这个控件内输入文字时,会触发textView:shouldChange- TextInRange:replacementText:、textViewDidBeginEditing:和textViewDidChange:这三个方法,用来操控用户界面。shortenTableView和showTableView方法用来控制YDChatTableView的高度。

完整的实现方式如代码清单2-16中所示。

[csharp] view plaincopy
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">代码清单2-16  Chapter2/YDChatApp/YDViewController.m
  2. #import "YDChatUser.h"
  3. #import "YDChatTableViewDataSource.h"
  4. #import "YDViewController.h"
  5. #import <QuartzCore/QuartzCore.h>
  6. #import "YDChatTableView.h"
  7. #import "YDChatTableViewDataSource.h"
  8. #import "YDChatData.h"
  9. #import "YDChatUser.h"
  10. #define lineHeight  16.0f
  11. @interface YDViewController ()<YDChatTableViewDataSource,UITextViewDelegate>
  12. {
  13. YDChatTableView *chatTable;
  14. UIView *textInputView;
  15. UITextField *textField;
  16. NSMutableArray *Chats;
  17. UIView* sendView;
  18. UIButton* sendButton;
  19. UITextView* msgText;
  20. BOOL composing;
  21. float prevLines;
  22. YDChatUser* me ;
  23. YDChatUser* you ;
  24. }
  25. @end
  26. @implementation YDViewController
  27. CGRect  appFrame;
  28. - (void)viewDidLoad
  29. {
  30. [super viewDidLoad];
  31. self.view.backgroundColor=[UIColor lightGrayColor];
  32. //create your instance of YDChatTableView
  33. self.chatTable=[[YDChatTableView alloc] initWithFrame:
  34. CGRectMake(0,40,[[UIScreen mainScreen] bounds].size.width,
  35. [[UIScreen mainScreen] bounds].size.height -40)  style:UITableViewStylePlain];
  36. chatTable.backgroundColor=[UIColor whiteColor];
  37. [self.view addSubview:chatTable];
  38. appFrame= [[UIScreen mainScreen] applicationFrame];
  39. sendView = [[UIView alloc] initWithFrame:
  40. CGRectMake(0,appFrame.size.height-56,320,56)];
  41. sendView.backgroundColor=[UIColor blueColor];
  42. sendView.alpha=0.9;
  43. msgText = [[UITextView alloc] initWithFrame:CGRectMake(7,10,225,36)];
  44. msgText.backgroundColor = [UIColor whiteColor];
  45. msgText.textColor=[UIColor blackColor];
  46. msgText.font=[UIFont boldSystemFontOfSize:12];
  47. msgText.autoresizingMask =
  48. UIViewAutoresizingFlexibleHeight |
  49. UIViewAutoresizingFlexibleTopMargin;
  50. msgText.layer.cornerRadius = 10.0f;
  51. msgText.returnKeyType=UIReturnKeySend;
  52. msgText.showsHorizontalScrollIndicator=NO;
  53. msgText.showsVerticalScrollIndicator=NO;
  54. //Set the delegate so you can respond to user input
  55. msgText.delegate=self;
  56. [sendView addSubview:msgText];
  57. msgText.contentInset = UIEdgeInsetsMake(0,0,0,0);
  58. [self.view addSubview:sendView];
  59. sendButton = [[UIButton alloc] initWithFrame:CGRectMake(235,10,77,36)];
  60. sendButton.backgroundColor=[UIColor lightGrayColor];
  61. [sendButton addTarget:self action:@selector(sendMessage)
  62. forControlEvents:UIControlEventTouchUpInside];
  63. sendButton.autoresizingMask=UIViewAutoresizingFlexibleTopMargin;
  64. sendButton.layer.cornerRadius=6.0f;
  65. [sendButton setTitle:@"Send" forState:UIControlStateNormal];
  66. [sendView addSubview:sendButton];
  67. //create two YDChatUser object one representing me and one
  68. representing the other party
  69. me = [[YDChatUser alloc] initWithUsername:@"Peter"
  70. avatarImage:[UIImage imageNamed:@"me.png"]];
  71. you  =[[YDChatUser alloc] initWithUsername:@"You"
  72. avatarImage:[UIImage imageNamed:@"noavatar.png"]];
  73. //Create some YDChatData objects here
  74. YDChatData *first = [YDChatData dataWithText:
  75. @"Hey, how are you doing? I'm in Paris take a look at this picture."
  76. date:[NSDate dateWithTimeIntervalSinceNow:-600]
  77. type:ChatTypeMine andUser:me];
  78. YDChatData *second = [YDChatData dataWithImage:
  79. [UIImage imageNamed:@"eiffeltower.jpg"]
  80. date:[NSDate dateWithTimeIntervalSinceNow:-290]
  81. type:ChatTypeMine andUser:me];
  82. YDChatData *third = [YDChatData dataWithText:
  83. @"Wow.. Really cool picture out there. Wish I could be with you"
  84. date:[NSDate dateWithTimeIntervalSinceNow:-5]
  85. type:ChatTypeSomeone andUser:you];
  86. YDChatData *forth = [YDChatData dataWithText:
  87. @"Maybe next time you can come with me."
  88. date:[NSDate dateWithTimeIntervalSinceNow:+0]
  89. type:ChatTypeMine andUser:me];
  90. //Initialize the Chats array with the created YDChatData objects
  91. Chats = [[NSMutableArray alloc]
  92. initWithObjects:first, second, third,forth, nil];
  93. //set the chatDataSource
  94. chatTable.chatDataSource = self;
  95. //call the reloadData, this is actually calling your override method
  96. [chatTable reloadData];
  97. }
  98. -(void)sendMessage
  99. {
  100. composing=NO;
  101. YDChatData *thisChat = [YDChatData dataWithText:msgText.text
  102. date:[NSDate date] type:ChatTypeMine andUser:me];
  103. [Chats addObject:thisChat];
  104. [chatTable reloadData];
  105. [self showTableView];
  106. [msgText resignFirstResponder];
  107. msgText.text=@"";
  108. sendView.frame=CGRectMake(0,appFrame.size.height-56,320,56);
  109. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
  110. [chatTable scrollToRowAtIndexPath:indexPath
  111. atScrollPosition:UITableViewScrollPositionBottom
  112. animated:YES];
  113. }
  114. #pragma UITextViewDelegate
  115. //if user presses enter consider as end of message and send it
  116. -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
  117. replacementText:(NSString *)text
  118. {
  119. if([text isEqualToString:@"\n"]) {
  120. [self sendMessage];
  121. return NO;
  122. }
  123. return YES;
  124. }
  125. // this function returns the height of the entered text in the msgText field
  126. -(CGFloat )textY
  127. {
  128. UIFont* systemFont = [UIFont boldSystemFontOfSize:12];
  129. int width = 225.0, height = 10000.0;
  130. NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
  131. [atts setObject:systemFont forKey:NSFontAttributeName];
  132. CGRect size = [msgText.text boundingRectWithSize:CGSizeMake(width, height)
  133. options:NSStringDrawingUsesLineFragmentOrigin
  134. attributes:atts
  135. context:nil];
  136. float textHeight = size.size.height;
  137. float lines = textHeight / lineHeight;
  138. if (lines >=4)
  139. lines=4;
  140. if ([msgText.text length]==0)
  141. lines=0.9375f;
  142. return 190 - (lines * lineHeight) + lineHeight;
  143. }
  144. -(void)textViewDidChange:(UITextView *)textView
  145. {
  146. UIFont* systemFont = [UIFont boldSystemFontOfSize:12];
  147. int width = 225.0, height = 10000.0;
  148. NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
  149. [atts setObject:systemFont forKey:NSFontAttributeName];
  150. CGRect size = [msgText.text boundingRectWithSize:CGSizeMake(width, height)
  151. options:NSStringDrawingUsesLineFragmentOrigin
  152. attributes:atts
  153. context:nil];
  154. float textHeight = size.size.height;
  155. float lines = textHeight / lineHeight;
  156. if (lines >=4)
  157. lines=4;
  158. composing=YES;
  159. msgText.contentInset = UIEdgeInsetsMake(0,0,0,0);
  160. sendView.frame = CGRectMake(0,appFrame.size.height-270 - (lines * lineHeight) + lineHeight ,320,56 + (lines * lineHeight)-lineHeight);
  161. if (prevLines!=lines)
  162. [self shortenTableView];
  163. prevLines=lines;
  164. }
  165. prevLines=lines;
  166. }
  167. //let's change the frame of the chatTable so we can see the bottom
  168. -(void)shortenTableView
  169. {   [UIView beginAnimations:@"moveView" context:nil];
  170. [UIView setAnimationDuration:0.1];
  171. chatTable.frame=CGRectMake(0, 0, 320, [self textY] );
  172. [UIView commitAnimations];
  173. prevLines=1;
  174. }
  175. // show the chatTable as it was
  176. -(void)showTableView
  177. {
  178. [UIView beginAnimations:@"moveView" context:nil];
  179. [UIView setAnimationDuration:0.1];
  180. chatTable.frame=CGRectMake(0,0,320,460 - 56);
  181. [UIView commitAnimations];
  182. }
  183. //when user starts typing change the frame position and shorten the chatTable
  184. -(void)textViewDidBeginEditing:(UITextView *)textView
  185. { [UIView beginAnimations:@"moveView" context:nil];
  186. [UIView setAnimationDuration:0.3];
  187. sendView.frame = CGRectMake(0,appFrame.size.height-270,320,56);
  188. [UIView commitAnimations];
  189. [self shortenTableView];
  190. [msgText becomeFirstResponder];
  191. }
  192. - (BOOL)shouldAutorotateToInterfaceOrientation:
  193. (UIInterfaceOrientation)interfaceOrientation
  194. {
  195. return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
  196. }
  197. #pragma mark - YDChatTableView implementation
  198. //here are the required implementation from your YDChatTableViewDataSource
  199. - (NSInteger)rowsForChatTable:(YDChatTableView *)tableView
  200. {
  201. return [Chats count];
  202. }
  203. - (YDChatData *)chatTableView:(YDChatTableView *)tableView
  204. dataForRow:(NSInteger)row
  205. {
  206. return [Chats objectAtIndex:row];
  207. }
  208. @end
  209. </span>

虽然以非常出色的方式创建的Chat解决方案实现了创建聊天消息并显示这些消息的逻辑,但是它不发送和接收任何消息。因此,还需要实现一个遵循XMPP协议的真正的通信模块(注:有关XMPP协议请参考www.xmpp.org官方网站的内容)。

《iOS 高级编程》试读电子书,免费提供,有需要的留下邮箱,一有空即发送给大家。 别忘啦顶哦!

购书地址:

京东:http://item.jd.com/11573064.html

当当:http://product.dangdang.com/23596918.html

互动:http://product.china-pub.com/3770647

亚马逊:http://www.amazon.cn/dp/B00P7NO4K2

微信:qinghuashuyou  
更多最新图书请点击查看哦(即日起-12月31日:关注@qinghuashuyou 发送:关注技术方向,即有机会参与图书抽奖,每周抽取十个幸运读者)

《iOS 高级编程》之Tableview进阶指南相关推荐

  1. iOS开发笔记--Facebook POP 进阶指南

    https://github.com/facebook/pop Facebook 在发布了 Paper 之后,似乎还不满足于只是将其作为一个概念性产品,更进一步开源了其背后的动画引擎 POP,此举大有 ...

  2. iOS网络高级编程:iPhone和iPad的企业应用开发之错误处理

    本章内容 ●    iOS应用中的网络错误源 ●    检测网络的可达性 ●    错误处理的经验法则 ●    处理网络错误的设计模式 到目前为止,我们所介绍的iPhone与其他系统的网络交互都是基 ...

  3. 写给初中级前端的高级进阶指南等

    大家好,我是若川. 话不多说,这一次花了几小时精心为大家挑选了20余篇好文,供大家阅读学习.本文阅读技巧,先粗看标题,感兴趣可以都关注一波,绝对不亏. 程序员成长指北 考拉妹子,一个有趣的且乐于分享的 ...

  4. 《Objective-C高级编程 iOS与OS X多线程和内存管理》读书笔记

    <Objective-C高级编程 iOS与OS X多线程和内存管理>读书笔记 第一章:自动引用计数 自己生成的对象,自己所持有. 非自己生成的对象,自己也能持有 不再需要自己持有的对象时释 ...

  5. IOS并发编程指南:Dispatch Queue任务执行与Dispatch Source

    导读: 本文为读<Concurrency Programming Guide>笔记第三篇,在对OS X和iOS应用开发中实现任务异步执行的技术.注意事项.Operation与Dispatc ...

  6. 《音视频开发进阶指南:基于Android与iOS平台的实践》源码下载地址

    年前买了这本书,想看下随书源码,一开始从CSDN下载频道下载电子书+源码,但那个源码不是这边书的. 从网上找了一段时间,终于找到了(其实在书的前言/勘误和支持中有给出),作者展晓凯的相关网站如下: 作 ...

  7. iOS读书笔记之Objective-C高级编程(GCD)

    本文主要对GCD的概念.API以及实现进行梳理. 一.CCD的概念. 1.GCD,全称是Grand Central Dispatch,它是C语言的API. GCD的核心 : 将block(任务)添加到 ...

  8. 4万字【Python高级编程】保姆式教学,进阶感觉到吃力?学完这些就轻松了

    前几天和一个小伙子聊天时,发现了一个问题,他从零开始学Python,学完列表.字典和函数等基础之后,就开始往爬虫方向进阶学习,结果又花了一个多月的时间,啥也没学成,反而寸步难行. 其实这个问题的主要原 ...

  9. 【进阶技术】一篇文章搞掂:Spring高级编程

    本文篇幅较长,建议合理利用右上角目录进行查看(如果没有目录请刷新). 本文基于<Spring5高级编程>一书进行总结和扩展,大家也可以自行研读此书. 十一.任务调度 任务调度主要由三部分组 ...

最新文章

  1. 国产化达梦数据库数据迁移文档:oracle11g数据库转达梦8数据库实例演示
  2. evb测试板_做芯片 再“穷”不能“穷”测试
  3. 数据库表-权限表应用
  4. MySQL 高级repeat循环
  5. mybatis plus 新增,修改
  6. ProxyChains
  7. 西湖大学生命学院章永登实验室 (超分辨成像)科研团队招聘启事
  8. 配置生产环境加路径 /开发环境
  9. r语言list添加元素_Redis数据结构 List 类型】List 类型生产中的应用 消息队列、排行榜、朋友圈、监控程序的实现...
  10. Linux 关闭防火墙命令
  11. hihoCoder 1175 拓扑排序
  12. SpringMVC扩展
  13. CSS常用基础效果---文字与图片并排+导航栏
  14. 企业软件 - 创新尝试 - 用友 股份 产业链创新中心 - 产品流程会议问题解决 - 杨天政 - 原型产品发版标准 - 2014-3-13
  15. Padavan 老毛子路由器登录SSH教程
  16. linux dstat,dstat 用法详解
  17. Verilog HDL 出租车计费器实现
  18. python大数据书籍推荐-清华大学出版社-图书详情-《从零开始学Python大数据与量化交易》...
  19. TensorFlow2.0 Guide官方教程 学习笔记20 -‘Effective TensorFlow 2‘
  20. 使用LSTM进行预测,有一对一、多对一、多对多的预测,其中有一些疑问一起探讨(一)

热门文章

  1. linux docker升级,Docker 升级到最新版本
  2. 移动OA,开启企业智能办公服务新生态
  3. 笔记本安装安卓4.2系统尝试
  4. 802协议族太网帧格式
  5. 电容式触摸屏调试常见问题汇总
  6. scrapy爬虫入门:爬取《id97》电影
  7. 纳什均衡(Nash equilibrium)
  8. 选购kvm需要注意的重要事项
  9. 16万字智慧医疗-医院信息化大数据建设 方案
  10. 医药数据治理数字化方案