iOS基礎-高級視圖-UITableView--實例:QQ好友列表

事先已準備好plist文件,結構以下
1.Root爲Array。
2.裏面有十一個字典(每一個字典爲一個分組)
3.每一個分組裏有friends,name,online三個屬性
4.friends屬性裏有若干字典(每一個字典表明一個好友)
5.每一個好友字典裏有4個屬性,name,icon,intro和vip
6.須要兩個模型,分組模型和其裏面的好友模型
1、加載模型數據
1.新建一個好友模型MJFriend,添加4個屬性,提供加載模型數據的方法並實現數組

@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *icon;
@property(nonatomic,copy)NSString *intro;
@property(nonatomic,assign,getter = isVip)B0OL vip;
+(instancetype)friendWithDict:(NSDictionary *)dict
{
return [self alloc] initWithDict:dict];
}

-(instancetype)initWithDict:(NSDictionary *)dict
{
if(self = [super init]){
//好友字典裏全部的鍵值對都賦值給friend的4個屬性了
[self setValueForKeysWithDictionary:dict]; //KVC
}
return self;
} 

2.新建一個好友分組模型MJFriendGroup,添加3個屬性,提供加載模型數據的方法並實現 緩存

@property(nonatomic,copy)NSString *name;
@property(nonatomic,strong)NSArray *friends;//裝的都是MJFriend模型
@property(nonatomic,assign)int online;

+(instancetype)groupWithDict:(NSDictionary *)dict
{
return [self alloc] initWithDict:dict];
}
-(instancetype)initWithDict:(NSDictionary *)dict
{
if (self == [super init]){
//1.注入全部屬性(可是目前friends屬性裏裝的是字典)
[self setValueForKeysWithDictionary:dict]; //KVC

//2.特殊處理friends屬性(將friends字典轉成MJFriend模型)
NSMutableArray *friendArray = [NSMutableArray array];
for (NSDictionary *dict in self.friends){
MJFriend *friend = [MJFriend friendWithDict:dict];
[friendArray addObject:friend];
}
self.friends = friendArray;
}
return self;
}

3.控制器拿到groups數組屬性並懶加載(在其中將分組字典轉爲模型)安全

@property(nonatomic,strong)NSArray *groups; //安全起見用Array類型
-(NSArray *)groups
{
if(_groups == nil) {
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[NSBundle 
mainBundle] pathForResource:@"" ofType:nil];

NSMutableArray *groupArray = [NSMutableArray array];
for(NSDictionary *dict in dictArray){
MJFriendGroup *group = [MJFriendGroup groupWithDict:dict];
[groupArray addObject:group];
}
_groups = groupArray;
}
return _groups;
}

PS:groupArray數組裏面裝的就是group模型。group模型裏面的friendsArray數組裝
的是friend模型。ide

2、顯示模型數據
0.將默認控制器MJViewController改成繼承自UITableViewController。將storyboard裏的默認View刪除,拖一個TableViewController,Class設置爲MJViewController ,若是不設置Class,那麼就不會建立MJViewController,MJViewController裏的代碼,至關於白寫。
1.設置數據源,遵照數據源協議,並實現數據源方法佈局

#pragma mark - 數據源方法
第一個數據源方法:numberOfSectionsInTableView:方法
return self.groups.count;
第二個數據源方法:numberOfRowsInSection:方法
MJFriendGroup *group = self.groups[section];
return group.friends.count;
第三個數據源方法:numberOfRowsInSection:方法
{
//1.建立cell
MJFriendCell *cell = [MJFriend cellWithTableView:tableView];

//2.設置cell的數據
MJFriendGroup *group = self.groups[indexPath.section];
MJFriend *friend = group.friends[indexPath.row];
cell.friendData = friend;

return cell;
}

3、添加頭部控件(頭部控件也有循環利用機制)
1.設置代理,遵照代理協議,並實現代理方法atom

//內部會設置位置和尺寸,不用本身設置
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection 
:(NSInteger)section
{
//1.建立頭部控件 
MJHeaderView *header = [MJHeaderView headerViewWithTableView: 
tableView];
header.delegate = self;
//2.給header設置數據(給header傳遞模型)[重寫完setGroup方法後進行]
header.group = self.groups[section];

return header;
}

2.spa

1>自定義一個MJHeaderView,並提供一個類方法快速建立一個HeaderView代理

+(instancetype)headerViewWithTableView:(UITableView *)tableView
{
static NSString *ID = @"header";
MJHeaderView *header = [tableView 
dequeueReusableHeaderFooterViewWithIdentifier:ID];
if (header == nil){
header = [MJHeaderView alloc] initWithRreuseIdentifier:ID];
}
return header;
}

 

2>重寫initWithReuseIdentifier:方法code

//在這個初始化方法中,MJHeaderView的frame/bounds沒有值
{
if(self = [super initWithReuseIdentifier:reuseIdentifier]){
//添加子控件
//1.添加按鈕
UIButton *nameView = [UIButton buttonWithType:UIButtonTypeCustom];
//設置按鈕的背景圖片
[nameView setBackgroundImage:[UIImage imageNamed: 
@"buddy_header_bg"] forState:UIControlStateNormal];
[nameView setBackgroundImage:[UIImage imageNamed: 
@"buddy_header_bg_highlighted"] forState:UIControlStateHighlighted];
//設置按鈕內部左邊的箭頭圖片
[nameView setImage:[UIImage imageNamed:@"buddy_header_arrow"] 
forState:UIControlStateNormal];
[nameView setTitleColor[UIColor blackColor] forState:UIControlStateNormal];
//設置按鈕的內容左對齊
nameView.contentHorizontalAlignment = 
UIControlContentHorizontalAlignmentLeft;
//設置按鈕的內邊距
nameView.contentEdgeInsets = UIEdgeInsetsMake(0,10,0,0);
//設置標題的內邊距
nameView.titleEdgeInsets = UIEdgeInsetsMake(0,10,0,0);
[nameView addTarget:self acton:@selector(nameViewClick) 
forControlEvents:UIControlEventTouchUpInside];

//設置按鈕內部的imageView的內容模式爲居中(不要拉伸)
nameView.imageView.contentMode = UIViewContentModeCenter;
//超出邊框的內容不須要裁剪
nameView.imageView.clipsToBounds = NO;
[self.contentView addSubview:nameView];
self.nameView = nameView; //聲明成員屬性後必定要賦值
//2.添加好友數
UILabel *countView = [[UILabel alloc] init];
countView.textAligment = NSTextAligmentRight;
countView.textColor = [UIColor grayColor];
[self.contentView addSubview:countView];
self.countView = countView; //聲明成員屬性後必定要賦值
}
return self;
}

