QQ好友列表通过plist读取,plist的结构为一组字典,每个字典内有本组的信息和另外一组字典代表好友。

要读取plist,选择合适的数据结构,例如NSArray,然后调用initWithContentsOfFile:方法初始化,文件通过mainBundle的pathForResource::方法获取,如下:

 NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"friends" ofType:@"plist"];
NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistPath];

很明显需要一个Group模型和一个Friend模型,注意到这里的Friend模型有一个BOOL属性来判断是否是vip,根据规范,要把BOOL的get方法重命名为 isXxx,如下:

注意KVC使用时字典和模型一一对应。

#import <Foundation/Foundation.h>

@interface Friend : NSObject

@property (nonatomic, copy) NSString *icon;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *intro;
@property (nonatomic, assign, getter = isVip) BOOL vip; //注意这一行的规范 @end

Tip:将字典的键值对转化为模型的方法为setValuesForKeysWithDictionary:

对于Group组的初始化,除去friends属性(装Friend模型),其余的通过KVC就可以注入,可以采用先全部注入,然后取出friends中的每一个字典生成模型,再存入临时的模型数组,最后交给friends属性的方法:

- (instancetype)initWithDict:(NSDictionary *)dict{

    if (self = [super init]) {

        //1.注入所有属性,这里的问题是friends里面装的是字典,应该特殊处理
[self setValuesForKeysWithDictionary:dict];
//2.特殊处理friends数组中的字典为模型
NSMutableArray *friendArray = [NSMutableArray array];
for (NSDictionary *dict in self.friends) {
Friend *friend = [[Friend alloc] initWithDict:dict];
[friendArray addObject:friend];
}
self.friends = friendArray; } return self; }

一个细节:模型组使用NSArray而不是NSMutableArray,可以保证数据的安全性(不会被随意修改)。

团队开发:参数越安全越好。

好友列表的实现原理是修改TableView的组的头部视图(Plain样式下),修改为下面的结构:

细节:头部控件也应该进行循环利用,利用一个缓存池。

Tip:TableView支持的头部视图为UITableViewHeaderFooterView,它有一个UIView *类型的contentView。

注意:xib暂时不能描述UITableViewHeaderFooterView。

排查错误:一个控件没有出现

1.先看看frame的设置,位置和尺寸

2.检查控件的hidden属性是否为YES

3.检查有没有加入父控件中

4.看透明度alpha是否<0.01

5.被其他空间挡住

6.父控件有没有前面5个问题

打印结构体的方法:NSStringFromXxx函数转化为NSString对象。

注意:任何UIView控件的init方法中,获取到的frame和bounds都是0。

#warning xxx的应用:编译器会在当前位置产生一个警告,用于提醒。

Tip:HeaderFooterView的宽度永远是它所在的TableView的宽度。

Tip:如果每一组的HeaderFooterView宽度相同,不必调用函数,直接使用 self.tableView.sectionHeaderHeight =44; 来设置。

同理self.tableView.rowHeight = 50;表示每一行cell的高度。

Tip:Xcode的插件路径:/Users/soulghost/Library/Application Support/Developer/Shared/Xcode/Plug-ins/

因为任何UIView的init方法中都获取不到frame和bounds,因此应该通过layoutSubviews方式获取到各个控件的frame,然后进行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); }

具体的实现和前面的聊天实现差不多,但是设置frame的时机变为了HeaderView的layoutSubviews方法,因为只有在这里才能得到HeaderView的尺寸,进而计算左侧的按钮和右侧的Label尺寸。

注意因为指向View的指针为弱指针,先用强指针指向一个新建的View,加入视图后再交付给弱指针,从而保证不会被释放。

下面分析整个过程:

为了实现朋友列表,需要上图那样的View作为HeaderView,使用tableView的方法(注意控制器必须是UITableView)

tableView: viewForHeaderInSection:方法来设定HeaderView的视图,为了封装性,定义一个类HeaderView来设置该视图:

@class FriendsGroup;

@interface HeaderView : UITableViewHeaderFooterView

@property (nonatomic, strong) FriendsGroup *group;

+ (instancetype)headerViewWithTableView:(UITableView *)tableView;

@end

因为HeaderView中的内容包括组名和组内的在线情况,因此应该维护一个group模型,因为HeaderView采用缓存池来进行性能优化,因此要传入tableView。

