【QQ聊天界面、创建模型、懒加载数据 Objective-C语言】

一、今天我们要做的就是这个案例

效果

1.我们今天要做的案例,做好了之后的效果就是这样

这个案例,和昨天那个微博的案例是非常相像的,

哪些相像呢,

1)整体是不是也是能滚动啊,

2)能滚动,它不仅仅是一个UIScrollView

它里面,这个也是一行、两行、三行、四行、

所以说,整体,这也是一个tableView,

今天我们就做这么一个案例,

2.这个整体也是一个tableView,

这个tableView里面,也是我们需要先加载plist数据,然后创建模型,

然后,自定义Cell,把Cell都给它显示到这个上面

这些是不是和我们上次那个微博,都是一样的,

上次微博,也是写模型,懒加载数据,然后呢,自定义Cell,计算坐标,把Cell显示到这个屏幕上,

今天这个也是一样的,只不过后面又给大家多了这么一个,就是,注意,最后一行,现在是在这里,

这里

当我弹出键盘的时候,它并没有挡住这个最后一行,

最后一行

键盘弹出,键盘并没有把这一行给盖住,

现在这一句话是在这里,键盘弹出之后,键盘并没有把这一行给盖上,而是整个这个UITableView,向上移动,移上来了,

再加这么一个滚动,

我们可以通过“通知”的机制,来实现,

那么,所以说,今天我们就做这一个案例,

把这个案例搞定,就OK了,

3.发一些文字,点击“发送”

发送

发出去以后,它是不是自动给我回一个吧,

它这个就是,不论你发什么,它都回这个字,“滚”

当然我们程序里面,可以改,

你可以改成它,类似于聊天机器人一样,根据预先设定好的一些文字,给它回复一些词,

OK,今天我们就做这么一个案例,

二、在做这个案例的时候,我们首先,

1.有一部分代码,是和我们那个微博,是非常像的,

所以说,在写这个的时候,其实也是在复习微博,

所以说,你脑子里应该把那个概念,上次微博那个实现思路,应该还得有,

然后这里再给大家,简单介绍一下,

我们这个UITableView,你看我在滚动这个UITabView的时候,你看下边这一条儿,

这一条儿

这一条儿,你看它滚动吗,没有随着滚动吧,

那么,你说,最下边的这一条儿,它是在这个UITableView里面的吗,不是吧,

如果说,最下面这一条儿,是在UITableView里面,那么,当你滚动的时候,它是不是会跟随着滚,

就是说,你设置了UITableView的HeaderView、或者是FooterView,

这样的话,当你滚动UITableView的时候,它也会跟着滚,

现在,它没有跟着滚,证明它不是在UITableView里面,

所以说,我们整个这个界面的基本设计,是

1)拽一个普通的一个UIViewController

2)在这个UIViewController上面,放一个UITableView,

3)下面留出44的高度来,

4)然后,在这下面,放一个,底部的放一个UIView,

5)在这个UIView里面,拖一些子控件,

基本上,是这样的一个思路,

那么,上面是一个UITableView,

下面是一个UIView,

分两部分的,来组成的,

3.那么,既然我们这个界面,是分两部分,

上面是一个UITableView,

下面是一个UIView,

所以说,我们这里拽的时候,是不是应该用一个UIViewController,

而不是直接使用一个UITableViewController,

如果直接使用的是UITableViewController,

这样的话,整个界面上的控制器所管理这个View,自身本身就是一个UITableView,

但是我们这里不是,

是上面是UITableView,下面是UIView,

所以说,我们这里,不能用UITableViewController,

要用一个UIViewController,

里面拽两个子控件,一个是上面的UITableView,一个是下面的UIView,

微博

这里是两个子控件,一个是上面的UITableView,一个是下面的UIView,整体是一个UIViewController,

是不是大致上,就是由这么三部分,组成的,

好,那么,接下来,我们就一起来看一下,这个怎么实现,

二、实现这个案例

