第七天,xib 定义tableviewcell以及纯代码定义cell

1.  介绍按照MVC的方式分项目文件夹

 

2. 介绍UITableViewController

** 问题:为什么要使用 UITableViewController控制器

原因:更方便。已完成连线和设置代理、数据源。

 

** UIViewController 控制器

self.view 是一个UIView 对象

复习UITableView的使用的方式 (UITableView + UIViewController实现)

 

**UITableViewController 控制器

注意: self.view 与 self.tableView 指的都是默认的那个 UITableView

UITableView 控件中默认的那个单元格是一个模板,功能类似于通过 xib自定义 Cell。

 

3. 自定义Cell (今天的主要知识点)

    3.1 通过xib自定义cell

    * 添加tableView

    * 加载团购数据

    * 新建xib,获取子控件

    * 封装

    * 最后引入headerView和footerView(插入广告,加载更多)

 

    3.2 通过代码自定义cell

    * 引入UITableViewController

    * 加载模型数据SteveZWeibo,用自带的cell展示基本数据

    * 新建一个SteveZWeiboCell,封装模型数据

    * 在init方法中添加4个子控件

    * 在set方法中给子控件设置数据

    * 在set方法中给子控件计算frame

    * 说明cell的高度无法计算

    * 设计SteveZWeiboFrame

    * 建立SteveZWeibo、SteveZWeiboFrame、SteveZWeiboCell的关系(提醒属性名不能叫做frame)

    * 性能优化

 

 

一、 团购案例

1. 运行示例程序并分析

1> 整体是一个UITableView

2> 上面的UIScrollView是在tableHeaderView中

3> 下面的加载更多是在tableFooterView中

4> 整体只有一个Section

5> 团购案例还是采用普通的UIViewController + UITableView的方式实现

 

 

实现步骤:

0. 为项目分Models、Views、Controllers、Others"文件夹", 把不同的类放到不同的"文件夹"

** 注意1: 此处的"文件夹"是虚拟的, 新建的时候叫做"New Group", 也就是说在实际磁盘上并不存在这些"文件夹"

** 注意2: 使用这种方式就保证了所有的文件都在mainBundle下

 

1. 拖拽一个UITableView到界面, 设置数据源

 

2. 设置控制器遵守数据源协议

 

3. 把图片素材与plist素材拖拽到项目中

 

4. 加载tgs.plist文件数据。使用模型数据。为了操作方便, 把模型的属性类型都变成字符串。

4.1> 创建模型类

4.2> 在控制器中通过"懒加载"的方式加载数据

 

5. 完成数据源代理的3个方法。

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

-(NSInteger)tableView:(UITableView *)tableViewnumberOfRowsInSection:(NSInteger)section

 

** 注意: 在下面的这个方法中, 先通过使用系统自带的Cell(使用SubTitle样式)把数据显示到界面上

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath

 

6. 系统自带的Cell不好用,那么使用"自定义Cell"

 

7. 自定义cell的两中实现方式:

7.1> 通过Xib实现

* 每个Cell中的内容是固定的, 控件个数、位置、尺寸等都一样的时候(团购案例)。

 

7.2> 通过自己写代码的方式来实现

* 每个Cell的结构不一样, 每个Cell中的控件的个数、样式都不一样的时候(微博案例)。

 

8. 通过xib方式实现自定义cell

8.1> 创建一个SteveZTGCell.xib文件。

 

8.2> 在xib中拖一个UITableViewCell, 设置高度为80, 宽度为屏幕宽度

 

8.3> 向UITableViewCell中拖子控件

* 拖一个UIImageView到Cell中, 设置里面的图片框的大小为80 * 60。X = 10 , y = 10

* 拖一个标题Label

* 拖一个价格Label

* 拖一个已购买人数Label

** 注意: 向Cell中拖控件, 默认就放在了ContentView中。

** 验证: 输出textLable的superView, detailLable的superViwe和cell的contentView的地址看是否一样。

 

8.4> 新建一个自定义的UITableViewCell类与xib中的这个Cell相关联

