Outlets 和georeferencingg Outlets的区别

Outlets 和Referencing Outlets的区别_百度知道
Outlets 和Referencing Outlets的区别
提问者采纳
Outlets里面显示的是你的属性, 以及连接着的目标. Referencing Outlets是你被连接到了别人的属性上面. 比如UITableViewContro...
来自团队:
其他类似问题
为您推荐:
outlets的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁dialog,activity 障蔽Home键详解(转) - 移动开发当前位置:& &&&dialog,activity 障蔽Home键详解(转)dialog,activity 障蔽Home键详解(转)&&网友分享于:&&浏览:74次dialog,activity 屏蔽Home键详解(转)
相信在Android应用上,很多时候逻辑是需要屏蔽Home键的,但这个用户体验是否需要,就看各位的需求了。
一般的方法屏蔽Home键,大家一定看过不少文章了。我总结一下,先说一下一般情况下Activity的屏蔽按键和Home键吧。 屏蔽其他键,重写onKeyDown
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.i(TAG,"keycode="+keyCode + "
isBan="+isBan);
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
Log.i(TAG,"KEYCODE_BACK");
return true;
return super.onKeyDown(keyCode, event);
大家会发现,这里屏蔽Home键是捕捉不到的,因为大家的权限一般是User所以是无效的。 而其实android处理Home键等系统级按键是有一定的处理的。 引用
看看源码是怎样处理的 \frameworks\policies\base\phone\com\android\internal\policy\impl\PhoneWindowManager.java #1092
if (code == KeyEvent.KEYCODE_HOME) {
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
return false;
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.
for (int i=0; i&typeC i++) {
if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
return true;
通过源码,我们不难发现两个的参数 WindowManager.LayoutParams.TYPE_KEYGUARD和 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG 借鉴于此,重写onAttachedToWindow,以实现屏蔽Home键
public void onAttachedToWindow() {
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
super.onAttachedToWindow();
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 华丽的分界线,以下内容更精彩- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 轮到dialog了,如果在Activity弹出dialog,在Activity设置以上2个方法是没办法屏蔽的。 其实,原理是一样的,只是地方不一样而已。
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.mydailog);
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
dialog.show();
dialog.setOnKeyListener(new android.content.DialogInterface.OnKeyListener(){
public boolean onKey(DialogInterface dialog, int keyCode,KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
Log.i(TAG,"KEYCODE_BACK");
return true;
return false;
这样运行后,出错如下:
10-18 13:27:06.380: ERROR/AndroidRuntime(4684): Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRoot$W@2b046d68 -- permission denied for this window type
其实,只需要把dialog.getWindow().setType的位置放在show后面就可以了
dialog.show();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
这么,就完成了Back键的屏蔽 和Home键盘的屏蔽了! 总结: 1:)在以上用WindowManager.LayoutParams.TYPE_KEYGUARD的地方改用 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG 效果一样。至于两者的具体差别,得以后再研究研究。 2:)其实,在源码里是这样调用的。
final AlertDialog dialog = new AlertDialog.Builder(mContext)
.setTitle(null)
.setMessage(message)
.setNeutralButton(R.string.ok, null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
dialog.show();
但我们如果这样调用就会出现之前的那个error:permission denied for this window type 这就显而易见了吧~~ 3:)ProgressDialog 默认屏蔽 Back键,Dialog,AlertDialog则需setOnKeyListener 4:)其实屏蔽Home键,在页面的某个地方,例如一个Button的onClick里,去设置setType就可以了,如:
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
但前提是重载Activity的onAttachedToWindow(),哪怕只是一个空实现,然后返回父类方法。
public void onAttachedToWindow() {
super.onAttachedToWindow();
5:)其实它们,都是常用的~
switch (keyCode) {
case KeyEvent.KEYCODE_HOME:
Log.i(TAG,"KEYCODE_HOME");
return true;
case KeyEvent.KEYCODE_BACK:
Log.i(TAG,"KEYCODE_BACK");
return true;
case KeyEvent.KEYCODE_CALL:
Log.i(TAG,"KEYCODE_CALL");
return true;
case KeyEvent.KEYCODE_SYM:
Log.i(TAG,"KEYCODE_SYM");
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
Log.i(TAG,"KEYCODE_VOLUME_DOWN");
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
Log.i(TAG,"KEYCODE_VOLUME_UP");
return true;
case KeyEvent.KEYCODE_STAR:
Log.i(TAG,"KEYCODE_STAR");
return true;
希望大家看到这个文章能觉得有用,谢谢已阅者! - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - 华丽的分界线,以下内容更精彩- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
更新如下: 总结1:)的问题,有答案了,时间问题我就简单写写吧:
从功能上来说,是一样的,区别在样式。
如果你喜欢用Theme.Dialog去把一个Activity装饰成一个Dialog去显示,你会发现。 在
Androidmanifest.xml代码
android:theme="@android:style/Theme.Dialog"
背景是透明的。 如果在
Android代码
setTheme(android.R.style.Theme_Dialog);
背景则是黑色的。 这是为什么呢?。。。我不知道。 治标不治本的方法来了!若你在Activity重写onAttachedToWindow
public void onAttachedToWindow() {
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
super.onAttachedToWindow();
那么出来的效果,就是透明背景的dialog了,当然前提是你需要实现屏蔽Home键。至于其中到底哪一代码导致样式改变呢,那就以后再去看源代码了~ 希望大家看到这个文章能觉得有用,再次谢谢已阅者
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 12345678910 Copyright & &&版权所有java中的synchronized临界区应用 - 移动开发当前位置:& &&&java中的synchronized临界区应用java中的synchronized临界区应用&&网友分享于:&&浏览:165次java中的synchronized临界区使用
synchronized的3种用法1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入. 例如: public synchronized void synMethod() { //方法体 } 2.synchronized后面括号里是一对象,此时,线程获得的是对象锁.例如: public class MyThread implements Runnable { public static void main(String args[]) { MyThread mt = new MyThread(); Thread t1 = new Thread(mt, "t1"); Thread t2 = new Thread(mt, "t2"); Thread t3 = new Thread(mt, "t3"); Thread t4 = new Thread(mt, "t4"); Thread t5 = new Thread(mt, "t5"); Thread t6 = new Thread(mt, "t6"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); } public void run() { synchronized (this) { System.out.println(Thread.currentThread().getName()); } } } 对于2,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法。为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面。由于每个对象都有锁,可以如下所示使用虚拟对象来上锁: class FineGrainLock { MyMemberClass x, Object xlock = new Object(), ylock = new Object(); public void foo() { synchronized(xlock) { //access x here } //do something here - but don‘t use shared resources synchronized(ylock) { //access y here } } public void bar() { synchronized(this) { //access both x and y here } //do something here - but don‘t use shared resources } }
3.synchronized后面括号里是类.例如: class ArrayWithLockOrder{ private static long num_locks = 0; private long lock_ private int[] public ArrayWithLockOrder(int[] a) { arr = synchronized(ArrayWithLockOrder.class) {//-----------------------------------------这里 num_locks++; // 锁数加 1。 lock_order = num_ // 为此对象实例设置唯一的 lock_order。 } } public long lockOrder() { return lock_ } public int[] array() {
} } class SomeClass implements Runnable { public int sumArrays(ArrayWithLockOrder a1, ArrayWithLockOrder a2) { int value = 0; ArrayWithLockOrder first = a1; // 保留数组引用的一个 ArrayWithLockOrder last = a2; // 本地副本。 int size = a1.array(). if (size == a2.array().length) { if (a1.lockOrder() & a2.lockOrder()) // 确定并设置对象的锁定 { // 顺序。 first = a2; last = a1; } synchronized(first) { // 按正确的顺序锁定对象。 synchronized(last) { int[] arr1 = a1.array(); int[] arr2 = a2.array(); for (int i=0; i value += arr1[i] + arr2[i]; } } }
} public void run() { //... } } 对于3,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁.
以上3种之间的关系: 锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象的锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象。 在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。同步化方法在执行之前获得一个锁。如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁。如果这是一个实例方法,那么此锁是this对象的锁。
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 12345678910 Copyright & &&版权所有ios例子3 Test_xib1——使用xib布局
上一篇: &下一篇:
Xib文件就是MVC模式中的View这个层的界面显示布局的信息。即类似java中的jsp文件。以xml文件格式存储方式,可以手动编辑与修改。所有的xib中的控件及关联绑定信息都可以通过.m文件的中的代码进行实现。所以xib文件不是必须的,但为了更好的可视化编程建议还是使用。
此例中将实现如下图所示的应用程序,展示一个宝贝的信息(没有通过网络请求宝贝详情,网络请求参考AFNetworking)
&2. 创建一个empty应用
通过菜单创建一个新工程:路径为&File& New& New Project-Empty Application
3.创建文件
创建两个Group:Models与ViewControllers,并在ViewControllers中,利用模版创建xib与ViewControllers。
&4.查看File's Owner的class属性(在非模板创建方式中,用此设置关联viewControllers和xib文件)
在xib文件中有一个最重要的就是File's Owner,这个对象指的就是Class属性中设定的那个类的对象。只有设定了Class属性中的类才有办法进行xib界面上的控件与IBOutlet与IBAction的绑定关联。
选中view的File's Owner,并在功能区域中,点击属性查看器(identity inspector),在custom class中显示view与ItemViewController关联。
花一些时间来熟悉Xcode的工作空间窗口吧。你会在后续的部分中使用到下图所示的按钮和区域。
在功能区域(Utility area)下方是对象库,如果没有看到对象库,可以点击“立方体”按钮将之调出,如下图圈中所示。分别从库中拖曳三个标签(label),和一个图像视图(image view)放到视图中。选中对象后,可拖动大小调节器调整标签和图像视图的大小。
6.xib中对象与代码关联
在Editor按钮区域选中中间的“show the assistance editor”按钮,会在画板右侧出现first owner中绑定的itemViewController.h文件。
选中一个标签,同时按住“control”键不放,将标签拖到@interface和@end代码中间,会出现插入提示。
&&&&&松开鼠标,弹出属于设置对话框,填入name,点击Connect,会自动在插入处生成属性声明代码。
下图展示的是xib文件中对象与代码的关联,选中标签右键,弹出的黑框中Referencing
Outlets显示关联了titleLabel属性。另外代码中的属性语句中有IBOutlet声明。
&&&&将剩余控件关联补充完全。
ItemViewController.m
#import "ItemViewController.h"
#import "ItemModel.h"
@interface ItemViewController ()
@implementation ItemViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
- (void)viewDidLoad
[super viewDidLoad];
ItemModel *model=[ItemModel itemModel];
self.titleLabel.text = model.
self.titleLabel.textColor = [UIColor blackColor];
self.titleLabel.font = [UIFont fontWithName:@"FZZhongDengXian-Z07S" size:16];
self.titleLabel.backgroundColor = [UIColor clearColor];
NSURL *imageUrl = [NSURL URLWithString:model.images];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageUrl]];
self.imageView.image=
self.priceLabel.text=[NSString stringWithFormat:@"%@%@",@"价格:¥",[model price]];
self.priceLabel.font = [UIFont systemFontOfSize:14];
self.priceLabel.textColor = [UIColor redColor];
self.descriptionLabel.text = [model description];
self.descriptionLabel.textColor = [UIColor blackColor];
self.descriptionLabel.font = [UIFont systemFontOfSize:14.0f];
self.descriptionLabel.numberOfLines = 0;//相当于不限制行数
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
TestAppDelegate.m
#import "TestAppDelegate.h"
#import "ItemViewController.h"
@implementation TestAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
ItemViewController *viewController=[[ItemViewController alloc]initWithNibName:@"ItemViewController" bundle:nil];
//减去status bar的高度
CGRect r = viewController.view.
r.origin.y += 20;
viewController.view.frame =
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
return YES;
。。。。。。 &
用户留言:共有0条留言2410人阅读
本章内容:
●&&&&学习如何进阶使用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。
使用Interface Builder工具打开YDViewController.xib文件,并将一个UITableView控件添加到该窗口中。使用Assistant Editor工具为这个UITableView控件创建一个属性。也需要设置Referencing Outlets一栏中的datasource和delegate指向UITableView对象。确保YDViewController.xib文件看起来如图2-2中所示。
打开YDViewController.h文件,创建名为rowData的NSMutableArray对象充当datasource,如代码清单2-1中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-1
Chapter2/PlainTable/YDViewController.h
#import &UIKit/UIKit.h&
@interface YDViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITableView *mTableV
@property(nonatomic,strong) NSMutableArray* rowD
打开YDViewController.m文件,实现如代码清单2-2中所示的代码,关于这段代码,会在代码清单后详细说明。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-2
Chapter2/PlainTable/YDViewController.m
#import &YDViewController.h&
@interface YDViewController ()
@implementation YDViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self loadData];
-(void)loadData
if (self.rowData!=nil)
[self.rowData removeAllObjects];
self.rowData=
self.rowData = [[NSMutableArray alloc] init];
for (int i=0 ; i&100;i++)
[self.rowData addObject:[NSString stringWithFormat:@&Row: %i&,i]];
//now my datasource if populated let's reload the tableview
[self.mTableView reloadData];
#pragma mark UITableView delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.rowData count];
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @&Cell&;
UITableViewCell *cell = (UITableViewCell *)[tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleN
cell.textLabel.text = [self.rowData objectAtIndex:indexPath.row];
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
[tableView deselectRowAtIndexPath:indexPath animated:YES];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
下面对这段代码进行分解,向你解释代码中各方法的作用。
在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中所示。
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中所示。
如代码清单2-3中所示,建立YDViewController.h文件。作为前一个例子的补充,为引入的两个UIButton添加两个动作。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-3
Chapter2/ScrollingTable/YDViewController.h
#import &UIKit/UIKit.h&
@interface YDViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITableView *mTableV
@property(nonatomic,strong) NSMutableArray* rowD
- (IBAction)scrollToTop:(UIButton *)
- (IBAction)scrollToBottom:(UIButton *)
YDViewController.m文件的实现与前面的代码清单2-2中的类似,唯一的区别在于此时scrollToTop:和scrollToBottom:这两个方法的实现如代码清单2-4中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-4
Chapter2/ScrollingTable/YDViewController.m
#import &YDViewController.h&
@interface YDViewController ()
@implementation YDViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self loadData];
-(void)loadData
if (self.rowData!=nil)
[self.rowData removeAllObjects];
self.rowData=
self.rowData = [[NSMutableArray alloc] init];
for (int i=0 ; i&100;i++)
[self.rowData addObject:[NSString stringWithFormat:@&Row: %i&,i]];
//now my datasource if populated let's reload the tableview
[self.mTableView reloadData];
#pragma mark UITableView delegates
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.rowData count];
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @&Cell&;
UITableViewCell *cell = (UITableViewCell *)[tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleN
cell.textLabel.text = [self.rowData objectAtIndex:indexPath.row];
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath
[tableView deselectRowAtIndexPath:indexPath animated:YES];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
- (IBAction)scrollToTop:(UIButton *)sender
NSIndexPath *topRow = [NSIndexPath indexPathForRow:0 inSection:0];
[self.mTableView scrollToRowAtIndexPath:topRow
atScrollPosition:UITableViewScrollPositionTop animated:YES];
- (IBAction)scrollToBottom:(UIButton *)sender
NSIndexPath *bottomRow = [NSIndexPath indexPathForRow:
[self.rowData count]-1 inSection:0];
[self.mTableView scrollToRowAtIndexPath:bottomRow
atScrollPosition:UITableViewScrollPositionBottom animated:YES];
在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中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-5
Chapter2/YDChatApp/YDChatTableViewDataSource.h
#import &Foundation/Foundation.h&
@class YDChatD
@class YDChatTableV
@protocol YDChatTableViewDataSource &NSObject&
- (NSInteger)rowsForChatTable:(YDChatTableView *)tableV
- (YDChatData *)chatTableView:(YDChatTableView *)tableView
dataForRow:(NSInteger)
这个协议直接继承于NSObject类,其中定义了必须在YDViewController类里实现的两个方法,定义定制的UITableView的地方就是这里。
2.2.2& 构建聊天数据对象
为了使生活更轻松,定义一个名为YDChatData的对象,用来保存一条聊天消息的相关信息。可以用聊天的用户、时间戳、文字或者图片来初始化这个对象。枚举类型YDChatType有两种可能的值,ChatTypeMine和ChatTypeSomeone,用来负责聊天消息在UITableView上的位置。创建一个继承于NSObject的新的Objective-C类,将其命名为YDChatData。
YDChatData.h文件的源代码如代码清单2-6中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-6
Chapter2/YDChatApp/YDChatData.h
#import &Foundation/Foundation.h&
@class YDChatU
//enumerator to identify the chattype
typedef enum _YDChatType
ChatTypeMine = 0,
ChatTypeSomeone = 1
@interface YDChatData : NSObject
@property (readonly, nonatomic) YDChatT
@property (readonly, nonatomic, strong) NSDate *
@property (readonly, nonatomic, strong) UIView *
@property (readonly, nonatomic) UIEdgeI
@property (nonatomic,strong) YDChatUser *chatU
//custom initializers
+ (id)dataWithText:(NSString *)text date:(NSDate *)date
type:(YDChatType)type andUser:(YDChatUser *)_
+ (id)dataWithImage:(UIImage *)image date:(NSDate *)date
type:(YDChatType)type andUser:(YDChatUser *)_
+ (id)dataWithView:(UIView *)view date:(NSDate *)date
type:(YDChatType)type andUser:(YDChatUser *)_user
insets:(UIEdgeInsets)
在类的实现中,实现几个不同的初始化方法,如代码清单2-7中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-7
Chapter2/YDChatApp/YDChatData.m
import &YDChatData.h&
#import &QuartzCore/QuartzCore.h&
@implementation YDChatData
//create some constant UIEdgeInsets to property align text and images
const UIEdgeInsets textInsetsMine = {5, 10, 11, 17};
const UIEdgeInsets textInsetsSomeone = {5, 15, 11, 10};
const UIEdgeInsets imageInsetsMine = {11, 13, 16, 22};
const UIEdgeInsets imageInsetsSomeone = {11, 18, 16, 14};
#pragma initializers
+ (id)dataWithText:(NSString *)text date:(NSDate *)date type:(YDChatType)type
andUser:(YDChatUser *)_user
return [[YDChatData alloc] initWithText:text date:date
type:type andUser:_user];
o(id)initWithText:(NSString *)text date:(NSDate *)date type:(YDChatType)type andUser:(YDChatUser *)_user
UIFont* font = [UIFont boldSystemFontOfSize:12];
int width = 225, height = 10000.0;
NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
[atts setObject:font forKey:NSFontAttributeName];
CGRect size = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:atts
context:nil];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size.size.width, size.size.height)];
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordW
label.text = (text ? text : @&&);
label.font =
label.backgroundColor = [UIColor clearColor];
UIEdgeInsets insets = (type == ChatTypeMine ? textInsetsMine : textInsetsSomeone);
return [self initWithView:label date:date type:type andUser:_user
insets:insets];
o(id)initWithImage:(UIImage *)image date:(NSDate *)date type:(YDChatType)type
andUser:(YDChatUser *)_user
CGSize size = image.
if (size.width & 220)
size.height /= (size.width / 220);
size.width = 220;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:
CGRectMake(0, 0, size.width, size.height)];
imageView.image =
imageView.layer.cornerRadius = 5.0;
imageView.layer.masksToBounds = YES;
UIEdgeInsets insets =
(type == ChatTypeMine ? imageInsetsMine : imageInsetsSomeone);
return [self initWithView:imageView date:date type:type andUser:_user
insets:insets];
+ (id)dataWithView:(UIView *)view date:(NSDate *)date type:(YDChatType)type
andUser:(YDChatUser *)_user insets:(UIEdgeInsets)insets
return [[YDChatData alloc] initWithView:view date:date type:type
andUser:_user
insets:insets];
o(id)initWithView:(UIView *)view date:(NSDate *)date type:(YDChatType)type
andUser:(YDChatUser *)_user insets:(UIEdgeInsets)insets
self = [super init];
_chatUser = _
2.2.3& 构建定制的UITableView控件
创建一个名为YDChatTableView的新的Objective-C类,继承于UITableView类,并且实现了名为ChatBubbleTypingType的枚举类型和需要的属性,如代码清单2-8中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-8
Chapter2/YDChatApp/YDChatTableView.h
#import &UIKit/UIKit.h&
#import &YDChatTableViewDataSource.h&
#import &YDChatTableViewCell.h&
//enumerator to identify the bubble type
typedef enum _ChatBubbleTypingType
ChatBubbleTypingTypeNobody = 0,
ChatBubbleTypingTypeMe = 1,
ChatBubbleTypingTypeSomebody = 2
} ChatBubbleTypingT
@interface YDChatTableView : UITableView
@property (nonatomic, assign) id&YDChatTableViewDataSource& chatDataS
@property (nonatomic) NSTimeInterval snapI
@property (nonatomic) ChatBubbleTypingType typingB
在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中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-9
Chapter2/YDChatApp/YDChatTableView.m
#import &YDChatTableView.h&
#import &YDChatData.h&
#import &YDChatHeaderTableViewCell.h&
@interface YDChatTableView ()&UITableViewDelegate, UITableViewDataSource&
@property (nonatomic, retain) NSMutableArray *bubbleS
@implementation YDChatTableView
- (void)initializer
self.backgroundColor = [UIColor clearColor];
self.separatorStyle = UITableViewCellSeparatorStyleN
self.delegate =
self.dataSource =
//the snap interval in seconds implements a headerview to seperate chats
self.snapInterval = 60 * 60 * 24; //one day
self.typingBubble = ChatBubbleTypingTypeN
- (id)init
self = [super init];
if (self) [self initializer];
- (id)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self) [self initializer];
- (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
self = [super initWithFrame:frame style:UITableViewStylePlain];
if (self) [self initializer];
#pragma mark - Override
- (void)reloadData
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
self.bubbleSection =
int count = 0;
self.bubbleSection = [[NSMutableArray alloc] init];
if (self.chatDataSource && (count = [self.chatDataSource
rowsForChatTable:self]) & 0)
NSMutableArray *bubbleData = [[NSMutableArray alloc]
initWithCapacity:count];
for (int i = 0; i & i++)
NSObject *object = [self.chatDataSource
chatTableView:self dataForRow:i];
assert([object isKindOfClass:[YDChatData class]]);
[bubbleData addObject:object];
[bubbleData sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
YDChatData *bubbleData1 = (YDChatData *)obj1;
YDChatData *bubbleData2 = (YDChatData *)obj2;
return [bubbleData1.date compare:bubbleData2.date];
NSDate *last = [NSDate dateWithTimeIntervalSince1970:0];
NSMutableArray *currentSection =
for (int i = 0; i & i++)
YDChatData *data = (YDChatData *)[bubbleData objectAtIndex:i];
if ([data.date timeIntervalSinceDate:last] & self.snapInterval)
currentSection = [[NSMutableArray alloc] init];
[self.bubbleSection addObject:currentSection];
[currentSection addObject:data];
last = data.
[super reloadData];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
int result = [self.bubbleSection count];
if (self.typingBubble != ChatBubbleTypingTypeNobody) result++;
o(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
if (section &= [self.bubbleSection count]) return 1;
return [[self.bubbleSection objectAtIndex:section] count] + 1;
o(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:
(NSIndexPath *)indexPath
if (indexPath.row == 0)
return [YDChatHeaderTableViewCell height];
YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row - 1];
return MAX(data.insets.top + data.view.frame.size.height +
data.insets.bottom, 52);
o(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
// Header based on snapInterval
if (indexPath.row == 0)
static NSString *cellId = @&HeaderCell&;
YDChatHeaderTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:cellId];
YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
objectAtIndex:0];
if (cell == nil) cell = [[YDChatHeaderTableViewCell alloc] init];
cell.date = data.
// Standard
static NSString *cellId = @&ChatCell&;
YDChatTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:cellId];
YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row - 1];
if (cell == nil) cell = [[YDChatTableViewCell alloc] init];
cell.data =
2.2.4& 灵活的单元格高度
由于聊天消息具有图片和文本,这会使每个单元格的宽度和高度发生变化,因此表格可以是不相同的,并且需要根据单元格的内容进行计算。在重写的tableview:heightForRow- AtIndexPath:方法中,存在一个判断条件,如果当前的单元格是标题单元格,那么就返回标题单元格的高度;如果这个单元格是一个普通的聊天单元格,那么就获取与该单元格相连的YDChatData对象,并计算用作显示单元格的相关UIEdgeInset变量的最大值。下面的代码片段在YDChatTableView类中实现,负责灵活地返回单元格的高度。
&span style=&font-family:Microsoft YaHfont-size:14&&(float)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
if (indexPath.row == 0)
return [YDChatHeaderTableViewCell height];
YDChatData *data = [[self.bubbleSection objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row - 1];
return MAX(data.insets.top + data.view.frame.size.height +
data.insets.bottom, 52);
2.2.5& 开发定制的单元格
为了正确显示聊天数据,需要两种不同的定制的UITableViewCell对象,它们都继承于UITableViewCell类。
一种用以显示标题头,在这种情况下,就是显示与snapInterval属性相关的日期和时间编组。另外一种用以显示YDChatData对象中保存的聊天消息。前一种表格对象有一个名为height返回值类型为CGFloat的静态方法,返回这个UITableViewCell的高度,还有一个日期类型的属性,因而日期和时间可以从snapInterval属性中获得。创建一个名为YDChatTableViewHeaderCell的新的Objective-C类,打开YDChatTableViewHeaderCell.h文件,应用如代码清单2-10中所示的代码。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-10
Chapter2/YDChatApp/YDChatTableViewHeaderCell.h
#import &UIKit/UIKit.h&
@interface YDChatHeaderTableViewCell : UITableViewCell
+ (CGFloat)
@property (nonatomic, strong) NSDate *
&span style=&font-family:Microsoft YaHfont-size:14&&
YDChatTableViewHeaderCell类的实现简单地返回30.0作为height方法的返回值。setDate方法接收一个日期对象,并创建UILabel控件,将其添加到视图上,用以显示section的日期-时间戳。实现如代码清单2-11中所示的代码。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-11
Chapter2/YDChatApp/YDChatTableViewHeaderCell.m
#import &YDChatHeaderTableViewCell.h&
@interface YDChatHeaderTableViewCell ()
@property (nonatomic, retain) UILabel *
@implementation YDChatHeaderTableViewCell
+ (CGFloat)height
return 30.0;
- (void)setDate:(NSDate *)value
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *text = [dateFormatter stringFromDate:value];
if (self.label)
self.label.text =
self.selectionStyle = UITableViewCellSelectionStyleN
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0,
self.frame.size.width, [YDChatHeaderTableViewCell height])];
self.label.text =
self.label.font = [UIFont boldSystemFontOfSize:12];
self.label.textAlignment = NSTextAlignmentC
self.label.shadowOffset = CGSizeMake(0, 1);
self.label.shadowColor = [UIColor whiteColor];
self.label.textColor = [UIColor darkGrayColor];
self.label.backgroundColor = [UIColor clearColor];
[self addSubview:self.label];
既然已经为HeaderCell创建了类,那么也需要为ChatCell创建一个定制的类,用来显示真实的聊天消息。创建一个继承于UITableViewCell的新的Objective-C类,将其命名为YDChatTableViewCell。为这个类添加YDChatData类型的唯一的一个属性,用以显示真实的聊天消息,并将单元格作为定制的UITableViewCell对象返回。
在YDChatTableViewCell.h文件中实现如代码清单2-12中所示的代码。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-12
Chapter2/YDChatApp/YDChatTableViewCell.h
#import &UIKit/UIKit.h&
#import &YDChatData.h&
@interface YDChatTableViewCell : UITableViewCell
@property (nonatomic, strong) YDChatData *
-(void)setData(YDChatData*)
@end&/span&
setData:方法接受YDChatData对象,将它赋值给data属性。下一步,它会调用rebuild- UserInterface方法,如果该方法之前没有创建过bubbleImage,那么就会创建这个对象。如果YDChatData对象有代表一个用户的值,那么就会使用该聊天用户的头像,作为子视图添加到界面上。
YDChatTableViewCell.m文件的实现代码如代码清单2-13中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-13
Chapter2/YDChatApp/YDChatTableViewCell.m
#import &QuartzCore/QuartzCore.h&
#import &YDChatTableViewCell.h&
#import &YDChatData.h&
#import &YDChatUser.h&
@interface YDChatTableViewCell ()
//declare properties
@property (nonatomic, retain) UIView *customV
@property (nonatomic, retain) UIImageView *bubbleI
@property (nonatomic, retain) UIImageView *avatarI
- (void) setupInternalD
@implementation YDChatTableViewCell
@synthesize data=_
- (void)setData:(YDChatData *)data
[self rebuildUserInterface];
- (void) rebuildUserInterface
self.selectionStyle = UITableViewCellSelectionStyleN
if (!self.bubbleImage)
self.bubbleImage = [[UIImageView alloc] init];
[self addSubview:self.bubbleImage];
YDChatType type = self.data.
CGFloat width = self.data.view.frame.size.
CGFloat height = self.data.view.frame.size.
CGFloat x = (type == ChatTypeSomeone) ? 0 :
self.frame.size.width -
self.data.insets.left -
self.data.insets.
CGFloat y = 0;
//if we have a chatUser show the avatar of the YDChatUser property
if (self.data.chatUser)
YDChatUser *thisUser = self.data.chatU
[self.avatarImage removeFromSuperview];
self.avatarImage = [[UIImageView alloc] initWithImage:(thisUser.avatar ?
thisUser.avatar : [UIImage imageNamed:@&noAvatar.png&])];
self.avatarImage.layer.cornerRadius = 9.0;
self.avatarImage.layer.masksToBounds = YES;
self.avatarImage.layer.borderColor =
[UIColor colorWithWhite:0.0 alpha:0.2].CGC
self.avatarImage.layer.borderWidth = 1.0;
//calculate the x position
CGFloat avatarX = (type == ChatTypeSomeone) ? 2 :
self.frame.size.width - 52;
CGFloat avatarY = self.frame.size.height - 50;
//set the frame correctly
self.avatarImage.frame = CGRectMake(avatarX, avatarY, 50, 50);
[self addSubview:self.avatarImage];
CGFloat delta = self.frame.size.height -
(self.data.insets.top + self.data.insets.bottom +
self.data.view.frame.size.height);
if (delta & 0) y =
if (type == ChatTypeSomeone) x += 54;
if (type == ChatTypeMine) x -= 54;
[self.customView removeFromSuperview];
self.customView = self.data.
self.customView.frame =
CGRectMake(x + self.data.insets.left,
y + self.data.insets.top, width, height);
[self.contentView addSubview:self.customView];
//depending on the ChatType a bubble image on the left or right
if (type == ChatTypeSomeone)
self.bubbleImage.image = [[UIImage imageNamed:@&yoububble.png&]
stretchableImageWithLeftCapWidth:21 topCapHeight:14];
self.bubbleImage.image = [[UIImage imageNamed:@&mebubble.png&]
stretchableImageWithLeftCapWidth:15 topCapHeight:14];
self.bubbleImage.frame =
CGRectMake(x, y, width + self.data.insets.left +
self.data.insets.right, height +
self.data.insets.top + self.data.insets.bottom);
- (void)setFrame:(CGRect)frame
[super setFrame:frame];
[self rebuildUserInterface];
2.2.6& 创建聊天用户对象
创建一个新的名为YDChatUser的类,具有两个属性:用户名和头像,它们会被显示在刚刚创建的YDChatTableViewCell中。设计YDChatUser类用来设置用户对象的用户名和头像图片,这样可以关联到YDChatData对象上。
创建一个继承于NSObject的新的Objective-C类,将其命名为YDChatUser。YDChatUser.h文件如代码清单2-14中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-14
Chapter2/YDChatApp/YDChatUser.h
#import &Foundation/Foundation.h&
@interface YDChatUser : NSObject
@property (nonatomic, strong) NSString *
@property (nonatomic, strong) UIImage *
- (id)initWithUsername:(NSString *)user avatarImage:(UIImage *)
实现定制的构造方法,并且将传入的参数值赋给属性,如代码清单2-15中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-15
Chapter2/YDChatApp/YDChatUser.m
#import &YDChatUser.h&
@implementation YDChatUser
@synthesize avatar = _
@synthesize username = _
- (id)initWithUsername:(NSString *)user avatarImage:(UIImage *)image
self = [super init];
self.avatar = [image copy];
self.username = [user copy];
@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中所示。
&span style=&font-family:Microsoft YaHfont-size:14&&代码清单2-16
Chapter2/YDChatApp/YDViewController.m
#import &YDChatUser.h&
#import &YDChatTableViewDataSource.h&
#import &YDViewController.h&
#import &QuartzCore/QuartzCore.h&
#import &YDChatTableView.h&
#import &YDChatTableViewDataSource.h&
#import &YDChatData.h&
#import &YDChatUser.h&
#define lineHeight
@interface YDViewController ()&YDChatTableViewDataSource,UITextViewDelegate&
YDChatTableView *chatT
UIView *textInputV
UITextField *textF
NSMutableArray *C
UIView* sendV
UIButton* sendB
UITextView* msgT
float prevL
YDChatUser*
YDChatUser*
@implementation YDViewController
- (void)viewDidLoad
[super viewDidLoad];
self.view.backgroundColor=[UIColor lightGrayColor];
//create your instance of YDChatTableView
self.chatTable=[[YDChatTableView alloc] initWithFrame:
CGRectMake(0,40,[[UIScreen mainScreen] bounds].size.width,
[[UIScreen mainScreen] bounds].size.height -40)
style:UITableViewStylePlain];
chatTable.backgroundColor=[UIColor whiteColor];
[self.view addSubview:chatTable];
appFrame= [[UIScreen mainScreen] applicationFrame];
sendView = [[UIView alloc] initWithFrame:
CGRectMake(0,appFrame.size.height-56,320,56)];
sendView.backgroundColor=[UIColor blueColor];
sendView.alpha=0.9;
msgText = [[UITextView alloc] initWithFrame:CGRectMake(7,10,225,36)];
msgText.backgroundColor = [UIColor whiteColor];
msgText.textColor=[UIColor blackColor];
msgText.font=[UIFont boldSystemFontOfSize:12];
msgText.autoresizingMask =
UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleTopM
msgText.layer.cornerRadius = 10.0f;
msgText.returnKeyType=UIReturnKeyS
msgText.showsHorizontalScrollIndicator=NO;
msgText.showsVerticalScrollIndicator=NO;
//Set the delegate so you can respond to user input
msgText.delegate=
[sendView addSubview:msgText];
msgText.contentInset = UIEdgeInsetsMake(0,0,0,0);
[self.view addSubview:sendView];
sendButton = [[UIButton alloc] initWithFrame:CGRectMake(235,10,77,36)];
sendButton.backgroundColor=[UIColor lightGrayColor];
[sendButton addTarget:self action:@selector(sendMessage)
forControlEvents:UIControlEventTouchUpInside];
sendButton.autoresizingMask=UIViewAutoresizingFlexibleTopM
sendButton.layer.cornerRadius=6.0f;
[sendButton setTitle:@&Send& forState:UIControlStateNormal];
[sendView addSubview:sendButton];
//create two YDChatUser object one representing me and one
representing the other party
me = [[YDChatUser alloc] initWithUsername:@&Peter&
avatarImage:[UIImage imageNamed:@&me.png&]];
=[[YDChatUser alloc] initWithUsername:@&You&
avatarImage:[UIImage imageNamed:@&noavatar.png&]];
//Create some YDChatData objects here
YDChatData *first = [YDChatData dataWithText:
@&Hey, how are you doing? I'm in Paris take a look at this picture.&
date:[NSDate dateWithTimeIntervalSinceNow:-600]
type:ChatTypeMine andUser:me];
YDChatData *second = [YDChatData dataWithImage:
[UIImage imageNamed:@&eiffeltower.jpg&]
date:[NSDate dateWithTimeIntervalSinceNow:-290]
type:ChatTypeMine andUser:me];
YDChatData *third = [YDChatData dataWithText:
@&Wow.. Really cool picture out there. Wish I could be with you&
date:[NSDate dateWithTimeIntervalSinceNow:-5]
type:ChatTypeSomeone andUser:you];
YDChatData *forth = [YDChatData dataWithText:
@&Maybe next time you can come with me.&
date:[NSDate dateWithTimeIntervalSinceNow:+0]
type:ChatTypeMine andUser:me];
//Initialize the Chats array with the created YDChatData objects
Chats = [[NSMutableArray alloc]
initWithObjects:first, second, third,forth, nil];
//set the chatDataSource
chatTable.chatDataSource =
//call the reloadData, this is actually calling your override method
[chatTable reloadData];
-(void)sendMessage
composing=NO;
YDChatData *thisChat = [YDChatData dataWithText:msgText.text
date:[NSDate date] type:ChatTypeMine andUser:me];
[Chats addObject:thisChat];
[chatTable reloadData];
[self showTableView];
[msgText resignFirstResponder];
msgText.text=@&&;
sendView.frame=CGRectMake(0,appFrame.size.height-56,320,56);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[chatTable scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
#pragma UITextViewDelegate
//if user presses enter consider as end of message and send it
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
if([text isEqualToString:@&\n&]) {
[self sendMessage];
return NO;
return YES;
// this function returns the height of the entered text in the msgText field
-(CGFloat )textY
UIFont* systemFont = [UIFont boldSystemFontOfSize:12];
int width = 225.0, height = 10000.0;
NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
[atts setObject:systemFont forKey:NSFontAttributeName];
CGRect size = [msgText.text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:atts
context:nil];
float textHeight = size.size.
float lines = textHeight / lineH
if (lines &=4)
if ([msgText.text length]==0)
lines=0.9375f;
return 190 - (lines * lineHeight) + lineH
-(void)textViewDidChange:(UITextView *)textView
UIFont* systemFont = [UIFont boldSystemFontOfSize:12];
int width = 225.0, height = 10000.0;
NSMutableDictionary *atts = [[NSMutableDictionary alloc] init];
[atts setObject:systemFont forKey:NSFontAttributeName];
CGRect size = [msgText.text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:atts
context:nil];
float textHeight = size.size.
float lines = textHeight / lineH
if (lines &=4)
composing=YES;
msgText.contentInset = UIEdgeInsetsMake(0,0,0,0);
sendView.frame = CGRectMake(0,appFrame.size.height-270 - (lines * lineHeight) + lineHeight ,320,56 + (lines * lineHeight)-lineHeight);
if (prevLines!=lines)
[self shortenTableView];
prevLines=
prevLines=
//let's change the frame of the chatTable so we can see the bottom
-(void)shortenTableView
[UIView beginAnimations:@&moveView& context:nil];
[UIView setAnimationDuration:0.1];
chatTable.frame=CGRectMake(0, 0, 320, [self textY] );
[UIView commitAnimations];
prevLines=1;
// show the chatTable as it was
-(void)showTableView
[UIView beginAnimations:@&moveView& context:nil];
[UIView setAnimationDuration:0.1];
chatTable.frame=CGRectMake(0,0,320,460 - 56);
[UIView commitAnimations];
//when user starts typing change the frame position and shorten the chatTable
-(void)textViewDidBeginEditing:(UITextView *)textView
{ [UIView beginAnimations:@&moveView& context:nil];
[UIView setAnimationDuration:0.3];
sendView.frame = CGRectMake(0,appFrame.size.height-270,320,56);
[UIView commitAnimations];
[self shortenTableView];
[msgText becomeFirstResponder];
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
#pragma mark - YDChatTableView implementation
//here are the required implementation from your YDChatTableViewDataSource
- (NSInteger)rowsForChatTable:(YDChatTableView *)tableView
return [Chats count];
- (YDChatData *)chatTableView:(YDChatTableView *)tableView
dataForRow:(NSInteger)row
return [Chats objectAtIndex:row];
&/span&虽然以非常出色的方式创建的Chat解决方案实现了创建聊天消息并显示这些消息的逻辑,但是它不发送和接收任何消息。因此,还需要实现一个遵循XMPP协议的真正的通信模块(注:有关XMPP协议请参考www.xmpp.org官方网站的内容)。
《iOS 高级编程》试读电子书,免费提供,有需要的留下邮箱,一有空即发送给大家。 别忘啦顶哦!
购书地址:
京东:/.html
当当:/.html
互动:http://product./3770647
亚马逊:/dp/B00P7NO4K2
微信:qinghuashuyou &
更多最新图书请点击查看哦(即日起-12月31日:关注@qinghuashuyou 发送:关注技术方向,即有机会参与图书抽奖,每周抽取十个幸运读者)
版权声明:本文为博主原创文章,未经博主允许不得转载。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:448588次
积分:5973
积分:5973
排名:第1891名
原创:115篇
评论:814条
(1)(1)(3)(2)(2)(1)(1)(3)(1)(1)(2)(1)(1)(6)(1)(2)(1)(9)(9)(4)(3)(3)(1)(1)(3)(25)(10)(9)(2)(2)(5)

我要回帖

更多关于 dereferencing 的文章

 

随机推荐