1.我们一起来建一个,这个项目,我们还是用3.5英寸,

新建一个项目,名字叫做“001QQ聊天界面”

项目前缀,来个“CZ”

CZ

然后在这个里面,首先,第一步,要干什么,

是不是先建那个文件夹,

先分组吧,建文件夹,New Group,

我们这里建三个,MVC、Models、Views、Controllers,

再建一个Others,

建文件夹

把这些文件都拖到对应的文件夹里面,

然后,我们基本上,这个分组,是不是已经分完了,

4.紧接着,我们先把那个图片、和素材,拷进来,

我们说,这个程序运行起来之后,首先,是不是有一个启动图片,

然后呢,我们还有一个应用程序图标吧,

图标

看这里,是不是有个应用程序图标,

当你点击的时候,

点击

是不是有个启动图,

所以说,我们先把这个应用程序图标、和启动图,给它拷贝过来,

那么,要拷应用程序图标,在哪里,是不是要找到素材,

素材,在哪里,在这里吧,这里是不是有个QQ聊天这个文件夹,里面有素材,

素材

这个素材里面,AppIcon,就是我们所有的素材,

那么,这些图片,你看,凡是@2x的,是不是都是为那个,我们的视网膜屏幕准备的啊,

视网膜屏幕

每一个大小,大家看,这些图片,每一个大小都不一样,

大小

我们要做的就是,把所有的图片,直接拷到素材里面,拷到我们的项目里面,

找到我们的AppIcon,然后呢,把这一堆,直接拽过来,

拽过来

拽过来,出现这个绿色加号的时候,就松手,

绿色加号
这样的话

这样的话,就一一对应了吧,

5.然后,我们要设置启动图,设置启动图,我们直接怎么做呢,

直接,我们先选中项目,

默认

默认,它是不是使用的是这个LaunchScreen.xib,来实现启动图吧,

我们把这里,Use Asset Catalog(使用资产目录),把这里改成,

改成

把这里改成,迁移到使用图片文件夹的方式,

点一下“Migrate”,“迁移”,

迁移

然后呢,这里变成了LaunchImage,

文件夹

然后呢,把下面这个删掉,

删掉

OK,删掉以后呢,再打开我们的这个Images.xcassets,文件夹,

发现,这个地方,是不是有一个LaunchImage,文件夹了,

文件夹

然后,把我们的什么,启动图片,拷进来吧,

启动图片

这里给大家提醒一下,以后,从xCode6以后开始,iOS8系统里面,

当你拖素材的时候,只要你把图片,拖到这个Images.xcassets文件夹下,那么这个时候,它程序编译的时候,会把所有图片,编译到一个好像是后缀是assets这个文件里面,也就意味着,以后,你下载的那些通过网上,或者通过应用商店里面,下载的那些别人做的应用,你就拿不到他们的图片了,就拿不到他们的图片了,

现在,大家从这个苹果商店,下载的别人的应用,下载下来以后,那个.app,那个Bundle里面,是不是有所有的这个应用所有的素材,是不是都能拷出来,

但是,以后可能就不行了,

所以说,你现在下的那些素材,别扔,

不要删,别说随时我都可以联网下,以后,凡是拖到这个Images.xcassets文件夹里面的图片,编译的时候,会生成一个加密的文件,目前还破解不了,所以说,以后那些图片,你下载下来是拿不到的,

所以说,你现在下载的之前那些版本的,那些就是ipa,那个资源包,不要删,你先留着,说不定以后你就拿不到这个资源图片了,

自己做,你就费劲了,

所以说,大家要注意这个问题,

6.好,接下来,我们把我们的启动图片,现在我们只要把App下载下来,这些图片,是不是都可以盗出来,

盗出来

然后,把它拽进来,

拽进来
拽进来

OK,拽进来以后,注意看,我们再复习一下,

1)这个图片,是为

这个图片

这个图片,是为了什么程序而生的,

哪个屏幕而用的,

大小

