在 initWithReuseIdentifier: 方法中, 首先需要调用父对象的同名方法, 然后创建对应的控件, 为控件赋值并设置相关属性
知识点:
获取当前屏幕的尺寸可以通过 [UIScreen mainScreen].bounds.size 来获取
知识点:
UIButton 控件的 contentHorizontalAlignment 属性可以更改控件内容的水平方向的对齐方式, 同理 UIControlContentVerticalAlignment 属性可以更改控件内容的垂直方向的对齐方式
知识点:
UIButton 控件的 contentEdgeInsets 属性可以更改控件内容的内边距
UIButton 控件的 titleEdgeInsets 属性可以更改控件文本框内容的内边距
两者都是需要传递 UIEdgeInsets 的结构体变量, 可以通过 UIEdgeInsetsMake( , , , ) 来创建
知识点:
UIImageView 中图片的平铺方式可以通过 contentMode 属性来设置, 该属性需要指定一个类型为 UIViewContentMode 的枚举量, 该枚举量主要有以下成员
- typedef NS_ENUM(NSInteger, UIViewContentMode) {
- // 图片拉伸填充至整个 UIImageView(图片可能会变形), 这也是默认的属性, 如果什么都不设置就是它在起作用
- UIViewContentModeScaleToFill,
- // 图片拉伸至完全显示在 UIImageView 里面为止 (图片不会变形), 这种方式图片完全放置在父容器的范围内, 空白区域显示背景色
- UIViewContentModeScaleAspectFit,
- // 图片拉伸至图片的的宽度或者高度等于 UIImageView 的宽度或者高度为止. 看图片的宽高哪一边最接近 UIImageView 的宽高, 一个属性相等后另一个就停止拉伸. 这种方式图片可能会超出父容器的范围
- UIViewContentModeScaleAspectFill,
- // 调用 setNeedsDisplay 方法时, 就会重新渲染图片
- // 下面的属性都是不会拉伸图片的, 图片也不会缩放, 如果图片原始尺寸过大, 可能会严重超出父容器的范围
- UIViewContentModeRedraw,
- // 中间模式
- UIViewContentModeCenter,
- // 顶部
- UIViewContentModeTop,
- // 底部
- UIViewContentModeBottom,
- // 左边
- UIViewContentModeLeft,
- // 右边
- UIViewContentModeRight,
- // 左上
- UIViewContentModeTopLeft,
- // 右上
- UIViewContentModeTopRight,
- // 左下
- UIViewContentModeBottomLeft,
- // 右下
- UIViewContentModeBottomRight,
- };
关于 UIViewContentMode 属性的更多信息请参考
https://www.jianshu.com/p/8c784b59fe6a
知识点:
如果图片超出了父容器的范围, 可以通过 clipsToBounds 属性来设置超出部分是否需要被裁剪, 它接收一个 Boolean 变量
重写 LJGroup 类成员的 set 方法, 在方法中对标题和在线人数进行赋值
为 headerView 注册单击事件
实现 headerView 的单击事件
分析: 该单击事件中主要实现两个功能, 第一是实现箭头的旋转, 这可以通过 setTransform 来实现; 第二是实现好友列表的折叠和展开, 这可以通过改变该 section 对应的行数然后刷新单元格来实现, 但单击事件是在 headerView 中实现的, 它无法直接刷新单元格, 因此需要代理. 代理方法虽然可以实现 tableView 的重载, 但是它无法知道当前单击的是哪一个 headerView, 因此我们需要以单击事件的 sender 为媒介, 传递当前 headerView 所在的 section 的索引号, 可以通过控件的 tag 来实现这个目的
- //LJGroup 类的声明
- #import <Foundation/Foundation.h>
- #import "LJFriend.h"
- @interface LJGroup : NSObject
- @property(nonatomic , strong) NSString * name;
- @property(nonatomic , strong) NSArray * friends;
- @property(nonatomic , assign) int online;
- // 用于记录当前列表是否为展开状态
- @property(nonatomic , assign , getter=isopen) Boolean open;
- -(instancetype)initWithDic:(NSDictionary *)dic;
- +(instancetype)groupWithDic:(NSDictionary *)dic;
- @end
- //LJGroupHeaderView 的声明
- #import <UIKit/UIKit.h>
- #import "LJGroup.h"
- @class LJGroupHeaderView;
- // 声明 LJGroupHeaderView 的 delegate 方法
- @protocol LJGroupHeaderViewDelegate <NSObject>
- @optional
- -(void)groupHeaderViewDidClicked:(LJGroupHeaderView *)groupHeaderView withSection:(NSInteger)section;
- @end
- @interface LJGroupHeaderView : UITableViewHeaderFooterView
- @property(nonatomic , strong) LJGroup * groupHeaderView_model;
- // 声明 delegate 属性
- @property(nonatomic , weak) id<LJGroupHeaderViewDelegate> delegate;
- +(instancetype)groupHeaderViewWith:(UITableView *)tableView;
- @end
- // 在. m 文件中为控件注册单击事件并实现
- -(void)groupHeaderViewClick:(UIButton *)sender
- {
- // 反转当前的 Group 的展开状态
- self.groupHeaderView_model.open = !self.groupHeaderView_model.open;
- [UIView animateWithDuration:0.3 animations:^{
- // 根据当前的展开状态判断应该旋转的角度
- CGFloat angle = self.groupHeaderView_model.isopen ? M_PI_2 : 0;
- // 使控件旋转
- [self.groupname.imageView setTransform:CGAffineTransformMakeRotation(angle)];
- }];
- // 刷新单元格 (调用代理方法)
- // 获取当前 headerView 的 tag
- NSInteger tagnumber = [sender superview].tag;
- if ([self.delegate respondsToSelector:@selector(groupHeaderViewDidClicked: withSection:)]) {
- // 调用代理方法
- [self.delegate groupHeaderViewDidClicked:self withSection:tagnumber ];
- }
- }
- // 在 controller 中实现 tableView 的代理方法来返回 headerView
- -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
- {
- // 获取模型信息
- LJGroup * group_current = self.groups[section];
- // 创建 headerView
- LJGroupHeaderView * headerView = [LJGroupHeaderView groupHeaderViewWith:tableView];
- // 为 headerView 赋值
- headerView.groupHeaderView_model = group_current;
- // 指定 headerView 的 delegate 属性
- headerView.delegate = self;
- // 将当前 section 的索引号传递给 headerView 的 tag
- headerView.tag = section;
- // 返回 headerView
- return headerView;
- }
- // 在 controller 中实现 LJGroupHeaderView 的代理方法
- -(void)groupHeaderViewDidClicked:(LJGroupHeaderView *)groupHeaderView withSection:(NSInteger)section
- {
- NSIndexSet * indexsection = [NSIndexSet indexSetWithIndex:section];
- //[self.tableView reloadData] 太过浪费资源, 下面的方法可以进行某个 Section 的 reload, 节省资源
- [self.tableView reloadSections:indexsection withRowAnimation:UITableViewRowAnimationAutomatic];
- }
问题: 小箭头的旋转是动画形式执行的, 动画尚未执行完毕的时候, 这个 section 就已经开始执行 reload 的代码了, 所以会看到箭头刚开始旋转就闪一下又恢复了.
解决办法: 将箭头旋转的行为放置在 layoutSubvies 方法中, 这个方法在子视图被重新布局时被自动调用, 默认什么也不做.
- -(void)layoutSubviews
- {
- [super layoutSubviews];
- [UIView animateWithDuration:0.3 animations:^{
- //NSLog(@"该动画被执行了");
- CGFloat angle = self.groupHeaderView_model.isopen ? M_PI_2 : 0;
- [self.groupname.imageView setTransform:CGAffineTransformMakeRotation(angle)];
- }];
- }
来源: http://www.jianshu.com/p/72a2cababccb