Swift3 QQ联系人列表
来源:互联网 发布:英雄联盟 kda 软件 编辑:程序博客网 时间:2024/05/21 10:04
最终效果:
第一步:搭建一个基础的界面
- 首先在main.storyboard中拖拽一个tableViewController,并且身份设置检查器中的Class设置为已经继承UITableViewController的ViewController
- 分析plist文件中的内容,并且根据内容创建模型创建一个Friend类、一个FriendGroup类,并且让它们都继承NSObject(可以利用KVC快速设置值)。在Friend类中有四个个属性 :icon、intro、name的三个String属性和一个vip的Int属性。利用 setValuesForKeys(_ keyedValues: [String : Any]) 快速设置属性在Friend类中赋值:
init(dict : [String : NSObject]) { super.init() setValuesForKeys(dict) } //forUnderfineKey重写这个方法是为了防止以后出现一个未定义key并为其赋值是出现错误 override func setValue(_ value: Any?, forUndefinedKey key: String) { }
在FriendGroup中,由于FriendGroup本身就是一个模型,而这个模型里面需要添加一个Friends即是一个Friend的数组,是模型里面嵌套模型,不能通过直接用setValuesForKeys的方法来直接为friends属性赋值,因为在在plist中friends是一个数组其类型是 [[String : NSObject]] 这样的数组,而在我们FriendGroup中的friends属性的类型是 [friend] 是一个friend类型的数组 , 若是直接用 setValuesForKeys 则是直接将 [[String : NSObject]] 数组赋值给 [friend] 数组,类型不匹配 ,故会出错。由于setValuesForKeys的原理是通过遍历键值 利用 funcsetValue(_value: Any?,forKeykey:String) 这个方法为每个key赋值,所以我们可以重写这个方法来保证通过KVC快速赋值不出错。override func setValue(_ value: Any?, forKey key: String) { //当遍历到的键为。friends 时 ,利用friend构造方法将其转化为 [friend] 数组 if key == "friends"{ let array = value as! [[String : NSObject]] var friendItems = [Friend]() for item in array{ let friend = Friend(dict: item) friendItems.append(friend) } self.friends = friendItems return } super.setValue(value, forKey: key) }
- 在ViewController中通过懒加载来读取文件
lazy var itemList : [FriendGroup] = { let plistPath = Bundle.main.path(forResource: "friends", ofType: "plist")! let arrays = NSArray(contentsOfFile: plistPath)! var dataList = [FriendGroup]() for array in arrays{ let item = FriendGroup(dict: array as! [String : NSObject]) dataList.append(item) } return dataList }()
根据最终效果图定义一个Cell类的模型(ViewController)
var friend : Friend?{ willSet{ self.imageView?.image = UIImage(named: (newValue?.icon ?? "")) self.detailTextLabel?.text = newValue?.intro self.textLabel?.text = newValue?.name if newValue?.vip == 1 { self.textLabel?.textColor = UIColor.red }else{ self.textLabel?.textColor = UIColor.black } } } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
实现数据源协议 与 代理协议(ViewController)
//MARK: UITableViewDataSource override func numberOfSections(in tableView: UITableView) -> Int { return itemList.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let group = self.itemList[section] return group.groupBtnIsOpened == true ? (group.friends?.count ?? 0) : 0 }//用 dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) 得到重用cell 并对每个Cell中内容进行赋值 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "FriendCell", for: indexPath) as! FriendCell let currentFriend = self.itemList[indexPath.section].friends?[indexPath.row] cell.friend = currentFriend return cell }
注意:对于纯代码书写的Cell在使用重用方法要去得到一个可重用的Cell的时候,需要在ViewDidLoad方法中在tableView中注册一个可重用标志符一样的Celloverride func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. tableView.register(FriendCell.self, forCellReuseIdentifier: "FriendCell")}
观察最终效果图不难看出具有悬停功能显示分组名称和在线人数的视图应该为表头:即 UItableViewHeaderFooterView ,但是效果图中表头具有点击功能,
class ContactHeaderView: UITableViewHeaderFooterView { var groupBtn : UIButton? var onlineUsers : UILabel? var friendGroup : FriendGroup?{ willSet{ groupBtn?.setTitle(newValue?.name, for: .normal) onlineUsers?.text = "\(newValue?.online ?? 0)/\(newValue?.friends?.count ?? 0)"//由于可重用表头视图的特殊性,必须判断每个视图的三角形形状 let imageView = self.groupBtn?.imageView if !(newValue?.groupBtnIsOpened)! { imageView?.transform = CGAffineTransform.identity }else{ imageView?.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2) } } } override init(reuseIdentifier: String?) {//表头初始化时并不会给自己设置frame,设置frame是在tableView中进行的 super.init(reuseIdentifier: reuseIdentifier) self.setUpUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } //MARK: -- layOutSubviews()方法的调用情况 /* 1.视图控件将要显示的时候会调用这个方法,并给这个视图控件的子控件设置frame 2.当视图控件的尺寸发生了变化的时候,会重新调用这个方法,来布局子视图的位置 3.当屏幕发生了选装的时候,也会调用这个方法,布局子控件 4.在这个方法中最好只是设置子控件的frame,而不进行有关值的设置 */ override func layoutSubviews() { super.layoutSubviews() //MARK: -- 此处无法设置btn的frame为self.frame 因为由于self.frame的orgin的x,y是以父视图即tableview为参照的,不为零! groupBtn?.frame = self.bounds onlineUsers?.frame = CGRect(x: self.bounds.width - 80, y: 0, width: 72, height: self.bounds.height) } func setUpUI(){ self.groupBtn = UIButton() self.onlineUsers = UILabel() groupBtn?.setImage(UIImage(named: "buddy_header_arrow"), for: .normal) groupBtn?.contentHorizontalAlignment = .left groupBtn?.setBackgroundImage(UIImage(named:"buddy_header_bg"), for: .normal) groupBtn?.setBackgroundImage(UIImage(named:"buddy_header_bg_highlighted"), for: .highlighted) self.groupBtn?.setTitleColor(UIColor.black, for: .normal) groupBtn?.contentEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 8) groupBtn?.titleEdgeInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 0) onlineUsers?.textAlignment = .right self.contentView.addSubview(groupBtn!) self.contentView.addSubview(onlineUsers!) self.groupBtn?.addTarget(self, action: #selector(self.groupBtnDidOnClick), for: .touchUpInside) //MARK: -- 设置图片的填充样式,避免图片在形变以后图片的大小比例被改变 , 并且设置超出部分不被父视图裁剪 self.groupBtn?.imageView?.contentMode = .center //居中显示会显示原有的尺寸 self.groupBtn?.imageView?.clipsToBounds = false } func groupBtnDidOnClick(){ //MARK: 三角形产生形变 let imageView = self.groupBtn?.imageView if (self.friendGroup?.groupBtnIsOpened)! { imageView?.transform = CGAffineTransform.identity //恢复形变 }else{ imageView?.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2) } self.friendGroup?.groupBtnIsOpened = !((self.friendGroup?.groupBtnIsOpened)!) }}p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #000000}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #000000}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000}span.s1 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures}span.s3 {font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s4 {font: 11.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures}span.Apple-tab-span {white-space:pre}//在这个方法中获得一个自定一的表头视图(ViewController)
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let contactHeaderView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "ContactHeaderView") as? ContactHeaderView
//MARK : --必须实现代理
contactHeaderView?.contactHeaderViewDelegate = self
contactHeaderView?.friendGroup = self.itemList[section]
// print(contactHeaderView?.groupBtn?.frame)
return
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px 'PingFang SC'; color: #000000}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font: 11.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s3 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures}//使用可重用的表头视图需要在ViewController中的ViewDidLoad()注册一个可重用的表头视图。 tableView.register(ContactHeaderView.self, forHeaderFooterViewReuseIdentifier: "ContactHeaderView")
7.由于点击事件是在 ContactHeaderView 中定义的 因此在点击事件之后要“回传” ViewController 让 ViewController 知道已经发生了点击事件并且让tableView重新实现数据源方法,此时可以使用两种方法来达成目的。
一、使用代理
代理设计模式本质是将本身一部分的功能让另外一个代理类去实现
设计一个表头类的代理 ContactHeaderViewDelegate
并且在ContactHeaderView类中定义一个属性
var contactHeaderViewDelegate :ContactHeaderViewDelegate?
并在按键点击事件groupBtnDidOnClick方法中添加self.contactHeaderViewDelegate?.headerViewDidOnClick(header:self)
在ViewController中遵守contactHeaderViewDelegate代理协议,并且设置表头视图的代理为VIewController
然后实现代理协议中的方法,在方法中使用tableView。reloadData()方法刷新表数据
二、使用通知
首先要在ViewController中定义个全局的通知名称ontactHeaderView
let reloadGroupNotificationName =NSNotification.Name(rawValue:"reloadGroupNotification")
然后在ViewController中监听一个通知。 //通知时现监听后注册的
NotificationCenter.default.addObserver(self, selector: #selector(self.reloadGroup(_:)), name:reloadGroupNotificationName, object:nil)
添加一个收到通知后执行的方法
func reloadGroup(_ notification :Notification){
let header = notification.objectas! ContactHeaderView
let indexSet =NSIndexSet(index: header.tag)
self.tableView.reloadSections(indexSetas IndexSet, with: .automatic)
}
在ContactHeaderView的groupBtnDidOnClick中发出通知
NotificationCenter.default.post(name:reloadGroupNotificationName, object:self)
- Swift3 QQ联系人列表
- 仿QQ联系人列表
- 类QQ,微信,联系人列表搜索UISearchBar使用
- 仿写 QQ 和 微信 联系人列表:
- ListView--QQ联系人样式
- QQ联系人1
- QQ联系人菜单2:
- Android开发 QQ联系人列表升级版——ListView和ScrollView高阶使用方法
- android 仿qq好友列表分组效果及联系人分组效果
- listview侧滑菜单的实现——高仿QQ联系人列表
- listview侧滑菜单的实现——高仿QQ联系人列表
- 类似于QQ联系人列表的二级listview的伸缩效果---ExpandableListView的使用指南
- 读取联系人列表
- 打开联系人列表
- Android获取联系人列表
- 原创android联系人列表
- Android 获取联系人列表
- Android 读写联系人列表
- ddos一个思路
- 压缩图片的方式个人总结
- isight调用matlab专业模块
- MySql获取时间范围中的随机日期
- Struts(6)Struts框架中使用filter过滤关键词
- Swift3 QQ联系人列表
- Handler机制
- 数据访问层
- Tablayout Viewpager的实现
- ubuntu下编译linux内核
- 事件分发机制机制详解
- HDOJ1003(最大连续子串)
- UVA
- Android xml编写tween animation(补间动画)