首先,看,是1x,肯定不是视网膜屏幕,那么,在看它的大小,是320乘480,几英寸的,3.5英寸吧,3.5英寸的非视网膜屏幕,

2)这个一看,是2x,

2x

这个一看,2x,是视网膜屏幕吧,

一看它,乘以2以后,是640乘960,那么除以2以后,就是320乘480,

所以说,这个也是为3.5英寸的视网膜屏幕而生的吧,

并且,这两个图片,都是在iOS5,iOS6,下

左边这个是,iOS5下的3.5英寸非视网膜屏幕,

右边这个是,iOS5、iOS6下的3.5英寸视网膜屏幕,

3)然后,再看这个,

这个

这个,它的大小是多大,640乘960吧,那么一看这个640乘960,

肯定就是乘以2以后的,大小,证明它原来也是320乘480,

也就是说,这个也是为3.5英寸的视网膜屏幕而生的,

那么,它的大小,是不是和这个图片,的大小,刚好是一样的,

大小

所以说,我们要把它拽过来,

那么,按住option键,直接把它拖过来,

拖过来

就OK了,

拖过来

那么,但是,这个时候,大家会问,为什么同样一张图片,一模一样,都是为视网膜屏幕,3.5英寸的视网膜屏幕,而生的,为什么,要写两个呢,为什么这里要拽两个呢,

一个是为iOS5、iOS6,而生的,一个是为iOS7、iOS8,而生的,

就是说,有可能,它3.5英寸的屏幕,比如说,iPhone4S,它升级到了iOS7、iOS8,是不是也有可能,这个时候,是不是就得用这张图了,

所以说,把这个都拽过来,

4)这个Retina4,一看就是为4英寸的屏幕而生的吧,

而生

4英寸的屏幕,你看一下,640乘1136,

是不是就是iPhone5,iPhone5S,就是320乘568,吧,

320乘568,就是iPhone5,iPhone5S,iPhone5C,等等,

5)那么这个,一看

一看

一看,4.7英寸,肯定是iPhone6,

6)这个一看,

这个一看

这个一看,是5.5英寸的,肯定是多少,iPhone6plus,吧,

所以说,我们直接把这几个,都拽过来,

是不是就OK了,

大家就可以看这个图,来分析它是哪张图片,

好,把这个启动图,和应用程序图标,拽过来以后,

command + R,运行一下,

哦,先不走,先看一下这个控制器,

控制器现在是多大,

控制器

控制器,我们是不是得把它调成3.5英寸的啊,把这里调成什么,iPhone4S吧,

iPhone4S

这样的话,我们就看起来,小点儿了,command + R,

看到

看到了吧,启动图有了吧,

对,这就是我们这儿这个东西,好,

7.把这个拽好以后,接下来,我们就把那些素材,拷进来

我们先把我们这个Chat,先把这个图片,也给它拽过来,

拽到我们Image.xcassets下,

所以说,以后,你的项目,不希望别人盗用图片,就把图片拽到这个Images.xcassets下面啊,

拽过来

这样的话,以后加密了以后,就盗用不了你的图片了,

然后呢,我们这个messages.plist文件,把它拽到我们这个SupportingFiles下面,

OK,好,这样就拽进来了,

这样的话,我们是不是基本素材,就拷进来了,

基本素材拷进来以后,等会儿我们是不是肯定要实现,把这个数据,加载到我们这里,

但是,加载的时候,怎么做呢,我们首先,是不是要把这个数据懒加载起来,

然后呢,我们先把数据懒加载起来,

二、那么,写懒加载的时候,注意看,

1.我还是,观察一下这个messages.plist文件,

里面,整体是一个什么,Array吧,

Array

整体是个Array吧,里面每一个就是一个字典,

我们看,这个字典里面,

字典

分几项,是不是三个键值对儿,

分三个键值对

1)第一个,是什么,消息正文,就是你发的QQ消息,text,

2)第二个,是什么,是你这个QQ消息的发送时间,time,

