• iOS基础-高级视图-UITableView--实例:QQ好友列表


    事先已准备好plist文件,结构如下
    1.Root为Array。
    2.里面有十一个字典(每个字典为一个分组)
    3.每个分组里有friends,name,online三个属性
    4.friends属性里有若干字典(每个字典代表一个好友)
    5.每个好友字典里有4个属性,name,icon,intro和vip
    6.需要两个模型,分组模型和其里面的好友模型
    一、加载模型数据
    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模型。

    二、显示模型数据
    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;
    }

    三、添加头部控件(头部控件也有循环利用机制)
    1.设置代理,遵守代理协议,并实现代理方法

    //内部会设置位置和尺寸,不用自己设置
    -(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.

    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:方法

    //在这个初始化方法中,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个情况

    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:

    四、设置头部数据
    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;
    }

    五、展开/合并好友组
    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);
       }
    }
  • 相关阅读:
    python 字符串常用操作
    markdown 基础语法
    网络安全入门的16个基本问题
    Linux中20个crontab例子
    使用python爬取一个网页里表格的内容
    浅谈python的深浅拷贝
    Linux中设置普通用户可以su和sudo
    iptables四表五链
    CentOS7编译安装NFS
    源码安装csvn
  • 原文地址:https://www.cnblogs.com/marshall-yin/p/4758272.html
Copyright © 2020-2023  润新知