* 新建一个SteveZTGCell类继承自UITableViewCell

* 通过拖线的方式将Cell中的子控件拖线到SteveZTGCell的的属于性上, 方便将来使用

 

 

9. 改造数据源方法:cellForRowAtIndex中的创建cell的代码, 通过加载xib的方式来创建cell

9.1> 通过xib创建cell

UITableViewCell *cell =[[[NSBundle mainBundle] loadNibNamed:@"SteveZTGCell" owner:nil options:nil] lastObject];

 

9.2> 为了可以重用xib中的Cell, 所以要设置xib中的cell的identifier。这个identifier, 就是将来的重用ID

 

 

10. 设置cell中的子控件的数据

10.1> 在外部访问不到cell中的子控件的内容, 解决办法: 把模型数据传递给cell对象, 由cell对象内部自己来解析模型数据, 并把数据设置到对应的子控件中

10.2> 在自定义Cell的类中创建一个模型类型的属性, 重写该属性的set方法,在set方法中将数据赋值给控件。

 

 

11. 至此完成数据列表的显示, 运行看效果

 

 

12. 把根据xib创建cell的代码封装到自定义的cell类中

 

13. 设置tableView.rowHeight = xib中的cell的高度。如果没有为tableView指定行高, 运行起来的时候会有警告。

 

** 注意: 按照mvc的方式分不同文件夹来存储数据

 

 

================================================================================

14. 实现"加载更多"按钮

** 思路: 在viewDidLoad方法中, 设置tableView的tableFooterView即可

** 为tableFooterView中增加一个按钮, 设置按钮背景色, 不设置按钮的frame, 查看效果

** 注意:

        1> footerView的宽度永远是与tableView的宽度一致, 不能修改

        2> y值永远是0, 不能修改

        3> 只能修改x和height的值

 

14.1> "加载更多"分析

* 距离两边有"间隙"

* 点击"加载更多"后, 会显示一个Label"正在努力加载"、等待指示器, 并且背景色变成了白色

* 结论: 底部的"加载更多"是由多个控件组成的, 所以可以考虑使用xib来实现

 

 

14.2> 实现"加载更多"功能

1. 新建一个xib

2. 在xib中拖拽一个UIView, 设置高度为44

3. 在UIView中拖拽一个UIButton, 设置按钮的背景色为"橘黄色"、x = 10, y = 2, height = 40, width = 355, 设置按钮上的文字为"加载更多"

4. 编写一个自定义的UIView类与xib中的UIView相关联

5."加载更多"按钮设置单击事件

6. 封装一个"创建View"的类方法, 在控制器的viewDidLoad方法中创建该view, 并设置tableFooterView

 

 

 

***** 加载xib文件的另外一种办法 *****

1) UINib *nib = [UINibnibWithNIbName:@"" bundle:nil], nil表示使用mainBundle。根据xib文件创建nib对象。

2) UIView *vw = [[nibinstantiateWithOwner:nil options:nil] lastObject]。获取xib中的某个view对象(控件)

***** 加载xib文件的另外一种办法 *****

 

 

 

7.  监听xib中的UIView中的按钮的点击事件。模拟点击时的"等待"效果。

** 分析: 点击"加载更多"的时候, "拼命加载"的Label和"等待指示器"同时显示或消失, 所以可以把这两个控件再放到一个UIView中。

1) 拖一个UIView到Cell中, 设置width = 200, height = 40, x = 85, y = 2

2) 再拖一个"等待指示器"和Label到该UIView中

3) 设置"等待指示器"的Animating属性和Hides When Stopped

4) 通过拖线使用一个属性来引用这个UIView, 通过这个属性就可以控制这个UIView显示或隐藏

5) 完成点击"加载更多"按钮的操作