3)第三个,是什么,是你这个类型,type,这个类型,指的是这个消息是“你发的”,还是“对方发的”,

如果说,你给别人发的,这个消息类型,就是“0”,

如果说,是别人给你发的,这个消息类型,就是“1”,

来,给大家看一下我们这里,

看一下

大家看一下,比如说,这个,右边是不是你,左边是不是对方吧,

右边是你,你首先发一条消息,那么这个消息的类型,就是”0“,

表示这个是你发的,

然后这个,你又发了一个消息,”问你个严肃的事“,这个是不是又是你发的,

那么这个消息的正文,text,就是“问你个严肃的事”,这是消息的正文,

然后呢,这个消息的,我们看,这个消息的发送时间,

发送时间

发送时间,就是我们哪里,

发送时间

发送时间,就是我们这个time,这个键吧,这个time,前边是“前天10:27“,

然后呢

然后呢,这个消息的类型,就是“0”,因为它是你发的,而下面这个消息,它的类型,就是“1”,是表示对方发的,

对方

所以说,我们这个type,就是表示当前,发送消息的,是谁发的,这就是我们这里这么一个type,

好,接下来,我们就是先把这个模型写好,这个模型,有几个属性,

三个吧,第一个是什么,text,time,type,

2.我们现在来建一个模型,

OK,因为我们这是一个,用来表示消息的模型,所以我们这个模型,就可以叫什么,message,CZMessage,

CZMessage

注意,有人说,这儿是不是要加个s啊,CZMessages,

message

这个地方,不要加s,因为它表示,模型,这个模型,表示一个对象,一个CZMessage,是不是表示一个对象啊,

所以我们一般模型,不加s,

你说,这儿为什么加s呢,

模型

因为这个文件夹下,是不是有很多个模型啊,

所以,我们这里就是CZMessage,

哦,这是我们示例程序吧,差点儿建错了,我去,这个才是我们写的吧,

示例程序

这个才是我们写的,是不是里面干干净净的,什么都没有啊,

示例程序

找到我们模型文件夹,Models,右键,New File,

右键

建一个这个类文件,

CZMessage

名字叫,CZMessage,

点Next,

点击下一步,

模型

然后,在模型里面,是不是三个属性,

那么,三个属性,我就给它写一下吧,我不拷贝,

1)第一个叫什么,text,消息的正文,

//消息的正文

@property(nonatomic,copy)NSString *text;

2)第二个是什么,time,消息发送时间,

//消息发送时间

@property(nonatomic,copy)NSString *time;

3)第三个是什么,type,消息的类型(),

注意,我们在这个plist文件里面,看到类型是一个数字类型啊,

在这里插入图片描述

数字类型,是一个0和1,那么我们在写模型的时候,你这里用一个数字类型,0和1,来表示这个消息类型,肯定是没问题的,

0:表示你自己,

1:表示对方,

没有问题,

但是我们这里也可以不用这个0和1,不用这个数字类型,

可以使用一个枚举吧,

枚举,我们学过,那么我问一下大家,使用枚举,我这里消息的类型,就两种,要么是0,,要么是1,0表示自己,1表示对方,

我这里用数字合适,还是用枚举合适,

枚举合适吧,为什么,就是说,如果要是用数字的话,你开发过程当中,比如说,别人要用你这个属性的话,它是不是还得查一下,0表示什么,1表示什么,

他是不是还得查啊,不查还不敢随便写,因为谁知道你这个,0表示什么,1表示什么,

但是用枚举的时候,你在写代码的时候,智能提示,是不是直接就看到这个枚举了,

一看这个枚举,的意思,因为都是字母,都是字母表示的,文字表示,一看这个意思,就知道,第一个枚举表示什么,第二个枚举表示什么,

是不是省的去查开发文档,省的去,再去查这个0表示什么,1表示什么,就是为了开发当中方便,

事实上,枚举,最终就是什么啊,数字吧,