經驗:某個控件出不來
1.frame的尺寸和位置對不對(不要在初始化方法中設置frame)
2.hidden是否爲YES
3.有沒有添加到父控件中
4.alpha是否小於0.01
5.被其餘控件擋住了
6.查看父控件的前面5個狀況orm

PS:由於layoutSubviews方法裏要用到nameView和countView,因此將它們添加爲屬性

@property(nonatomic,weak) UILabel *countView;
@property(nonatomic,weak) UIButton *nameView;
//通常在這裏面佈局內部的子控件(設置子控件的frame)
//當一個控件的frame發生改變的時候就會調用
-(void)layoutSubviews
{
#warning 必定要調用父類的方法
[super layoutSubviews];

//1.設置按鈕的frame
self.nameView.frame = self.bounds;

//2.設置好友數的frame
CGFloat countY = 0;
CGFloat countH = self.frame.size.height;
CGFloat countW = 150;
CGFloat countX = self.frame.size.width - 10 - countW;
self.countView.frame = CGRectMake(countX,countY,countW,countH);
}

//若是頭部控件每一組高度同樣,那麼就用下面的方法設置高度
self.tableView.sectionHeaderHeight = 44;
//若是頭部控件每一組高度不同,那麼就用下面的方法設置高度
//heightForHeaderInSection:

4、設置頭部數據
1.在HeaderView中添加一個group模型屬性
@property(nonatomic,strong)MJFriendGroup *group;
2.重寫setter方法

-(void)setGroup:(MJFriendGroup *)group
{
_group = group;

// 1.設置按鈕文字(組名)
[self.nameView setTitle:group.name forState:UIControlStateNormal];

//2.設置好友數(在線數/總數)
self.countView.text = [NSString stringWithFormat:@"%d/% 
d",group.online,group.frinedns.count];
}

3.將建立cell和設置cell數據的代碼封裝起來
1>新建一個Cell
2>提供一個類方法,傳TableView返回一個cell,一個Cell對應一個Friend模型

+(instancetype)cellWithTableView:(UITableView *)tableView;
//friend是C++的關鍵字,不能用friend做爲屬性名
@property(nonatomic,strong)MJFriend *friendData;

3>重寫setter方法(在這裏給cell控件賦值)

-(void)setFriendData:(MJFriend *)friendData
{
_friendData = friendData;

self.imageView.image = [UIImage imageNamed:friendData];
self.textLabel.text = friendData.name;
self.textLabel.textColor = friendData.isVip ? [UIColor redColor] : [UIColor 
blackColor];
self.detailTextLabel.text = friendData.intro;
}

4>實現cellWithTableView:方法

{
static NSString *ID = @"friend";
MJFirendCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil){
cell = [MJFirendCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
reuseIdentifier:ID];
}
return cell;
}

5、展開/合併好友組
1.用addTarget:方法監聽按鈕點擊
2.來到MJFriendGroup.h,添加一個BOOL屬性(標識這組是否須要展開)
@property(nonatomic,assign,getter = isOpened) BOOL opened;
3.重寫第二個數據源方法 numberOfRowsInSection:

{
   MJFriendGroup *group = self.groups[section];
   return (group.isOpened ? group.friends.count : 0);
}

4.實現按鈕的方法

- (void)nameViewClick
{
//1.修改模型的標識(狀態取反)
self.group.opened = !self.group.isOpened;
//2.刷新表格
if([self.delegate respondsToSelector:@selector 
(headerViewDidClickNameView:)]) {
[self.delegate headerViewDidClickNameView:self];
}

5.來到MJHeaderView.h新建代理協議,添加代理屬性(由於HeaderView想把它內部的點擊告訴控制器) ,實現代理方法

@protocol MJHeaderViewDelegate(NSObject)
@optional
-(void)headerViewDidClickNameView:(MJHeaderView *)headerView;
@property(nonatomic,weak)id<MJHeaderViewDelegate> delegate
#pragma mark - headerVIew的代理方法
//點擊了headerView上的分組名按鈕時就會調用
-(void)headerViewDidClickNameView:(MJHeaderView *)headerView
{
[self.tableView reloadData];
}

說明:由於只有控制器纔有reloadData方法,因此headerVIew不能直接使用,只能委託控制器這個"代理"去刷新表格。

6.刷新表格是不會從緩存池裏取的,會從新建立。因此拿到從新建立好的新View來修改

//當一個控件被添加到父控件中就會調用(系統自動調用)
-(void)didMoveToSuperview
{
//控制箭頭的狀態
if [self.group.opened] {
self.nameView.imageView.transform = CGAffineTransformMakeRotation 
(M_PI_2);
} else {
self.nameView.imageView.transform = CGAffineTransformMakeRotation(0);
   }
}
相關文章
相關標籤/搜索