HeaderView的实现比较麻烦,initWithReuse...方法用于得到一个可重用的或者新的HeaderView,如果是新的,就创建新的Label和Button,注意一个细节,为了实现类似下图的效果:

按钮的图片与左侧有距离,按钮的图片与按钮的标题也有距离,这是通过设置内边距实现的,设置content的内边距(EdgeInset)可以使得按钮整体左移,使用imageEdgeInset或者titleEdgeInset都可以实现按钮图片和标题的距离,需要注意的是弱指针的赋值技巧和在这里获取控件的frame(init中)是nil。

第一个方法是用于生成一个特定ID的HeaderView,进行初步的初始化(注意上面的描述),然后返回,它是供第四个方法调用用的。

第二个方法就是所谓的HeaderView尺寸被系统改变时,frame可以获取了,在这里设置各个控件的尺寸。

第三个方法重写了group的set方法,用于设定控件的内容,调用时机是取出HeaderView并且传递模型时。

第四个方法是先从缓存池中取得HeaderView,失败则调用第一个方法获取一个HeaderView,然后返回,这个方法的主要目的是内存优化。

@interface HeaderView ()

@property (nonatomic, weak) UILabel *countView;
@property (nonatomic, weak) UIButton *nameView; @end @implementation HeaderView - (id)initWithReuseIdentifier:(NSString *)reuseIdentifier{ //初始化时,headerFooterView的frame和bounds暂时没有值。
//任何UIView控件的init方法中,获取到的frame和bounds都是0
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
//添加底部Button
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];
//左对齐+内边距实现靠左。设置图片和文字的内边距(设置Title的内边距)。
[nameView setTitle:@"我的好友" 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); [self.contentView addSubview:nameView];
self.nameView = nameView;
//添加好友数目Label
UILabel *countView = [[UILabel alloc] init];
countView.textAlignment = NSTextAlignmentRight;
countView.textColor = [UIColor grayColor];
[self.contentView addSubview:countView];
self.countView = countView; } return self; }
/**
* 布局子控件,当一个控件的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); } - (void)setGroup:(FriendsGroup *)group{ _group = group; //设置按钮文字
[self.nameView setTitle:group.name forState:UIControlStateNormal];
//设置在线数
self.countView.text = [NSString stringWithFormat:@"%d/%d",group.online,group.friends.count];
} + (instancetype)headerViewWithTableView:(UITableView *)tableView{ static NSString *ID = @"group"; HeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID]; if (headerView == nil) {
headerView = [[HeaderView alloc] initWithReuseIdentifier:ID]; }
return headerView;
} @end

通过下面的方法调用,就可以成功得到多个headerView供系统显示:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
//1.创建头部空间
HeaderView *header = [HeaderView headerViewWithTableView:tableView];
//2.给header设置数据(传递模型)
header.group = self.groups[section]; return header;
}

对于显示好友信息的Cell,和上一节的方法基本一致,但是比较简单,直接使用系统的SubTitle样式即可。

Cell维护一个Friend模型,并且在为Cell传递模型时(控制器里进行)初始化数据,因此要重写friendData的set方法(它是Friend成员变量,为了和C++的友元区分,固命此名)。

对于cell的初始化,有固定的写法,init方法用于从父类创建一个特定ID的Cell,一个用于内存优化,Cell的代码如下:

@implementation FriendCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
return self;
} + (instancetype)cellWithTableView:(UITableView *)tableView{ static NSString *ID = @"friend";
FriendCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[FriendCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
return cell; } -(void)setFriendData:(Friend *)friendData{ _friendData = friendData; self.imageView.image = [UIImage imageNamed:friendData.icon];
self.textLabel.text = friendData.name;
self.detailTextLabel.text = friendData.intro; }

这样,只要在取得Cell的函数里,通过cellWithTableView获取cell,然后传入模型的同时会调用set方法设定数据,最后返回的Cell便是最终的Cell。

(二十七)QQ好友列表的实现的更多相关文章

  1. iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)

    iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一) 一.项目结构和plist文件 二.实现代码 1.说明: 主控制器直接继承UITableViewController // ...

  2. [iOS基础控件 - 6.9.3] QQ好友列表Demo TableView

    A.需求 1.使用plist数据,展示类似QQ好友列表的分组.组内成员显示缩进功能 2.组名使用Header,展示箭头图标.组名.组内人数和上线人数 3.点击组名,伸展.缩回好友组   code so ...

  3. 仿QQ好友列表界面的实现

    TableView有2种style:UITableViewStylePlain 和 UITableViewStyleGrouped. 但是QQ好友列表的tableView给人的感觉似乎是2个style ...

  4. ExpandableListView仿QQ好友列表

    本例中,对ExpandableListView中的数据进行了封装,分为两个JavaBean,一个为Group类表示组信息,一个Child类表示该组下子列表信息: Group: public class ...

  5. android 实现QQ好友列表

    在某些Android开发群里,看到有些新手问怎么实现QQ好友列表,其实网上一搜挺多的.接触Android,也才一年的时间,大部分时间花在工作上(解bug...),界面上开发很少参与.自己维护的系统应用 ...

  6. 基于Qt的相似QQ好友列表抽屉效果的实现

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/shuideyidi/article/details/30619167     前段时间在忙毕业设计, ...

  7. swift 实现QQ好友列表功能

    最近项目中有类似QQ好友列表功能,整理了一下,话不多说,直接上代码 import UIKit class QQFriend: NSObject { var name: String? var intr ...

  8. Windows UIA自动化测试框架学习--获取qq好友列表

    前段时间应公司要求开发一款针对现有WPF程序的自动化测试工具,在网上查资料找了一段时间,发现用来做自动化测试的框架还是比较多的,比如python的两个模块pywinauto和uiautomation, ...

  9. iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(二)

    一.实现效果             二.实现代码 1.数据模型部分 YYQQGroupModel.h文件 // // YYQQGroupModel.h // 02-QQ好友列表(基本数据的加载) / ...

随机推荐

  1. TensorFlow实验环境搭建

    初衷: 由于系统.平台的原因,网上有各种版本的tensorflow安装教程,基于linux的.mac的.windows的,各有不同,tensorflow的官网也给出了具体的安装命令.但实际上,即使te ...

  2. 2016移动端Android新技术综合预览--好文不多,这一篇就足够

    Csdn /Tamic 原文地址: http://blog.csdn.net/sk719887916/article/details/53525067 本文章6月份已完成(http://www.jia ...

  3. iOS开源加密相册Agony的实现(六)

    简介 虽然目前市面上有一些不错的加密相册App,但不是内置广告,就是对上传的张数有所限制.本文介绍了一个加密相册的制作过程,该加密相册将包括多密码(输入不同的密码即可访问不同的空间,可掩人耳目).Wi ...

  4. Java web文件上传下载

    [版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...

  5. MacOS和iOS开发中异步调用与多线程的区别

    很多童鞋可能对Apple开发中的异步调用和多线程的区别不是太清楚,这里本猫将用一些简单的示例来展示一下它们到底直观上有神马不同. 首先异步调用可以在同一个线程中,也可以在多个不同的线程中.每个线程都有 ...

  6. MPAndroidChart——饼图

    MPAndroidChart--饼图 MPAndroidChart是安卓下的一个开源图形库,很多效果,简单看几个效果图 Github地址:https://github.com/PhilJay/MPAn ...

  7. [Vim]vim学习笔记--多个文件打开,切换,关闭

    一种情况是在shell中用vim打开多个文件,另一种是在vim编辑器中打开多个文件 同时打开多个文件 vim file1 file2  打开文件并水平窗口显示 vim -o file1 file2 打 ...

  8. J2EE进阶(十四)超详细的Java后台开发面试题之Spring IOC与AOP

    J2EE进阶(十四)超详细的Java后台开发面试题之Spring IOC与AOP 前言   搜狐畅游笔试题中有一道问答题涉及到回答谈谈对Spring IOC与AOP的理解.特将相关内容进行整理.    ...

  9. UNIX网络编程——揭开网络编程常见API的面纱【下】

    Linux网络编程数据收发的API流程分析        只要把数据在协议栈中的流动线路和脉络弄清楚了,关于协议栈的实现部分,理解起来就轻松多了.在网络编程章节的数据接收过程中,我们主要介绍过read ...

  10. C语言实现简单黑客帝国代码流

    #include <stdio.h> #include <stdlib.h> #include <time.h> #include <windows.h> ...