枚举最终就是数字啊,就是数字类型,

所以说它,和数字是一样的,

只不过,开发的过程中,在有限的几个值的时候,用枚举,更方便,仅仅是为了开发更方便,

所以说,我们这个类型,有两个值,我们就用枚举,来表示,

既然要用枚举,是不是要写一个枚举啊,

那么,我请问大家,这个枚举应该写在什么地方,

是不是需要新建一个文件,来写枚举,

不需要吧,为什么,因为你这个枚举,是不是要在这个类当中使用,

这个类

用到这个类的时候,才会用到枚举,所以说,可以直接在这个类的头文件的上边,写个枚举啊,

枚举

枚举怎么定义,typedef吧,

typedef
enum

这儿来个什么,括号{};

枚举

然后,这个枚举类型,注意看,这个枚举,怎么起名儿,

这个类

因为,我这个枚举,是为了这个类中的一个属性,而用的,

所以说,这个枚举首先是以这个类名儿,开头,CZMessage,

开头

为这个类中,哪个属性用的呢,这儿将来会有一个type,这个属性,

我先把这个写上,

type属性

这儿将来会有个type这个属性,所以说,我后面就加一个Type,

CZMessageType

type

明白吧,这就是给枚举的这个起名儿,

然后呢,这个枚举里面,包含几个值呢,包含两个值,

每一个值,最好,就以这个枚举的名字开头,

比如说,这个枚举,叫CZMessageType,

1)第一个是表示自己吧,CZMessageType……

第一个

表示自己,这里来个Me,CZMessageTypeMe

枚举

Me,或者self,都OK,都OK,

然后呢,它是不是

是不是

CZMessageTypeMe = 0,

0表示第一个,给它赋值个0,

2)然后再来一个,CZMessageTypeOther = 1,

Other,对方,

这个就是枚举

OK,这个就是表示自己,

下面这个就是表示什么,表示对方,

对方

那么,有人可能会问,如果不给它设置这个0和1,行吗,

行,你不设置,默认就是,第一个就是0,第二个就是1,

那么,如果你手动设置上,是不是更清楚,

如果你希望,第一个是10,那么这儿是不是要写个10,

写个10

第二个是20,那么这儿是不是要写个20,

这个时候就必须得明确指定了,

明确指定

如果你不明确指定,默认就是0,和1,但是我写上,肯定也没错,

好,这样的话,这个枚举,就写好了,

写好以后,接下来,我们这个地方,枚举是基本数据类型,还是对象类型,基本数据类型吧,所以说,我们这个地方,应该用什么,

assign

这个地方,应该用什么,assign,吧,

assign

这个地方用assign,后面这个数据类型这里,应该用什么,

类型

应该用枚举吧,

这里应该用枚举

@property(nonatomic,assign)CZMessageType type;

这个地方,还要它吗,“*”,不要,

去掉星号

这是不是我们这个消息类型吧,

//消息的类型(表示是对方发送的消息,还是自己发送的消息)

@property(nonatomic,assign)CZMessageType type;

OK,这样的话,这三个属性,都有了,

我们将来,要把这个字典,转成模型啊,

字典

所以说,是不是要在我们的这个模型里面,干什么,是不是给它封装两个方法,根据字典,创建模型的这两个方法,

9.封装两个方法

1)怎么写,

- (instancetype)initWithDict:(NSDictionary *)dict;

+ (instancetype)messageWithDict:(NSDictionary *)dict;

方法

是不是实现这两个方法,就OK了,

2)实现这两个方法

- (instancetype)initWithDict:(NSDictionary *)dict{

if(self = [super init]){

[self setValuesForKeysWithDictionary:dict];

}

return self;

}

实现

3)然后呢,怎么写,

+ (instancetype)messageWithDict:(NSDictionary *)dict{

return [[self alloc] initWithDict:dict];

}

message

这样,是不是创建好这些了,

好,这样的话,这两个创建好了,