具体步骤:

    * 隐藏"加载更多"按钮

 

    * 显示"等待指示器"所在的UIView

 

    * 向模型数组中增加一个新的模型数据(既然要向数据模型中动态添加数据, 那么模型数组需要使用可变数组)

    ** 分析: 通过代理实现.

    1> 如何找代理, 自己想做某件事, 但是无法做, 看谁能做, 那么谁就可以拉过来做代理。

    2> 要做什么事? 那么代理协议中就要编写对应的方法声明

    3> 在自定义的FooterView类中, 增加一个代理协议

    ** 注意: 因为现在是要为某个UIView(会加到界面)上的一个控件来编写代理, 所以代理属性delegate要使用weak关键字来修饰

 

 

    * 刷新UITableView

    ** 注意:reloadRowsAtIndexPaths:方法只能用于在tableView总行数不变的情况下刷新数据(删除原来某行, 在替换成某行), 对于原来就没有的行, 在删除的时候就报错了。

    ** 解决: 使用reloadData方法或者使用insertRowsAtIndexPaths:@[idxPath] withRowAnimation:

    ** 注意: 通过[self.tableViewscrollToRowAtIndexPath:idxPath atScrollPosition:UITableViewScrollPositionTopanimated:YES];方法, 设置将某行滚动到view的最上方

 

    * 显示"加载更多"按钮, 隐藏"等待指示器"所在的UIView

 

    * 把"加载数据"和显示"加载更多"按钮、隐藏"等待指示器"的UIView放到一个延迟方法中执行

    /** 参考代码:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    

     });

    

     dispathch + GCD 来记忆

    

     */

 

 

15. 实现头部的图片轮播器

** 分析: 头部的所有内容都是放到了tableHeaderView中, 直接设置tableview的tableHeaderview属性

** 头部内容包含:

1) 图片轮播器

2) 分割线(通过UIView来实现)可以改变透明度,使线变得更浅色

3) 猜你喜欢,UILabel

4) 在自定义View的awakeFromNib中操作UIScrollView。添加图片轮播器。

 

** 结论: 通过一个xib来实现headerView

** 步骤:

1> 创建一个xib文件

2> 拖一个UIView到xib中, width = 375, height = 200

3> 创建两根分割线、一个"猜你喜欢"Label、一个UIScrollView

 

** 问题: 因为UIScrollView是放在自定义的xib的UIView中, 所以编写操作UIScrollView的代码不能写到控制器中, 一定是放在对应的自定义view的类中。如果UIScrollView放在控制器的view中, 那么操作UIScrollView的代码可以写在viewDidLoad中, 但是现在这些操作UIScrollView的代码该写在哪里呢?

** 分析1: 要获取UIScrollView控件对象, 需要等到这个控件加载完毕以后才能获取, 那么在什么时候这个控件才会加载完毕呢?以前都是在控制器的viewDidLoad方法中操作UIScrollView, 因为当控制器的view加载完毕后, 就表示view中的所有子控件都创建(加载)好了

** 分析2: viewDidLoad表示控制器的view加载完毕以后执行, 那么在自定义view类中, 那个方法(事件)表示是当前view加载完毕后执行呢?

** - (void)awakeFromNib表示对象已经从xib中加载好了。这个方法是NSObject类的。

 

** 答: 在自定义View的awakeFromNib中编写操作UIScrollView的代码, 添加图片轮播器。

 

 

 

 

 

=========================================================================================

一、微博案例

** 微博案例中的难点:

1> 自定义cell, 这个案例中的自定义cell不是通过xib创建的, 而是通过完全自己写代码来创建的自定义cell。

2> 每个cell中的控件的个数、位置都不一定一样, 所以每个cell都需要手动写代码来根据不同情况, 创建不同的cell

 

 

0. 按照MVC的方式分目录、拷贝素材

 

 

1. 使用UITableViewController

* 如果使用了UITableViewController,那么控制器内部的view就是UITableViewController

* 并且自动设置了数据源、代理为当前控制器

* 并且UITableViewController已经遵守了数据源协议与代理协议

 

 

2. 新建一个自定义的控制器类, 继承自UITableViewController

 

 

3. 编写微博模型类(5个属性)

** 注意: 如果CGRect不能使用, 那么导入#import<CoreGraphics/CoreGraphics.h>

 

