效果图
项目分析
JKFriendsModel
加载数据(字典)
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
构建class method
+ (instancetype)friendWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict];
}
JKGroupModel
加载数据(字典内含数组)
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
//加载组内字典数据
[self setValuesForKeysWithDictionary:dict];
//加载组内数组数据
NSMutableArray *muArray = [NSMutableArray array];
for (NSDictionary *dict in self.friends) {
JKFriendsModel *model = [JKFriendsModel friendWithDict:dict];
[muArray addObject:model];
}
self.friends = muArray;
}
return self;
}
HeaderView
子类
@interface HeaderView : UITableViewHeaderFooterView
初始化headerView
//类似cell的创建
+ (instancetype)headerView:(UITableView *)tableView{
static NSString *identifier = @"header";
HeaderView *header =(HeaderView *) [tableView dequeueReusableCellWithIdentifier:identifier];
//用以下这个方式,则只创建一次,此项目不采用
//HeaderView *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier];
if (!header) {
header = [[HeaderView alloc] initWithReuseIdentifier:identifier];
}
return header;
}
//这个headerview其实就是个button
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier{
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setBackgroundImage:[UIImage imageNamed:@"header_bg"] forState:UIControlStateNormal];
//[button setBackgroundImage:[UIImage imageNamed:@"header_bg_highlighted"] forState:UIControlStateHighlighted];
[button setImage:[UIImage imageNamed:@"arrow"] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
button.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
button.imageView.contentMode = UIViewContentModeCenter;
[button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
button.imageView.clipsToBounds = NO;
_arrowBtn = button;
[self addSubview:_arrowBtn];
//创建label,显示当前在线人数
UILabel *labelRight = [[UILabel alloc] init];
labelRight.textAlignment = NSTextAlignmentCenter;
_label = labelRight;
[self addSubview:_label];
}
return self;
}
UIButton的属性设置
//内容框的内边距
button.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
//内容的对齐
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
//标题的内边距
button.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
//为button设定方法
[button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
BOOL值取反
self.groupModel.isOpen = !self.groupModel.isOpen;
调用代理方法
代理就好比是自己有个武器不能用,然后让自己的小喽啰”id delegate” 交给他人来”viewController” 使用
if ([self.delegate respondsToSelector:@selector(clickView)]) {
[self.delegate clickView];
}
小箭头的旋转
//调用了tableView 的 reload方法,这个方法就会刷新headview,那样的话,就会重新生成一个headview
- (void)didMoveToSuperview{
_arrowBtn.imageView.transform = self.groupModel.isOpen ? CGAffineTransformMakeRotation(M_PI_2):CGAffineTransformMakeRotation(0);
}
布局子视图
- (void)layoutSubviews{
[super layoutSubviews];
_arrowBtn.frame = self.bounds;
_label.frame = CGRectMake(self.frame.size.width - 70, 0, 60, self.frame.size.height);
}
- layoutSubviews方法,是当改变父控件高度的时候,自动调用这个方法,所以一般在这里面设置子控件的frame。因为子控件随着父控件而改变。
- UITableViewHeaderFooterView不可以用xib,只能用代码创建
setter赋值button title
- (void)setGroupModel:(JKGroupModel *)groupModel{
_groupModel = groupModel;
[_arrowBtn setTitle:_groupModel.name forState:UIControlStateNormal];
_label.text = [NSString stringWithFormat:@"%@/%lu",_groupModel.online,(unsigned long)_groupModel.friends.count];
}
imageView.contentMode
button.imageView.contentMode = UIViewContentModeCenter;
button.imageView.clipsToBounds = NO;
这个居中是包括了,横向和纵向都是居中。图片不会拉伸或者压缩,就是按照imageView的frame和图片的大小来居中显示的。
这里有两种情况:
- 图片比view的区域更大。这个时候会截取图片的中间部位显示在frame区域里面。
- 图片比view的区域更小。这个时候图片会完整的显示在frame的中间位置。
如果在默认情况,图片的多出来的部分还是会显示屏幕上。如果不希望超过frame的区域显示在屏幕上要设置。clipsToBounds属性。
ListTableViewController
加载数据
- (NSArray *)dataArray{
//为什么不能用self.dataArray?? BadAccess
if (!_dataArray) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"friends.plist" ofType:nil];
NSArray *array = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *muArray = [NSMutableArray arrayWithCapacity:array.count];
for (NSDictionary *dict in array) {
JKGroupModel *groupModel = [JKGroupModel GroupWithDict:dict];
[muArray addObject:groupModel];
}
_dataArray = [muArray copy];
}
return _dataArray;
}
- 每个字典对应一个数据模型(Model class),plist中有多少个字典就有多少个Model class
- 使用该方法加载数据 — [self setValuesForKeysWithDictionary:dict]
- plist的第一层级都是数组,所以懒加载返回的也是数组
尾部去掉多余的线
#pragma mark - 去掉多余的线 ,将尾部设为一个透明的UIView
- (void)clipExtraCellLine:(UITableView *)tableView{
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor clearColor];
[self.tableView setTableFooterView:view];
}
Section Header
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
每次表视图变动需要reload
[self.tableView reloadData];
判断展开的Item数量
NSInteger count = groupModel.isOpen ? groupModel.friends.count : 0;
设置cell的代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"friendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}
//对应的哪一个section
JKGroupModel *groupModel = self.dataArray[indexPath.section];
//对应的哪一个row
JKFriendsModel *friendModel = groupModel.friends[indexPath.row];
cell.imageView.image = [UIImage imageNamed:friendModel.icon];
cell.textLabel.text = friendModel.name;
cell.detailTextLabel.text = friendModel.intro;
return cell;
}
AppDelegate
创建导航视图
ListTableViewController *listVC = [[ListTableViewController alloc] init];
UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:listVC];
self.window.rootViewController = navCtrl;