现在这个模型,创建完毕以后,我们是不是第一个这个模型,已经写好了,

写好了这个模型以后,注意,还没完,

因为我们等会儿是不是也要实现,就是这个,等会儿是不是也要根据,是不是也要进行我们这个自定义单元格啊,

单元格

注意看,这TableView里面,这就是一行、这就是又一行、这就是又一行,

每一行

每一行里面,是不是都有这么几个东西,

就是说,每一行里面,是不是

1)都有一个Label、显示时间,

2)都有一个图片框,显示头像,

3)并且,注意看,右边这个是一个按钮,这是一个按钮,这不是Label,

Label

这是个按钮,这不是Label,因为它可以设个背景图,它可以设一堆,这是一个按钮,

就是说,每一行里面,是不是都有这三个子控件,并且,每一个子控件,大小是不是可能不一样,

每一个子控件,大小,可能不一样,

这行中,这个图片的X在这里,而在这行中,这个图片的X,是不是在这里,

那么,这行中,这个按钮的X在这儿,而这行中,这个按钮的X,在这儿,

X在这儿

而这行中的按钮,X在这儿,

这行

就是说,每一个单元格中,虽然都是有同样的三个子控件,

但是每个子控件的大小,和每个子控件的X、Y,可能不一样,

所以说,这个时候,我们这里是不是不能通过xib,来实现这个自定义Cell吧,

所以说,这个时候,我们还是要通过什么,还是要通过完全手写代码的方式,是不是来实现这个自定义Cell,

既然要通过手写代码的方式,来实现这个自定义Cell,

所以说,和昨天的微博,意思是一样的,

我们在创建Cell中的子控件的时候,就要动态去为每一个子控件,是不是计算坐标啊,

动态为每一个子控件,计算坐标,最后是不是还要计算每一个单元格的每一行的行高吧,

既然要动态为每个子控件计算行高,

计算坐标、计算行高,

所以说,这个时候,肯定会用到一个frame模型,是不是,

对吧,肯定会用到一个frame模型,

咱们昨天,微博,是不是通过frame模型,来实现的,

所以说,我们一定会用到一个frame模型,那么既然我们已经知道等会儿一定会用一个frame模型了,所以,有必要先写一遍,再翻过来改吗,

直接,是不是我们给它建两个模型,就可以了,

一个就是数据模型,再一个,就是frame模型,

一个是数据模型,再一个是frame模型,

思考,这个frame模型中,有几个属性,

1)首先,frame模型中,有一个属性,它是包括了一个数据模型啊,

frame模型中,要包括一个数据模型,

这是第一个属性,

2)然后,接下来,就是包括一堆子控件的frame,

注意看,我们每一个单元格中,有几个子控件,

属性

一、二、三、是不是有三个子控件,

所以说,这个时候,我们是不是一个是在frame模型中,第一个属性,是包括一个数据模型,这个属性,对吧,

还有三个,是包括我们的这些子控件的frame啊,

3)还有一个,是应该有一个行高吧,

还有一个,有行高,

现在我们是不是只分析出来了这五个属性啊,

那么,现在我们就写五个属性,

等会儿不够的时候,我们再给它加,

大家写的时候,就先分析出这五个,就OK了,

那么,基本上,frame,这个模型中,包含哪些属性呢,

1)第一,就是它会引用一个数据模型,

2)第二,当前这个单元格中,有几个子控件,这里就有几个frame,有几个属性吧,

3)第三,是不是还有一个行高,

是不是就是这三部分组成,

好,那么接下来,我们就再写一个这个frame模型,

会写这个frame模型了吗,

来,咱们新建一个frame,这个模型,

右键,New File,

右键

新建一个类,

类

叫什么,CZMessageFrame,

frame模型

OK,然后,在这个frame模型中,开始写它的属性,

1)第一个,是不是我们的数据模型吧,

既然,你要引用数据模型,所以,我们这里,是不是要写数据模型这个对象吧,

数据模型

@class CZMessage;