4. 懒加载数据

 

5. 实现3个数据源方法

* 只有1个section

* 数据条数就是微博模型的个数

* 实现- (UITableViewCell *)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法

 

6. 创建一个自定义Cell类SteveZWeiboCell继承自UITableViewCell

 

 

7. 重写该类的initWithStyle方法, 在创建好cell后,同时动态生成里面的控件和内容。

* 将所有可能显示的控件都加到cell中(数据、frame都不需要设置)

* 注意: 主要要把cell的子控件加到cell的contentView中

* 在自定义cell中, 通过属性来引用Cell中的子控件, 方便其他方法中使用这些子控件

 

 

 

8. 为自定义cell类增加一个模型(数据模型)属性, 重写该属性的set方法, 在set方法中设置控件的数据(内容)、位置(frame)等

* 封装setttingData、setttingFrame方法

 

* 实现setttingData, 根据模型设置子控件的数据

** 注意: 配图如果有再设置, 如果模型中的配图为nil, 则不需要设置UIImageView.image的值

** 注意: CUICatalog: Invalid asset name supplied:(null)原因:imageNamed:参数是空则报错。

 

 

* 实现settingFrame方法, 设置子控件的frame

1> 假设每个控件的间距都是固定的margin = 10

2> 计算头像图片的frame

iconW = 30;

iconH = 30;

iconX = margin;

iconY = margin;

 

3> 计算昵称的frame

* 根据昵称的文字计算昵称label的宽和高

** 影响昵称Label的高和宽的因素: 字体大小、文字多少、高度取决于是否固定了宽度(是否限制了最大的宽度和高度)

** [字符串对象 boundingRectWithSize:CGSizeMake(MAXFLOAT,MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil]

** 保证这里计算的时候使用的字体大小和创建Label时设置的字体大小一致, 使用一个宏来统一设置

** 根据昵称Label的宽和高, 计算x和y

 

 

4> 计算vip头像的Frame

vipW =15

vipH = 15

 

 

5> 计算正文的Label的frame

* 封装一个根据字符串计算size的方法

-(CGSize)sizeWithText:(NSString *)text font:(UIFont *)fontmaxSize:(CGSize)maxSize

** 注意: 限制正文的宽度为355, 高度不限制

** 现在viewDidLoad中暂时修改行高300, 然后查看效果

** 设置Label中的文字可以换行(numberOfLines = 0 表示允许换行显示)

 

 

6> 计算配图的frame

pictureW = 100

pictureH = 100

 

 

7> 动态计算每个行的行高

* 根据是否有配图, 来决定单元格的高度是配图的最大的Y值 + margin, 还是正文的Label的最大的Y值 + margin

 

 

 

 

9. 通过UITableView的代理方法来指定每行的高度

** 注意:

代理方法- (CGFloat)tableView:(UITableView *)tableViewheightForRowAtIndexPath:(NSIndexPath *)indexPath

会先于数据源方法tableView:(UITableView *)tableViewcellForRowAtIndexPath:被调用。

 

** 解决: 在model中不仅包含每个控件的数据, 也包含每个控件的frame值。

 

 

10. 新建一个模型类, 这个模型中包含7个属性

* 1个数据模型属性

* 5个子控件的frame(都设置为只读的, 都是内部算出来的不是别人赋值的)

* 1个行高的属性(都设置为只读的, 都是内部算出来的不是别人赋值的)

 

 

11. 把设置vip图标的代码放到initWithStyle方法中, 因为只需要执行一次, 每条数据的这个图标都一样

 

 

12. 在settingData方法中, 判断当前的用户是否是会员

* 如果是则设置vip图标显示, 并且昵称文字为红色

* 如果不是则设置vip图标隐藏, 并且设置昵称文字为黑色

 

 

 

13. 解决cell重用时, 配图显示不正常问题

** 解决: 在设置数据的时候, 判断是否有配图

* 如果有配图, 则设置配图图片, 显示配图UIImageView

* 如果没有配图, 则设置配图图片隐藏

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值