OK,这里来个什么,

数据

这里来个什么,CZMessage吧,

CZMessage

叫什么名字,message吧,

message

这是一个数据模型,

//引用数据模型,

数据模型

2)好了,这个属性有了以后,接下来,这里有三个子控件,要写三个属性,来保存那三个子控件的frame吧,

第一个子控件,是不是这个时间,这个Label吧,

时间

//时间Label的frame

@property(nonatomic,assign)CGRect timeFrame;

CGRect

但是,这里报错了吧,为什么报错,

但是

这里,是不是它提示你,

提示

提示你,没有这个东西吧,

提示你,不能够识别这个CGRect类型,

注意,CGRect,CG,就是CoreGraphic,核心绘图,

所以说,我们这儿,应该导入哪个框架,是不是CoreGraphics框架,

导入框架

#import < CoreGraphics/CoreGraphics.h >

导入这个,是不是就OK了,

导入

直接导入那个UIKit,是不是也行啊,

这样的话,我们是不是就有这个东西了,

OK,有了它以后,接下来,是不是该第二个是什么,

3)第二个,是不是头像的一个frame吧,

头像

//头像的frame

@property(nonatomic,assign)CGRect iconFrame;

头像

4)第三个是什么,该我们这个按钮了吧,

按钮

//正文的frame,

@property(nonatomic,assign)CGRect textFrame;

正文

5)最后一个,是不是行高,

//好高

@property(nonatomic,assign)CGRect timeFrame;

这样的话,我们这几个属性,是不是都有了,

行高

二、注意看,现在这个frame模型写好了,我现在需不需要为这个frame模型,封装两个initWithDictionary、和messageFrameWithDictionary,需不需要封装这两个方法,

1.不需要,因为我们等会儿这个frame,是根据字典来创建这个frame对象吗,

我们这个frame模型,不是根据一个字典来创建的吧,是不是不是根据字典来创建的,

我们刚才,这个数据模型里面,有这三个属性,

数据模型

将来是不是要把这个字典中的这三个属性,赋值给这个数据模型,

字典

所以说,这个数据模型,是不是根据那个字典来创建的,

所以说,是不是要封装这个initWithDictionary、和这个messageWithDictionary啊,

封装

而我们的这个frame,等会儿不是根据那个字典来创建的,

frame

这个时候,是不是不需要封装initWithDictionary、和messageFrameWithDictionary,

好,这个不需要封装,那么这个模型,就基本上写完了,

2.但是,我们昨天写的这个frame模型,和数据模型之间的关系,是不是等会儿,懒加载的时候,要把这个数据模型,加载起来,赋值给这个frame模型啊,

数据模型

在赋值的时候,我们说,懒加载的时候,就要计算每一个子控件的frame,和行高了吧,

对,所以说,我们是不是要重写当前这个message这个属性,的set方法吧,

在这个message,属性的set方法里面,计算每一个子控件的frame,和行高,

所以说,这个时候,我们得把这个message属性的set方法,写一下,

2.写一下这个message属性的set方法,

我现在是不是要重写这个set方法,

OK,在这里,找到我们的CZMessageFrame.m文件,

set方法

在这里,重写message属性的set方法,

set方法

- (void)setMessage:(CZMessage *)message{

//在这个里面,首先,第一步,_message = message,吧,

//把这个message参数,赋值给我们的下划线message吧,

_message = message;

//然后,后边,赋值完毕以后,就该干什么了,计算每个控件的frame和行高,

//我们第一个,比如说,计算完那个Label的frame,让这个time,时间那个Label的frame

_timeFrame = ……

时间的frame

让这个时间Label的这个frame是不是等于具体的一个frame啊,

frame

也就是说,我们所有的这些属性,

属性

这些frame,这些属性,是不是都是在这个类内部,在这里赋的值,

frame

也就是说,它不是外部给我赋值,是不是我内部算出来的,既然都是你在内部算出来的,那么这个属性,有必要让别人给你赋值吗,没有,

它是不是别人不能给你赋值,只能是在你内部,去计算这些属性的值,别人是不能随便给你赋值的,既然你不希望这些属性能别人给你随便赋值,这个地方要加什么,对,这个地方要加一个readonly,

只读

所以说,我们这里每一个这些只能内部计算出来的属性,是不是前面都要加一个readonly,啊,

只读

那么,接下来,在frame模型里面,我们后面计算坐标,对我们相对来说是一个“体力活儿”吧,

这个“体力活儿”,我们等会儿再算,

体力活儿

我们现在,假设这个地方已经算好了,可以吧,

假设,它已经算好了,

算好了

对,假设它已经算好了,既然这里每一个子控件的frame,已经算好了,

行高也算好了,

数据模型有了,

数据模型

等会儿,我们只要懒加载数据,然后实现一下数据源方法,就OK了,

那么,所以说,模型写好以后,接下来,就开始懒加载数据,

额,在哪里懒加载数据呢,

三、懒加载数据,

1.在哪里懒加载数据呢,

是不是在这个控制器里面,

控制器

在这里,懒加载数据

懒加载

2.我们先来一个

@property(nonatomic,strong)NSArray *messages;

注意,这里是NSArray,还是NSMutableArray,

数组

为什么是NSMutableArray,

可变数组

因为等会儿,我们每次发一个新的消息的时候,这个新的消息,是不是都会动态加到这个TableView的数据里面,

所以说,这个消息的个数,不是一开始定死了,后面会随着程序的运行,会不断的增加消息吧,

所以说,这个应该用一个可变的数组,NSMutableArray,

这个NSMutableArray里面,因为我们现在用的这个模型,不是一个简单的一个数据模型,我们封装了一个frame模型了,

所以说,这个MutableArray,里面,保存的应该是frame模型,

messageFrames;

messageFrames

//这就是用来保存每一个消息的frame对象,

//用来保存所有的消息的frame模型对象

在这里插入图片描述

@property(nonatomic,strong)NSMutableArray *messageFrames;

3.有了这个以后,我们就要进行懒加载了,

懒加载

- (NSMutableArray *)messageFrames{

if(_messageFrames == nil){

//1.首先,拿到messages.plist文件的根路径

NSString *path = [[NSBundle mainBundle ] pathForResource:@“messages.plist” ofType:nil];

//2.拿到这个路径以后,紧接着,我们要加载数据了,

NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path];

//3.创建一个NSMutableArray,

NSMutableArray *arrayModels = [NSMutableArray array];

//4.字典转模型

for(NSDictionary *dict in arrayDict){

//5.每次有一个字典对象,要创建一个数据模型

//6.创建一个数据模型

CZMessage *model =

//7.创建一个frame模型

//8.把frame模型,加到arrayModels里面

//9.既然这里要用到数据模型,和frame模型,所以我们这里要导入头文件,

导入头文件

//创建一个数据模型

CZMessage *model = [CZMessage messageWithDict:dict];

//创建一个frame模型

CZMessage *modelFrame = [CZMessageFrame alloc] init];

//创建好这个frame模型以后,接下来,让这个frame模型.message属性

modelFrame.message = model;

这样

//这样,把数据模型,赋值给这个frame模型了,

//在你把数据模型,赋值给frame模型的同时,里面执行这个set方法,

set方法

//计算每一个子控件的frame了,

//假设这儿已经计算好了

//假设这里已经计算好了以后,frame模型有了,然后呢,数据模型有了,接下来,该怎么办,

[arrayModels addObject:modelFrame];

}

_messageFrames = arrayModels;

return _messageFrames;

}

//这样的话,我们这个懒加载数据,是不是也搞定了,

懒加载

//所以说,我们完全自定义单元格的时候,因为单元格的frame都得自己算,所以需要两组模型,一组是数据模型,一组是frame模型,

//这样的话,我们懒加载,就搞定了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风清晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值